wip checkpoint

This commit is contained in:
Rizky 2023-12-17 08:45:34 +07:00
parent 3dc13caab8
commit 21fa871b92
29 changed files with 200 additions and 537 deletions

View File

@ -0,0 +1,62 @@
import { w } from "../../../utils/types/general";
import { fetchViaProxy } from "../proxy";
export const loadApiProxyDef = async (url: string, with_types: boolean) => {
const raw = await fetchViaProxy(urlPath(url, "/_prasi/_"));
let ver = "";
if (raw && (raw as any).prasi) {
ver = (raw as any).prasi;
}
const base = baseUrl(url);
if (ver === "v2") {
await new Promise<void>((done) => {
const d = document;
const script = d.createElement("script");
script.onload = async () => {
done();
};
if (with_types) {
script.src = `${base}/_prasi/load.js?url=${url}&dev=1`;
} else {
script.src = `${base}/_prasi/load.js?url=${url}`;
}
d.body.appendChild(script);
});
} else {
const apiEntry = await fetch(base + "/_prasi/api-entry");
w.prasiApi[url] = {
apiEntry: (await apiEntry.json()).srv,
};
if (with_types) {
const apiTypes = await fetch(base + "/_prasi/api-types");
w.prasiApi[url].apiTypes = await apiTypes.text();
w.prasiApi[url].prismaTypes = {
"prisma.d.ts": await loadText(`${base}/_prasi/prisma/index.d.ts`),
"runtime/index.d.ts": await loadText(
`${base}/_prasi/prisma/runtime/index.d.ts`
),
"runtime/library.d.ts": await loadText(
`${base}/_prasi/prisma/runtime/library.d.ts`
),
};
}
}
};
const baseUrl = (url: string) => {
const base = new URL(url);
return `${base.protocol}//${base.host}`;
};
const urlPath = (url: string, pathname: string) => {
const base = new URL(url);
base.pathname = pathname;
return base.toString();
};
const loadText = async (url: string, v2?: boolean) => {
const res = await fetch(url);
return await res.text();
};

View File

@ -1,40 +1,49 @@
import { fetchSendApi } from "./client-frame";
import { w } from "../../../utils/types/general";
import { fetchViaProxy } from "../proxy";
import { loadApiProxyDef } from "./api-proxy-def";
export const apiClient = (
api: Record<string, { url: string; args: any[] }>,
apiUrl: string
) => {
export type ApiProxy<T extends Record<string, any> = {}> = any;
export const apiProxy = (api_url: string) => {
return new Proxy(
{},
{
get: (_, actionName: string) => {
const createFn = (actionName: string) => {
return function (this: { apiUrl: string } | undefined, ...rest: any) {
return function (
this: { api_url: string } | undefined,
...rest: any
) {
return new Promise<any>(async (resolve, reject) => {
try {
let _apiURL = apiUrl;
if (typeof this?.apiUrl === "string") {
_apiURL = this.apiUrl;
let base_url = api_url;
if (typeof this?.api_url === "string") {
base_url = this.api_url;
}
if (!api) {
reject(
new Error(`API Definition for ${_apiURL} is not loaded.`)
);
return;
if (!w.prasiApi) {
w.prasiApi = {};
}
if (api && !api[actionName]) {
reject(
`API ${actionName.toString()} not found, existing API: \n - ${Object.keys(
api || {}
).join("\n - ")}`
);
return;
if (!w.prasiApi[base_url]) {
await loadApiProxyDef(base_url, false);
}
let actionUrl = api[actionName].url;
const actionParams = api[actionName].args;
const api_def = w.prasiApi[base_url];
if (api_def) {
if (!api_def.apiEntry) api_def.apiEntry = {};
if (api_def.apiEntry && !api_def.apiEntry[actionName]) {
reject(
`API ${actionName.toString()} not found, existing API: \n - ${Object.keys(
api_def || {}
).join("\n - ")}`
);
return;
}
}
let actionUrl = api_def.apiEntry[actionName].url;
const actionParams = api_def.apiEntry[actionName].args;
if (actionUrl && actionParams) {
if (rest.length > 0 && actionParams.length > 0) {
for (const [idx, p] of Object.entries(rest)) {
@ -53,7 +62,7 @@ export const apiClient = (
}
}
const url = `${_apiURL}${actionUrl}`;
const url = `${base_url}${actionUrl}`;
const result = await fetchSendApi(url, rest);
resolve(result);
@ -82,3 +91,9 @@ export const apiClient = (
}
);
};
const fetchSendApi = async (url: string, params: any) => {
return await fetchViaProxy(url, params, {
"content-type": "application/json",
});
};

View File

@ -1,8 +1,7 @@
import { waitUntil } from "web-utils";
import { createFrameCors } from "./client-frame";
import hash_sum from "hash-sum";
import { fetchViaProxy } from "../proxy";
export const dbClient = (name: string, dburl?: string) => {
export const dbProxy = (dburl: string) => {
return new Proxy(
{},
{
@ -10,7 +9,6 @@ export const dbClient = (name: string, dburl?: string) => {
if (table === "_tables") {
return () => {
return fetchSendDb(
name,
{
name,
action: "definition",
@ -24,7 +22,6 @@ export const dbClient = (name: string, dburl?: string) => {
if (table === "_definition") {
return (table: string) => {
return fetchSendDb(
name,
{
name,
action: "definition",
@ -38,7 +35,6 @@ export const dbClient = (name: string, dburl?: string) => {
if (table.startsWith("$")) {
return (...params: any[]) => {
return fetchSendDb(
name,
{
name,
action: "query",
@ -60,7 +56,6 @@ export const dbClient = (name: string, dburl?: string) => {
action = "query";
}
return fetchSendDb(
name,
{
name,
action,
@ -83,37 +78,13 @@ const cachedQueryResult: Record<
{ timestamp: number; result: any; promise: Promise<any> }
> = {};
export const fetchSendDb = async (
name: string,
params: any,
dburl?: string
) => {
const w = typeof window === "object" ? window : (globalThis as any);
let url = `/_dbs/${name}`;
let frm: Awaited<ReturnType<typeof createFrameCors>>;
export const fetchSendDb = async (params: any, dburl: string) => {
const base = new URL(dburl);
base.pathname = `/_dbs/`;
if (params.table) {
url += `/${params.table}`;
}
const _base = dburl || w.serverurl;
if (!w.frmapi) {
w.frmapi = {};
}
if (!w.frmapi[_base]) {
w.frmapi[_base] = await createFrameCors(_base);
}
frm = w.frmapi[_base];
if (!frm) {
await waitUntil(() => {
frm = w.frmapi[_base];
return frm;
});
base.pathname += `/${params.table}`;
}
const url = base.toString();
const hsum = hash_sum(params);
const cached = cachedQueryResult[hsum];
@ -121,7 +92,9 @@ export const fetchSendDb = async (
if (!cached || (cached && Date.now() - cached.timestamp > 1000)) {
cachedQueryResult[hsum] = {
timestamp: Date.now(),
promise: frm.send(url, params, w.apiHeaders),
promise: fetchViaProxy(url, params, {
"content-type": "application/json",
}),
result: null,
};

View File

@ -47,11 +47,16 @@ export const fetchViaProxy = async (
const cur = new URL(location.href);
const base = new URL(url);
if (cur.host === base.host) {
const res = await fetch(base.pathname, {
method: "POST",
body,
headers,
});
const res = await fetch(
base.pathname,
data
? {
method: "POST",
body,
headers,
}
: undefined
);
const raw = await res.text();
try {
return JSON.parse(raw);
@ -59,17 +64,15 @@ export const fetchViaProxy = async (
return raw;
}
} else {
console.log(url);
return null;
// const res = await fetch(`/_proxy`, {
// method: "POST",
// body: JSON.stringify({
// url,
// body,
// headers,
// }),
// headers: { "content-type": "application/json" },
// });
// return res.json();
const res = await fetch(`/_proxy`, {
method: "POST",
body: JSON.stringify({
url,
body,
headers,
}),
headers: { "content-type": "application/json" },
});
return res.json();
}
};

View File

@ -1,12 +1,12 @@
import { Root as ReactRoot, createRoot } from "react-dom/client";
import { defineReact, defineWindow } from "web-utils";
import { apiProxy } from "./base/load/api/api-proxy";
import { dbProxy } from "./base/load/db/db-proxy";
import { Root } from "./base/root";
import "./index.css";
import { registerMobile } from "./render/live/logic/mobile";
import { reloadDBAPI } from "./utils/script/init-api";
import { w } from "./utils/types/general";
import { sworkerAddCache, sworkerRegister } from "./sworker-boot";
import { dbClient } from "./base/load/db/client-db";
import { w } from "./utils/types/general";
const start = async () => {
const base = `${location.protocol}//${location.host}`;
@ -14,7 +14,12 @@ const start = async () => {
root: null as null | ReactRoot,
};
w.mobile = registerMobile();
w.db = dbClient("prasi", location.origin);
const cur = new URL(location.href);
const base_url = `${cur.protocol}//${cur.host}`;
w.db = dbProxy(base_url);
w.api = apiProxy(base_url);
w.serverurl = base;
sworkerRegister(react);

View File

@ -1,17 +1,18 @@
import init from "wasm-gzip";
import { jscript } from "../../../utils/script/jscript";
import { dbClient } from "../../../base/load/db/client-db";
import { dbProxy } from "../../../base/load/db/db-proxy";
import { PG } from "./ed-global";
import { fetchViaProxy } from "../../vi/load/proxy";
import { fetchViaProxy } from "../../../base/load/proxy";
import { ApiProxy, apiProxy } from "../../../base/load/api/api-proxy";
let w = window as unknown as { db: ReturnType<typeof dbClient> };
let w = window as unknown as {
db: ReturnType<typeof dbProxy>;
api: ApiProxy;
};
export const edInit = async (p: PG) => {
p.status = "ready";
const cur = new URL(location.href);
w.db = dbClient("prasi", `${cur.protocol}//${cur.host}`);
await init();
jscript.init(p.render, { esbuild: false });

View File

@ -1,6 +1,7 @@
import { IContent } from "../../../../utils/types/general";
import { VG } from "../../../vi/render/global";
import { IMeta, PG, active } from "../../logic/ed-global";
import { treeRebuild } from "../../logic/tree/build";
type MPIVParam = Parameters<Exclude<VG["visit"], undefined>>;
export const mainPerItemVisit = (
@ -59,6 +60,12 @@ export const mainPerItemVisit = (
parts.props.onPointerDown = (e) => {
e.stopPropagation();
if (active.comp_id && !p.comp.list[active.comp_id]) {
active.comp_id = "";
treeRebuild(p);
return;
}
const item = getOuterItem(
{
meta: active.comp_id ? p.comp.list[active.comp_id].meta : p.page.meta,

View File

@ -119,7 +119,7 @@ export const EdPopUser = ({
</div>
))}
</div>
{user.all.length > 0 && onAdd && (
{Array.isArray(user.all) && user.all.length > 0 && onAdd && (
<Select
options={user.all
.filter((e) => {

View File

@ -1,7 +1,8 @@
import Downshift from "downshift";
import { FC, useEffect } from "react";
import { useGlobal, useLocal } from "web-utils";
import { createAPI, createDB } from "../../../../../utils/script/init-api";
import { apiProxy } from "../../../../../base/load/api/api-proxy";
import { dbProxy } from "../../../../../base/load/db/db-proxy";
import { FMCompDef, FNCompDef } from "../../../../../utils/types/meta-fn";
import { EDGlobal } from "../../../logic/ed-global";
import { EdPropLabel } from "./prop-label";
@ -27,8 +28,8 @@ export const EdPropInstanceOptions: FC<{
if (!local.loaded) {
try {
if (p.site.config.api_url) {
if (!p.script.db) p.script.db = createDB(p.site.config.api_url);
if (!p.script.api) p.script.api = createAPI(p.site.config.api_url);
if (!p.script.db) p.script.db = dbProxy(p.site.config.api_url);
if (!p.script.api) p.script.api = apiProxy(p.site.config.api_url);
}
const args = {

View File

@ -1,17 +1,12 @@
import get from "lodash.get";
import {
createAPI,
createDB,
initApi,
reloadDBAPI,
} from "../../../utils/script/init-api";
import { apiProxy } from "../../../base/load/api/api-proxy";
import { dbProxy } from "../../../base/load/db/db-proxy";
import { jscript } from "../../../utils/script/jscript";
import { devLoader } from "../../live/dev-loader";
import { LSite } from "../../live/logic/global";
import { validateLayout } from "../../live/logic/layout";
import importModule from "../tools/dynamic-import";
import { EditorGlobal, PG } from "./global";
import { devLoader } from "../../live/dev-loader";
import { jscript } from "../../../utils/script/jscript";
import { deepClone } from "web-utils";
export const w = window as unknown as {
basepath: string;
@ -121,7 +116,7 @@ export const initEditor = async (p: PG, site_id: string) => {
prodUrl: localStorage.getItem(`prasi-ext-prod-url-${p.site.id}`) || "",
};
p.site.api_url = await initApi(site.config);
p.site.api_url = site.config.api_url;
if (w.externalAPI.prodUrl !== p.site.api_url) {
w.externalAPI.prodUrl = p.site.api_url;
@ -129,7 +124,6 @@ export const initEditor = async (p: PG, site_id: string) => {
}
if (w.externalAPI.mode === "dev" && w.externalAPI.devUrl) {
p.site.api_url = w.externalAPI.devUrl;
await reloadDBAPI(w.externalAPI.devUrl);
}
w.apiurl = p.site.api_url;
@ -173,8 +167,8 @@ export const execSiteJS = (p: PG) => {
};
const fn = p.site.js_compiled;
scope["api"] = createAPI(p.site.api_url);
scope["db"] = createDB(p.site.api_url);
scope["api"] = apiProxy(p.site.api_url);
scope["db"] = dbProxy(p.site.api_url);
const f = new Function(...Object.keys(scope), fn);
try {
const res = f(...Object.values(scope));

View File

@ -1,5 +1,6 @@
import { FC } from "react";
import { createAPI, createDB } from "../../../utils/script/init-api";
import { apiProxy } from "../../../base/load/api/api-proxy";
import { dbProxy } from "../../../base/load/db/db-proxy";
import { FNCompDef } from "../../../utils/types/meta-fn";
import { EItem } from "../elements/e-item";
import { ItemMeta, PG } from "./global";
@ -15,8 +16,8 @@ export const treePropEval = (
) => {
if (meta.item.type === "item" && meta.item.component) {
if (p.site.api_url) {
if (!p.script.db) p.script.db = createDB(p.site.api_url);
if (!p.script.api) p.script.api = createAPI(p.site.api_url);
if (!p.script.db) p.script.db = dbProxy(p.site.api_url);
if (!p.script.api) p.script.api = apiProxy(p.site.api_url);
}
const props = meta.item.component.props;

View File

@ -1,6 +1,7 @@
import { FC, ReactNode, Suspense, useEffect } from "react";
import { deepClone } from "web-utils";
import { createAPI, createDB } from "../../../utils/script/init-api";
import { apiProxy } from "../../../base/load/api/api-proxy";
import { dbProxy } from "../../../base/load/db/db-proxy";
import { ErrorBox } from "../elements/e-error";
import { ItemMeta, PG } from "./global";
@ -30,8 +31,8 @@ export const treeScopeEval = (
// prepare args
if (p.site.api_url) {
if (!p.script.db) p.script.db = createDB(p.site.api_url);
if (!p.script.api) p.script.api = createAPI(p.site.api_url);
if (!p.script.db) p.script.db = dbProxy(p.site.api_url);
if (!p.script.api) p.script.api = apiProxy(p.site.api_url);
}
const w = window as any;

View File

@ -2,7 +2,6 @@ import { useGlobal } from "web-utils";
import { EditorGlobal } from "../../../../logic/global";
import { ExternalDeploy } from "./ExternalDeploy";
import { w } from "../../../../logic/init";
import { reloadDBAPI } from "../../../../../../utils/script/init-api";
export const ExternalAPI = ({
status,
@ -88,11 +87,9 @@ export const ExternalAPI = ({
if (w.externalAPI.mode === "dev") {
p.site.api_url = w.externalAPI.devUrl;
checkApi();
await reloadDBAPI(w.externalAPI.devUrl, "dev");
} else {
p.site.api_url = w.externalAPI.prodUrl;
checkApi();
await reloadDBAPI(w.externalAPI.prodUrl, "dev");
}
}}
>

View File

@ -3,9 +3,9 @@ import { formatDistance } from "date-fns/esm";
import trim from "lodash.trim";
import { useEffect } from "react";
import { deepClone, useGlobal, useLocal } from "web-utils";
import { createAPI } from "../../../../../../utils/script/init-api";
import { AutoHeightTextarea } from "../../../../../../utils/ui/auto-textarea";
import { EditorGlobal } from "../../../../logic/global";
import { apiProxy } from "../../../../../../base/load/api/api-proxy";
const server = {
status: "ready" as
@ -40,7 +40,7 @@ export const ExternalDeploy = () => {
try {
const url = p.site.api_url;
local.api = createAPI(url);
local.api = apiProxy(url);
let res = await local.api._deploy({
type: "check",
id_site: p.site.id,

View File

@ -5,7 +5,6 @@ import trim from "lodash.trim";
import { EditorGlobal } from "../../../../logic/global";
import { Loading } from "../../../../../../utils/ui/loading";
import { w } from "../../../../../../utils/types/general";
import { reloadDBAPI } from "../../../../../../utils/script/init-api";
export const InternalAPI: FC<{
close: () => void;
@ -178,10 +177,6 @@ export const InternalAPI: FC<{
config.api_url = `https://${p.site.api_prasi.port}.prasi.world`;
const base = trim(config.api_url, "/");
try {
await reloadDBAPI(base);
} catch (e) {}
}
} catch (e) {
console.log(e);
@ -198,16 +193,7 @@ export const InternalAPI: FC<{
<div
className={cx("cursor-pointer hover:underline")}
onClick={async () => {
local.clearingCache = true;
local.render();
try {
await reloadDBAPI(p.site.api_url, "dev");
} catch (e) {}
local.clearingCache = false;
local.render();
alert("API Cache Cleared");
}}
onClick={async () => {}}
>
{local.clearingCache ? "Clearing Cache..." : "Clear API Cache"}
</div>

View File

@ -1,7 +1,7 @@
import { createRouter } from "radix3";
import { validate } from "uuid";
import { type apiClient } from "web-utils";
import { createAPI, createDB, initApi } from "../../../utils/script/init-api";
import { apiProxy } from "../../../base/load/api/api-proxy";
import { dbProxy } from "../../../base/load/db/db-proxy";
import importModule from "../../editor/tools/dynamic-import";
import { LSite, PG } from "./global";
import { validateLayout } from "./layout";
@ -17,7 +17,6 @@ export const w = window as unknown as {
isDesktop: boolean;
exports: any;
params: any;
apiClient: typeof apiClient;
apiurl: string;
preload: (path: string) => void;
mobile?: ReturnType<typeof registerMobile>;
@ -94,7 +93,7 @@ export const initLive = async (p: PG, domain_or_siteid: string) => {
await validateLayout(p);
p.site.api_url = await initApi(site.config, "prod");
p.site.api_url = site.config.api_url || "";
w.apiurl = p.site.api_url;
@ -104,8 +103,8 @@ export const initLive = async (p: PG, domain_or_siteid: string) => {
const exec = (fn: string, scopes: any) => {
if (p) {
if (p.site.api_url) {
scopes["api"] = createAPI(p.site.api_url);
scopes["db"] = createDB(p.site.api_url);
scopes["api"] = apiProxy(p.site.api_url);
scopes["db"] = dbProxy(p.site.api_url);
}
if (!w.params) {
w.params = {};

View File

@ -1,4 +1,4 @@
import { w } from "../../../utils/script/init-api";
import { w } from "../../../utils/types/general";
import { PG } from "./global";
type NOTIF_ARG = {

View File

@ -1,5 +1,6 @@
import { FC } from "react";
import { createAPI, createDB } from "../../../utils/script/init-api";
import { apiProxy } from "../../../base/load/api/api-proxy";
import { dbProxy } from "../../../base/load/db/db-proxy";
import { FNCompDef } from "../../../utils/types/meta-fn";
import { LItem } from "../elements/l-item";
import { PG } from "./global";
@ -19,8 +20,8 @@ export const treePropEval = (
if (meta.item.type === "item" && meta.item.component) {
if (p.site.api_url) {
if (!p.script.db) p.script.db = createDB(p.site.api_url);
if (!p.script.api) p.script.api = createAPI(p.site.api_url);
if (!p.script.db) p.script.db = dbProxy(p.site.api_url);
if (!p.script.api) p.script.api = apiProxy(p.site.api_url);
}
const props = meta.item.component.props;

View File

@ -1,10 +1,11 @@
import hash_sum from "hash-sum";
import { FC, ReactNode, Suspense, useEffect, useState } from "react";
import { deepClone } from "web-utils";
import { createAPI, createDB } from "../../../utils/script/init-api";
import { apiProxy } from "../../../base/load/api/api-proxy";
import { dbProxy } from "../../../base/load/db/db-proxy";
import { ErrorBox } from "../../editor/elements/e-error";
import { ItemMeta, PG } from "./global";
import { extractNavigate, preload } from "./route";
import hash_sum from "hash-sum";
export const JS_DEBUG = false;
@ -43,8 +44,8 @@ export const treeScopeEval = (
// prepare args
if (p.site.api_url) {
if (!p.script.db) p.script.db = createDB(p.site.api_url);
if (!p.script.api) p.script.api = createAPI(p.site.api_url);
if (!p.script.db) p.script.db = dbProxy(p.site.api_url);
if (!p.script.api) p.script.api = apiProxy(p.site.api_url);
}
const w = window as any;

View File

@ -1,4 +1,4 @@
import { w } from "../../utils/script/init-api";
import { w } from "../../utils/types/general";
import { Loader } from "../live/logic/global";
const cache = {
@ -55,7 +55,7 @@ export const mobileLoader: Loader = {
};
const load = async (url: string) => {
const res = await fetch(`${w.mobilepath}${url}`);
const res = await fetch(`${(w as any).mobilepath}${url}`);
try {
const text = await res.text();
const json = JSON.parse(text);

View File

@ -1,4 +1,4 @@
import { w } from "../../utils/script/init-api";
import { w } from "../../utils/types/general";
import { Loader } from "../live/logic/global";
const base = `/_web/${(window as any).id_site}`;

View File

@ -1,154 +0,0 @@
import { createStore, get, set } from "idb-keyval";
import trim from "lodash.trim";
import { apiClient, dbClient, waitUntil } from "web-utils";
import { createFrameCors } from "web-utils";
export const w = window as unknown as {
prasiApi: Record<string, any>;
apiHeaders: any;
apiClient: typeof apiClient;
dbClient: typeof dbClient;
serverurl: string;
apiurl: string;
mobilepath: string;
};
export const createAPI = (url: string) => {
if (!w.apiClient) {
w.apiClient = apiClient;
}
if (!w.prasiApi) {
w.prasiApi = {};
}
if (!url) {
return null;
}
return w.apiClient(w.prasiApi[url]?.apiEntry, url);
};
export const createDB = (url: string) => {
if (!w.dbClient) {
w.dbClient = dbClient;
}
const dbc: typeof dbClient = w.dbClient;
return dbc("db", url);
};
export const initApi = async (config: any, mode: "dev" | "prod" = "dev") => {
let url = "";
if (config.prasi) {
if (
!(
location.hostname === "prasi.app" ||
location.hostname === "api.prasi.app"
) // android localhost
) {
if (
location.hostname === "localhost" ||
location.hostname === "127.0.0.1" ||
location.hostname === "10.0.2.2"
) {
url = `https://${config.prasi.port}.prasi.world`;
} else {
url = `https://${location.hostname}:${config.prasi.port}`;
}
} else {
url = `https://${config.prasi.port}.prasi.world`;
}
} else if (config.api_url) {
url = config.api_url;
}
if (!w.prasiApi) {
w.prasiApi = {};
}
if (url) {
if (!w.prasiApi[url]) {
try {
await reloadDBAPI(url, mode);
} catch (e) {}
}
}
return url;
};
const loadText = async (url: string, v2?: boolean) => {
const res = await fetch(url);
return await res.text();
};
export const reloadDBAPI = async (
url: string,
mode: "dev" | "prod" = "dev"
) => {
const base = trim(url, "/");
if (!w.prasiApi) {
w.prasiApi = {};
}
const cache = createStore(`prasi-api`, "config");
const forceReload = async () => {
if (!w.prasiApi[url]) {
w.prasiApi[url] = {};
}
const frm = await createFrameCors(base);
const raw = await frm.sendRaw(`/_prasi/_`);
let ver = "";
if (raw && (raw as any).prasi) {
ver = (raw as any).prasi;
}
if (ver === "v2") {
await new Promise<void>((done) => {
const d = document;
const script = d.createElement("script");
script.onload = async () => {
done();
};
script.src = `${base}/_prasi/load.js?url=${url}${
mode === "dev" ? "&dev=1" : ""
}`;
d.body.appendChild(script);
});
} else {
const apiTypes = await fetch(base + "/_prasi/api-types");
const apiEntry = await fetch(base + "/_prasi/api-entry");
w.prasiApi[url] = {
apiEntry: (await apiEntry.json()).srv,
prismaTypes: {
"prisma.d.ts": await loadText(`${base}/_prasi/prisma/index.d.ts`),
"runtime/index.d.ts": await loadText(
`${base}/_prasi/prisma/runtime/index.d.ts`
),
"runtime/library.d.ts": await loadText(
`${base}/_prasi/prisma/runtime/library.d.ts`
),
},
apiTypes: await apiTypes.text(),
};
}
await set(url, JSON.stringify(w.prasiApi[url]), cache);
};
const prasiBase = `${location.protocol}//${location.host}`;
try {
const found = await get(url, cache);
if (found) {
w.prasiApi[url] = JSON.parse(found);
await forceReload();
} else {
await forceReload();
}
} catch (e) {
console.warn("Failed to load API");
if (url === prasiBase) {
console.error("Failed to load prasi. Reloading...");
setTimeout(() => location.reload(), 3000);
}
}
};

View File

@ -12,12 +12,12 @@ export type PageProps = {
export type PrasiAPI = {
apiEntry: any;
prismaTypes: {
prismaTypes?: {
"prisma.d.ts": string;
"runtime/library.d.ts": string;
"runtime/index.d.ts": string;
};
apiTypes: string;
apiTypes?: string;
};
export const w = window as unknown as {
@ -32,6 +32,7 @@ export const w = window as unknown as {
editorGlbDefault: string;
ts: number;
serverurl: string;
apiurl: string;
api: any;
db: any;
offline: boolean;

View File

@ -1,21 +0,0 @@
import { apiContext } from "../server/api/api-ctx";
import { g } from "../utils/global";
export const _ = {
url: "/_api_frm",
async api() {
const { req, res } = apiContext(this);
let allowUrl = req.headers.get("origin") || req.headers.get("referer");
res.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT");
res.setHeader("Access-Control-Allow-Headers", "content-type");
res.setHeader("Access-Control-Allow-Credentials", "true");
if (allowUrl) {
res.setHeader("Access-Control-Allow-Origin", allowUrl);
}
res.setHeader("content-type", "text/html");
res.setHeader("etag", g.frm.etag);
res.send(`<script>${g.frm.js}</script>`);
},
};

View File

@ -2,8 +2,8 @@ import { apiContext } from "../server/api/api-ctx";
import { DBArg, execQuery } from "../utils/query";
export const _ = {
url: "/_dbs/:dbName/:action",
async api(dbName: any, action?: string) {
url: "/_dbs/:action",
async api(action?: string) {
const { req, res } = apiContext(this);
try {

View File

@ -1,7 +1,6 @@
import { createRouter } from "radix3";
import { g } from "utils/global";
import { createResponse } from "./api/api-ctx";
import { prepareApiRoutes } from "./api/api-scan";
export const serveAPI = {
init: async () => {

View File

@ -43,7 +43,6 @@ export const serveStatic = {
if (filename) {
const path = join("static", filename);
const file = Bun.file(dir.path(`app/${path}`));
console.log(_, filename);
if (await file.exists()) {
cache.static[`/${filename}`] = {
type: mime.getType(path) || "application/octet-stream",

View File

@ -1,205 +0,0 @@
import { waitUntil } from "web-utils";
import { createId } from "@paralleldrive/cuid2";
const cuid = createId;
(BigInt.prototype as any).toJSON = function (): string {
return `BigInt::` + this.toString();
};
export const createFrameCors = async (url: string, win?: any) => {
let w = window;
if (!!win) {
w = win;
}
const document = w.document;
const id = `__` + url.replace(/\W/g, "");
if (typeof document !== "undefined" && !document.querySelector(`#${id}`)) {
const iframe = document.createElement("iframe");
iframe.style.display = "none";
iframe.id = id;
const _url = new URL(url);
_url.pathname = "/_api_frm";
iframe.src = _url.toString();
await new Promise<void>((resolve, reject) => {
iframe.onload = () => {
if (!iframe.contentDocument) {
setTimeout(() => {
if (!iframe.contentDocument) {
reject(
`Cannot load iframe ${_url.toString()}. content document not found.`
);
}
}, 100);
}
};
const onInit = (e: any) => {
if (e.data === "initialized") {
iframe.setAttribute("loaded", "y");
w.removeEventListener("message", onInit);
resolve();
}
};
w.addEventListener("message", onInit);
document.body.appendChild(iframe);
});
}
const wm = {} as Record<string, any>;
const sendRaw = async (
input: RequestInfo | URL,
init?: RequestInit | undefined
) => {
if (w.document && w.document.querySelector) {
const iframe = w.document.querySelector(`#${id}`) as HTMLIFrameElement;
if (
!iframe ||
!iframe.contentWindow ||
(iframe && iframe.getAttribute("loaded") !== "y")
) {
await waitUntil(
() =>
iframe &&
iframe.contentWindow &&
iframe.getAttribute("loaded") === "y"
);
}
return await new Promise((resolve, reject) => {
if (iframe && iframe.contentWindow) {
const id = cuid();
wm[id] = (e: any) => {
if (id === e.data.id) {
w.removeEventListener("message", wm[id]);
delete wm[id];
if (e.data.error) {
let err = e.data.error;
if (typeof err === "string") {
reject(
err.replace(
/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g,
""
)
);
}
} else {
resolve(e.data.result);
}
}
};
w.addEventListener("message", wm[id]);
let _input = input;
if (typeof input === "string") {
if (!input.startsWith("http")) {
_input = `${url}${input}`;
}
}
iframe.contentWindow.postMessage({ input: _input, init, id }, "*");
}
});
}
};
return {
sendRaw,
async send(input: string | RequestInfo | URL, data?: any, _headers?: any) {
const uri = input.toString();
const headers = { ..._headers };
let body = data;
let isFile = false;
const formatSingle = async (data: any) => {
if (!(data instanceof w.FormData || data instanceof w.File)) {
headers["content-type"] = "application/json";
} else {
if (data instanceof w.File) {
isFile = true;
let ab = await new Promise<ArrayBuffer | undefined>((resolve) => {
const reader = new FileReader();
reader.addEventListener("load", (e) => {
resolve(e.target?.result as ArrayBuffer);
});
reader.readAsArrayBuffer(data);
});
if (ab) {
data = new File([ab], data.name);
}
}
}
return data;
};
if (Array.isArray(data)) {
body = await Promise.all(data.map((e) => formatSingle(e)));
} else {
body = await formatSingle(data);
}
if (!isFile) {
body = JSON.stringify(body);
}
return await sendRaw(
`${url.endsWith("/") ? url : `${url}/`}${
uri.startsWith("/") ? uri.substring(1) : uri
}`,
data
? {
method: "post",
headers,
body,
}
: {}
);
},
};
};
export const fetchSendApi = async (
rawUrl: string,
params: any,
parentWindow?: any
) => {
let w: any = typeof window === "object" ? window : globalThis;
const win = parentWindow || w;
const url = new URL(rawUrl);
let frm: Awaited<ReturnType<typeof createFrameCors>>;
const base = `${url.protocol}//${url.host}`;
if (!win.frmapi) {
win.frmapi = {};
}
if (!win.frmapi[base]) {
win.frmapi[base] = await createFrameCors(base, win);
}
frm = win.frmapi[base];
if (!win.apiHeaders) {
win.apiHeaders = {};
}
if (!frm) {
await waitUntil(() => {
frm = win.frmapi[base];
return frm;
});
}
if (url.pathname.startsWith("//")) {
url.pathname = url.pathname.substring(1);
}
return await frm.send(url.pathname, params, win.apiHeaders);
};

View File

@ -7,7 +7,4 @@ export * from "./page";
export * from "./global";
export * from "./define-react";
export * from "./define-window";
export * from './client-api';
export * from './client-frame';
export * from './client-db';
export const React = _React;