checkpoint

This commit is contained in:
Rizky 2024-05-07 00:27:29 +07:00
parent 0c96971a8f
commit 2af0e9932e
27 changed files with 426 additions and 193 deletions

View File

@ -132,15 +132,6 @@ model site {
page_folder page_folder[] page_folder page_folder[]
org org? @relation(fields: [id_org], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "site_id_organization_fkey") org org? @relation(fields: [id_org], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "site_id_organization_fkey")
user user @relation(fields: [id_user], references: [id], onDelete: NoAction, onUpdate: NoAction) user user @relation(fields: [id_user], references: [id], onDelete: NoAction, onUpdate: NoAction)
site_query site_query[]
}
model site_query {
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
id_site String? @db.Uuid
name String?
query String?
site site? @relation(fields: [id_site], references: [id], onDelete: NoAction, onUpdate: NoAction, map: "site")
} }
model site_use_comp { model site_use_comp {

View File

@ -1,15 +1,54 @@
import { apiContext } from "../../../pkgs/core/server/api/api-ctx"; import { apiContext } from "../../../pkgs/core/server/api/api-ctx";
const g = global as unknown as {
_font_cache: Record<string, { body: any; headers: any }>;
};
if (!g._font_cache) {
g._font_cache = {};
}
export const _ = { export const _ = {
url: "/_font/**", url: "/_font/**",
async api() { async api() {
const { req } = apiContext(this); const { req } = apiContext(this);
const pathname = req.url.split("/_font").pop(); const pathname = req.url.split("/_font").pop() || "";
const f = await fetch(`https://api.fonts.coollabs.io${pathname}`); const cache = g._font_cache[pathname];
const body = await f.arrayBuffer(); if (cache) {
const res = new Response(body); if (req.headers.get("accept-encoding")?.includes("gzip")) {
return new Response(Bun.gzipSync(cache.body), {
headers: {
"content-type": cache.headers["content-type"],
"content-encoding": "gzip",
},
});
} else {
return new Response(cache.body, {
headers: {
"content-type": cache.headers["content-type"],
},
});
}
}
let f: Response = null as any;
if (pathname?.startsWith("/s/")) {
f = await fetch(`https://fonts.gstatic.com${pathname}`);
} else {
f = await fetch(`https://fonts.googleapis.com${pathname}`);
}
if (f) {
const body = await f.text();
g._font_cache[pathname] = { body, headers: {} };
f.headers.forEach((v, k) => {
g._font_cache[pathname].headers[k] = v;
});
res.headers.set("content-type", f.headers.get("content-type") || ""); const res = new Response(
return res; body.replaceAll("https://fonts.gstatic.com", "/_font")
);
res.headers.set("content-type", f.headers.get("content-type") || "");
return res;
}
}, },
}; };

View File

@ -1,10 +1,15 @@
import { parseFile } from "@swc/core";
import { dir } from "dir"; import { dir } from "dir";
import { apiContext } from "service-srv"; import { apiContext } from "service-srv";
import { validate } from "uuid"; import { validate } from "uuid";
import { prodIndex } from "../util/prod-index"; import { prodIndex } from "../util/prod-index";
import { gzipAsync } from "../ws/sync/entity/zlib";
import { code } from "../ws/sync/code/code"; import { code } from "../ws/sync/code/code";
import { initFrontEnd } from "../ws/sync/code/parts/init/frontend"; import { initFrontEnd } from "../ws/sync/code/parts/init/frontend";
import { gzipAsync } from "../ws/sync/entity/zlib";
import { visit } from "woodpile";
import { parseTypeDef } from "../util/parse-type-def";
import { Glob, build } from "bun";
import { removeAsync } from "fs-jetpack";
export const _ = { export const _ = {
url: "/prod/:site_id/**", url: "/prod/:site_id/**",
@ -25,16 +30,85 @@ export const _ = {
const action = pathname.split("/")[1]; const action = pathname.split("/")[1];
switch (action) { switch (action) {
case "code": { case "type_def": {
const arr = pathname.split("/").slice(2); const path = dir.data(`/code/${site_id}/site/typings.d.ts`);
const codepath = arr.join("/"); const file = Bun.file(path);
const build_path = code.path(site_id, "site", "build", codepath); if (await file.exists()) {
const glob = new Glob("type_def*");
for await (const item of glob.scan(
dir.data(`/code/${site_id}/site`)
)) {
const stamp = parseInt(item.split(".")[1]);
if (file.lastModified !== stamp) {
await removeAsync(dir.data(`/code/${site_id}/site/${item}`));
} else {
return new Response(
Bun.gzipSync(
await Bun.file(
dir.data(`/code/${site_id}/site/${item}`)
).arrayBuffer()
),
{
headers: {
"content-type": "application/json",
"content-encoding": "gzip",
},
}
);
}
}
const res = JSON.stringify(await parseTypeDef(path));
await Bun.write(
dir.data(
`/code/${site_id}/site/type_def.${file.lastModified}.json`
),
res
);
return new Response(Bun.gzipSync(res), {
headers: {
"content-type": "application/json",
"content-encoding": "gzip",
},
});
}
return new Response("{}", {
headers: { "content-type": "application/json" },
});
}
case "typings.d.ts": {
const build_path = dir.data(`/code/${site_id}/site/typings.d.ts`);
const file = Bun.file(build_path); const file = Bun.file(build_path);
if (!(await file.exists())) { if (!(await file.exists())) {
const root = `/code/${site_id}/site/src`; const root = `/code/${site_id}/site/src`;
await initFrontEnd(root, site_id); await initFrontEnd(root, site_id);
return new Response("", { status: 403 }); }
const body = Bun.gzipSync(await file.arrayBuffer());
return new Response(body, {
headers: { "content-type": file.type, "content-encoding": "gzip" },
});
}
case "code": {
const arr = pathname.split("/").slice(2);
const codepath = arr.join("/");
const build_path = code.path(site_id, "site", "build", codepath);
let file = Bun.file(build_path);
if (!(await file.exists())) {
const root = `/code/${site_id}/site/src`;
await initFrontEnd(root, site_id, true);
await new Promise<void>((resolve) => {
const ival = setInterval(async () => {
file = Bun.file(build_path);
if (await file.exists()) {
clearInterval(ival);
resolve();
}
}, 100);
});
} }
const body = Bun.gzipSync(await file.arrayBuffer()); const body = Bun.gzipSync(await file.arrayBuffer());

View File

@ -2,6 +2,7 @@ import { apiContext } from "service-srv";
import { code } from "../ws/sync/code/code"; import { code } from "../ws/sync/code/code";
import { initFrontEnd } from "../ws/sync/code/parts/init/frontend"; import { initFrontEnd } from "../ws/sync/code/parts/init/frontend";
import { initServer } from "../ws/sync/code/parts/init/server"; import { initServer } from "../ws/sync/code/parts/init/server";
import { initTypings } from "../ws/sync/code/parts/init/typings";
export const _ = { export const _ = {
url: "/rebuild/:id_site", url: "/rebuild/:id_site",
@ -13,6 +14,7 @@ export const _ = {
delete server[id_site]; delete server[id_site];
await initFrontEnd(root, id_site, true); await initFrontEnd(root, id_site, true);
await initServer(root, id_site, true); await initServer(root, id_site, true);
await initTypings(root, id_site, true);
return "ok"; return "ok";
}, },

File diff suppressed because one or more lines are too long

View File

@ -34,10 +34,13 @@ glb.server_hook = async (arg) => {
if (arr.length >= 3 && validate(site_id)) { if (arr.length >= 3 && validate(site_id)) {
try { try {
if (!g.server_runtime[site_id]) { if (!g.server_runtime[site_id]) {
await g.createServerRuntime(site_id); if (g.server_runtime[site_id] !== null) {
await g.createServerRuntime(site_id);
}
} }
const res = await server.http(site_id, arg); const res = await server.http(site_id, arg);
if (res instanceof Response) { if (res instanceof Response) {
return res; return res;
} else { } else {

View File

@ -0,0 +1,101 @@
import { parseFile, TsModuleDeclaration } from "@swc/core";
import { visit } from "woodpile";
type SingleExport = {
type: "all" | "named" | "default";
kind: "const" | "type";
val: string;
};
export const parseTypeDef = async (path: string) => {
const ast = await parseFile(path, { syntax: "typescript" });
const exports = {} as Record<string, SingleExport[]>;
visit(ast, {
visitWithPath: {
visitDecl(node, path) {
const t = node as TsModuleDeclaration;
if (t.type === "TsModuleDeclaration") {
if (t.body) {
exports[t.id.value] = [];
if (Array.isArray(t.body.body)) {
for (const body of t.body.body) {
if (body.type === "ExportAllDeclaration") {
exports[t.id.value].push({
type: "all",
kind: "const",
val: body.source.value,
});
} else if (body.type === "ExportDeclaration") {
if (body.declaration.type === "FunctionDeclaration") {
exports[t.id.value].push({
type: "named",
kind: "const",
val: body.declaration.identifier.value,
});
} else if (body.declaration.type === "VariableDeclaration") {
for (const dec of body.declaration.declarations) {
if (dec.type === "VariableDeclarator") {
const id = dec.id as unknown as Map<string, any>;
if (id.get("type") === "Identifier") {
exports[t.id.value].push({
type: "named",
kind: "const",
val: id.get("value"),
});
}
}
}
} else if (
body.declaration.type === "TsTypeAliasDeclaration"
) {
if (body.declaration.id.type === "Identifier") {
exports[t.id.value].push({
type: "named",
kind: "type",
val: body.declaration.id.value,
});
}
}
} else if (body.type === "ExportNamedDeclaration") {
for (const s of body.specifiers) {
if (s.type === "ExportSpecifier") {
if (s.exported) {
exports[t.id.value].push({
type: "named",
kind: "const",
val: s.exported.value,
});
} else if (s.orig) {
exports[t.id.value].push({
type: "named",
kind: "const",
val: s.orig.value,
});
}
}
}
}
}
}
}
}
},
},
});
const result = {} as Record<string, "const" | "type">;
const traverse = (items: SingleExport[]) => {
for (const item of items) {
if (item.type === "all") {
const found = exports[item.val];
traverse(found);
} else {
result[item.val] = item.kind;
}
}
};
traverse(exports.index);
return result;
};

View File

@ -4,6 +4,7 @@ import { initServer } from "./parts/init/server";
import { codeInternal } from "./parts/internal"; import { codeInternal } from "./parts/internal";
import { ensureFiles } from "./utlis/ensure-files"; import { ensureFiles } from "./utlis/ensure-files";
import { ensureLib } from "./utlis/ensure-lib"; import { ensureLib } from "./utlis/ensure-lib";
import { initTypings } from "./parts/init/typings";
export const code = { export const code = {
internal: codeInternal, internal: codeInternal,
@ -15,6 +16,7 @@ export const code = {
await initFrontEnd(root, id_site); await initFrontEnd(root, id_site);
await initServer(root, id_site); await initServer(root, id_site);
await initTypings(root, id_site);
}, },
path( path(
id_site: string, id_site: string,

View File

@ -6,7 +6,7 @@ import { cleanPlugin } from "esbuild-clean-plugin";
import isEqual from "lodash.isequal"; import isEqual from "lodash.isequal";
import { appendFile } from "node:fs/promises"; import { appendFile } from "node:fs/promises";
import { code } from "../../code"; import { code } from "../../code";
import { buildTypes } from "./typings";
const decoder = new TextDecoder(); const decoder = new TextDecoder();
export const initFrontEnd = async ( export const initFrontEnd = async (
root: string, root: string,
@ -74,7 +74,6 @@ export const initFrontEnd = async (
await installDeps(root, res, id_site); await installDeps(root, res, id_site);
} else { } else {
await codeError(id_site, ""); await codeError(id_site, "");
await buildTypes(root, id_site);
} }
}); });
} catch (e) { } catch (e) {

View File

@ -1,5 +1,32 @@
import { dir } from "dir";
import { code } from "../../code"; import { code } from "../../code";
export const buildTypes = async (root: string, id_site: string) => { export const initTypings = async (
// console.log(root); root: string,
id_site: string,
force?: boolean
) => {
let existing = code.internal.typings[id_site];
if (existing) {
if (force) {
existing.kill();
await existing.exited;
} else {
return;
}
}
try {
code.internal.typings[id_site] = Bun.spawn({
cmd: [
...`tsc --watch --moduleResolution node --emitDeclarationOnly --outFile ../typings.d.ts --declaration --noEmit false`.split(
" "
),
],
cwd: dir.data(`/code/${id_site}/site/src`),
stdio: ["ignore", "ignore", "ignore"],
});
} catch (e) {
console.log(e);
}
}; };

View File

@ -14,12 +14,12 @@ if (!g.server_main_handler) {
} }
const serverMain = () => ({ const serverMain = () => ({
handler: g.server_main_handler as Record<string, PrasiServer>, handler: g.server_main_handler as Record<string, null | PrasiServer>,
init_timeout: null as any, init_timeout: null as any,
ws(action: keyof WebSocketHandler<WSData>, ...arg: any[]) { ws(action: keyof WebSocketHandler<WSData>, ...arg: any[]) {
const id = arg[0].data.site_id; const id = arg[0].data.site_id;
if (this.handler[id]) { if (this.handler[id]) {
const handler = this.handler[id].ws; const handler = this.handler[id]?.ws;
if (handler) { if (handler) {
const fn = handler[action] as any; const fn = handler[action] as any;
@ -30,49 +30,47 @@ const serverMain = () => ({
} }
} }
}, },
init(site_id: string) { async init(site_id: string) {
clearTimeout(this.init_timeout); this.handler[site_id] = null;
this.init_timeout = setTimeout(async () => { const server_src_path = code.path(site_id, "server", "build", "index.js");
const server_src_path = code.path(site_id, "server", "build", "index.js"); const file = Bun.file(server_src_path);
const file = Bun.file(server_src_path); if (!this.handler[site_id] && (await file.exists()) && file.length) {
if (!this.handler[site_id] && (await file.exists()) && file.length) { try {
try { delete require.cache[server_src_path];
delete require.cache[server_src_path]; const svr = require(server_src_path);
const svr = require(server_src_path); if (svr && typeof svr.server === "object") {
if (svr && typeof svr.server === "object") { this.handler[site_id] = svr.server;
this.handler[site_id] = svr.server; svr.server.site_id = site_id;
this.handler[site_id].site_id = site_id; if (typeof svr.server.init === "function") {
if (typeof svr.server.init === "function") { svr.server.init({});
svr.server.init({});
}
Bun.write(
Bun.file(code.path(site_id, "site", "src", "server.log")),
""
);
} else {
const file = await Bun.file(server_src_path).text();
const log_path = code.path(site_id, "site", "src", "server.log");
if (file.length === 0) {
await Bun.write(Bun.file(log_path), "server.ts is empty");
} else {
await Bun.write(
Bun.file(log_path),
"server.ts does not return server object"
);
}
} }
} catch (e: any) { Bun.write(
Bun.file(code.path(site_id, "site", "src", "server.log")),
""
);
} else {
const file = await Bun.file(server_src_path).text(); const file = await Bun.file(server_src_path).text();
const log_path = code.path(site_id, "site", "src", "server.log"); const log_path = code.path(site_id, "site", "src", "server.log");
if (file.length === 0) { if (file.length === 0) {
await Bun.write(Bun.file(log_path), "server.ts is empty"); await Bun.write(Bun.file(log_path), "server.ts is empty");
} else { } else {
await Bun.write(Bun.file(log_path), e.message); await Bun.write(
console.log(`Failed to init server ${site_id}\n`, log_path); Bun.file(log_path),
"server.ts does not return server object"
);
} }
} }
} catch (e: any) {
const file = await Bun.file(server_src_path).text();
const log_path = code.path(site_id, "site", "src", "server.log");
if (file.length === 0) {
await Bun.write(Bun.file(log_path), "server.ts is empty");
} else {
await Bun.write(Bun.file(log_path), e.message);
console.log(`Failed to init server ${site_id}\n`, log_path);
}
} }
}, 10); }
}, },
async http( async http(
site_id: string, site_id: string,
@ -81,14 +79,15 @@ const serverMain = () => ({
if (arg.url.pathname.endsWith("main.js")) { if (arg.url.pathname.endsWith("main.js")) {
code.init(site_id, "init http"); code.init(site_id, "init http");
} }
if (typeof this.handler[site_id] === "undefined") { if (typeof this.handler[site_id] === "undefined") {
if ( if (
await existsAsync(code.path(site_id, "server", "build", "index.js")) await existsAsync(code.path(site_id, "server", "build", "index.js"))
) { ) {
this.init(site_id); await this.init(site_id);
await waitUntil(200);
} }
} }
const handler = this.handler[site_id]; const handler = this.handler[site_id];
if (handler) { if (handler) {

View File

@ -20,9 +20,11 @@ if (!g.createServerRuntime) {
db: dbProxy((site.config as any).api_url), db: dbProxy((site.config as any).api_url),
api: null as any, api: null as any,
}; };
} catch (e) { } } catch (e) {
g.server_runtime[site_id] = null;
}
} else { } else {
g.server_runtime[site_id] = { db: null as any, api: null } g.server_runtime[site_id] = { db: null as any, api: null };
} }
} }
}; };

View File

@ -7,7 +7,12 @@
content="width=device-width, initial-scale=1.0, user-scalable=1.0, minimum-scale=1.0, maximum-scale=1.0"> content="width=device-width, initial-scale=1.0, user-scalable=1.0, minimum-scale=1.0, maximum-scale=1.0">
<title>Prasi: App Builder</title> <title>Prasi: App Builder</title>
<link rel="stylesheet" href="/index.css"> <link rel="stylesheet" href="/index.css">
<script>window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = { <style>
@import url('/_font/css2?family=Source+Sans+3:wght@400;600&display=swap');
@import url('/_font/css2?family=JetBrains+Mono&display=swap');
</style>
<script>
window.__REACT_DEVTOOLS_GLOBAL_HOOK__ = {
isDisabled: true, isDisabled: true,
_renderers: {} _renderers: {}
};</script> };</script>

View File

@ -1,50 +1,46 @@
@import url('https://prasi.avolut.com/_font/css2?family=Source+Sans+3:wght@400;600&display=swap'); @tailwind base;
@tailwind components;
@tailwind utilities;
@import url('https://prasi.avolut.com/_font/css2?family=JetBrains+Mono&display=swap'); html,
body,
#root {
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
display: flex;
flex-direction: column;
flex: 1;
-webkit-tap-highlight-color: transparent;
overscroll-behavior-y: none;
}
@tailwind base; body {
@tailwind components; font-family: "Source Sans 3", system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
@tailwind utilities; }
html, .JsxText {
body, color: #5c6773;
#root { }
width: 100%;
height: 100%;
padding: 0px;
margin: 0px;
display: flex;
flex-direction: column;
flex: 1;
-webkit-tap-highlight-color: transparent;
overscroll-behavior-y: none;
}
body { .JsxSelfClosingElement,
font-family: "Source Sans 3", system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; .JsxOpeningElement,
} .JsxClosingElement,
.tagName-of-JsxOpeningElement,
.tagName-of-JsxClosingElement,
.tagName-of-JsxSelfClosingElement {
color: #41a6d9;
}
.JsxText { .name-of-JsxAttribute {
color: #5c6773; color: #f08c36;
} }
.JsxSelfClosingElement, .name-of-PropertyAssignment {
.JsxOpeningElement, color: #86b300;
.JsxClosingElement, }
.tagName-of-JsxOpeningElement,
.tagName-of-JsxClosingElement,
.tagName-of-JsxSelfClosingElement {
color: #41a6d9;
}
.name-of-JsxAttribute { .name-of-PropertyAccessExpression {
color: #f08c36; color: #f08c36;
} }
.name-of-PropertyAssignment {
color: #86b300;
}
.name-of-PropertyAccessExpression {
color: #f08c36;
}

View File

@ -1,4 +1,5 @@
import { useGlobal } from "web-utils"; import { useGlobal } from "web-utils";
import { jscript } from "../../utils/script/jscript";
import { Loading } from "../../utils/ui/loading"; import { Loading } from "../../utils/ui/loading";
import { EdLeft } from "./ed-left"; import { EdLeft } from "./ed-left";
import { EdMid } from "./ed-mid"; import { EdMid } from "./ed-mid";
@ -8,6 +9,7 @@ import { edInit } from "./logic/ed-init";
import { edRoute } from "./logic/ed-route"; import { edRoute } from "./logic/ed-route";
import { edUndoManager } from "./logic/ed-undo"; import { edUndoManager } from "./logic/ed-undo";
import { EdMain } from "./panel/main/main"; import { EdMain } from "./panel/main/main";
import { EdPageHistoryMain } from "./panel/main/main-history";
import { EdPane } from "./panel/main/pane-resize"; import { EdPane } from "./panel/main/pane-resize";
import { EdPopApi } from "./panel/popup/api/api-server"; import { EdPopApi } from "./panel/popup/api/api-server";
import { EdPopCode } from "./panel/popup/code/code"; import { EdPopCode } from "./panel/popup/code/code";
@ -16,9 +18,6 @@ import { EdPopComp } from "./panel/popup/comp/comp-popup";
import { EdPopPage } from "./panel/popup/page/page-popup"; import { EdPopPage } from "./panel/popup/page/page-popup";
import { EdPopScript } from "./panel/popup/script/pop-script"; import { EdPopScript } from "./panel/popup/script/pop-script";
import { EdPopSite } from "./panel/popup/site/site-popup"; import { EdPopSite } from "./panel/popup/site/site-popup";
import { EdPageHistoryMain } from "./panel/main/main-history";
import { jscript } from "../../utils/script/jscript";
import { useEffect } from "react";
export const EdBase = () => { export const EdBase = () => {
const p = useGlobal(EDGlobal, "EDITOR"); const p = useGlobal(EDGlobal, "EDITOR");

View File

@ -1,4 +1,7 @@
export const loadCode = async (id_site: string, ts?: number) => { import { PG } from "./ed-global";
export const loadCode = async (p: PG, ts?: number) => {
const id_site = p.site.id;
const url = `/prod/${id_site}/_prasi/code/index.js?ts=${ts}`; const url = `/prod/${id_site}/_prasi/code/index.js?ts=${ts}`;
const fn = new Function( const fn = new Function(
"callback", "callback",
@ -8,9 +11,24 @@ import("${url}")
.then(callback)` .then(callback)`
); );
try { try {
fetch(`/prod/${id_site}/_prasi/typings.d.ts`)
.catch(() => {})
.then(async (res) => {
if (res) {
p.site_dts = await res.text();
p.render();
}
});
return await new Promise<any>((resolve) => { return await new Promise<any>((resolve) => {
try { try {
fn((exports: any) => { fn((exports: any) => {
const w = window as any;
for (const [k, v] of Object.entries(exports)) {
w[k] = v;
p.site_exports[k] = v;
}
resolve(exports); resolve(exports);
}); });
} catch (e) { } catch (e) {
@ -20,5 +38,6 @@ import("${url}")
} catch (e) { } catch (e) {
console.log("Failed to load site code", e); console.log("Failed to load site code", e);
} }
return {}; return {};
}; };

View File

@ -5,6 +5,8 @@ import { ESite, PG } from "./ed-global";
export const loadSite = async (p: PG, site: ESite, note: string) => { export const loadSite = async (p: PG, site: ESite, note: string) => {
p.site = site; p.site = site;
if (!p.script.db && !p.script.api) { if (!p.script.db && !p.script.api) {
if (!location.pathname.startsWith("/ed/")) { if (!location.pathname.startsWith("/ed/")) {
await viLoadLegacy({ await viLoadLegacy({

View File

@ -184,13 +184,7 @@ export const edInitSync = (p: PG) => {
p.render(); p.render();
}, },
async code_changes({ ts }) { async code_changes({ ts }) {
const w = window as any; await loadCode(p, ts);
const exports = await loadCode(p.site.id, ts);
for (const [k, v] of Object.entries(exports)) {
w[k] = v;
p.site_exports[k] = v;
}
await treeRebuild(p); await treeRebuild(p);
p.render(); p.render();
}, },

View File

@ -98,7 +98,6 @@ export const EdScriptMonaco: FC<{}> = () => {
if (p.ui.popup.script.mode === "js") { if (p.ui.popup.script.mode === "js") {
const w = window as any; const w = window as any;
const types: any = {}; const types: any = {};
const values: any = {};
for (const prop_name of p.global_prop) { for (const prop_name of p.global_prop) {
if (prop_name === "_types") continue; if (prop_name === "_types") continue;
types[prop_name] = "any"; types[prop_name] = "any";
@ -110,10 +109,6 @@ export const EdScriptMonaco: FC<{}> = () => {
} }
} }
for (const [k, v] of Object.entries(p.site_exports)) {
values[k] = v;
}
let component = { id: "", props: {} as Record<string, FNCompDef> }; let component = { id: "", props: {} as Record<string, FNCompDef> };
if (meta?.item.component?.id && meta.item.component.props) { if (meta?.item.component?.id && meta.item.component.props) {
component.id = meta.item.component.id; component.id = meta.item.component.id;
@ -185,6 +180,7 @@ export const EdScriptMonaco: FC<{}> = () => {
await monacoTypings( await monacoTypings(
{ {
site_dts: p.site_dts, site_dts: p.site_dts,
site_exports: p.site_exports,
script: { script: {
siteTypes: p.script.site_types, siteTypes: p.script.site_types,
}, },
@ -193,7 +189,7 @@ export const EdScriptMonaco: FC<{}> = () => {
monaco, monaco,
{ {
types, types,
values, values: {},
} }
); );
await jsMount(editor, monaco, p); await jsMount(editor, monaco, p);

View File

@ -17,30 +17,6 @@ export const viLoadSnapshot = async (p: PG) => {
if (api_url && apiURL.hostname) { if (api_url && apiURL.hostname) {
await loadApiProxyDef(api_url, true); await loadApiProxyDef(api_url, true);
// const api = w.prasiApi[api_url];
// if (api && api.apiTypes && api.prismaTypes) {
// const zip = JSON.stringify({
// api: api.apiTypes,
// prisma: api.prismaTypes,
// });
// const hash = simpleHash(zip);
// const res = await p.sync?.code.action({
// type: "check-typings",
// site_id: p.site.id,
// hash,
// });
// if (res?.type === "check-typings" && !res.hash) {
// const body = Buffer.from(compress(encoder.encode(zip)));
// p.sync?.code.action({
// type: "push-typings",
// site_id: p.site.id,
// body,
// hash,
// });
// }
// }
if (!p.script.db) p.script.db = dbProxy(api_url); if (!p.script.db) p.script.db = dbProxy(api_url);
if (!p.script.api) p.script.api = apiProxy(api_url); if (!p.script.api) p.script.api = apiProxy(api_url);
} }
@ -48,7 +24,6 @@ export const viLoadSnapshot = async (p: PG) => {
if (e && !e.message.toLowerCase().includes("invalid url")) { if (e && !e.message.toLowerCase().includes("invalid url")) {
console.warn("Failed to load API [Snapshot]:", api_url); console.warn("Failed to load API [Snapshot]:", api_url);
} else { } else {
// console.error(e);
} }
} }
@ -63,5 +38,5 @@ export const applyEnv = async (p: PG) => {
w.api = apiProxy(p.site.config.api_url); w.api = apiProxy(p.site.config.api_url);
} }
await loadCode(p.site.id, p.site_tstamp); await loadCode(p, p.site_tstamp);
}; };

View File

@ -21,7 +21,7 @@ export const cssFont = (
if (glbFont.loadedFonts.indexOf(font.family) < 0) { if (glbFont.loadedFonts.indexOf(font.family) < 0) {
glbFont.loadedFonts.push(font.family); glbFont.loadedFonts.push(font.family);
const doc = document; const doc = document;
const _href = `https://prasi.avolut.com/_font/css2?family=${fontName}${weight}`; const _href = `/_font/css2?family=${fontName}${weight}`;
if (!doc.querySelector(`link[href="${_href}]`)) { if (!doc.querySelector(`link[href="${_href}]`)) {
const link = doc.createElement("link"); const link = doc.createElement("link");
link.type = "text/css"; link.type = "text/css";

View File

@ -1,9 +1,8 @@
import type { OnMount } from "@monaco-editor/react"; import type { OnMount } from "@monaco-editor/react";
import { w } from "../types/general"; import { w } from "../types/general";
import { prismaExtendType } from "./prisma-extend";
import { baseTypings } from "./types/base"; import { baseTypings } from "./types/base";
import { extractProp } from "./types/prop"; import { extractProp } from "./types/prop";
import { prismaExtendType } from "./prisma-extend";
import { propPopover } from "../../nova/ed/panel/side/prop-master/prop-form";
export type MonacoEditor = Parameters<OnMount>[0]; export type MonacoEditor = Parameters<OnMount>[0];
type Monaco = Parameters<OnMount>[1]; type Monaco = Parameters<OnMount>[1];
@ -13,34 +12,33 @@ export const monacoTypings = async (
p: { p: {
site_dts: string; site_dts: string;
site: { api_url: string }; site: { api_url: string };
site_exports: Record<string, any>;
script: { siteTypes: Record<string, string> }; script: { siteTypes: Record<string, string> };
}, },
monaco: Monaco, monaco: Monaco,
prop: { values: Record<string, any>; types: Record<string, string> } prop: { values: Record<string, any>; types: Record<string, string> }
) => { ) => {
register( if (p.site_dts) {
monaco, register(monaco, p.site_dts, "ts: site.d.ts");
` register(
declare module "momo" { monaco,
export type MO = "123"; `
export const MUU = "123"; declare global {
} import * as _ from "index"
`, type MOKA = _.MOKA;
"ts: momo.d.ts"
); ${Object.keys(p.site_exports)
.map((v) => {
register( return `
monaco, const ${v} = _.${v};`;
` })
declare global { .join("\n")}
import * as _ from "momo" }
const MUU = _.MUU; export {}
} `,
export {} "ts: active_global.d.ts"
`, );
"ts: coba.d.ts" }
);
if (!map.has(prop.values)) { if (!map.has(prop.values)) {
map.set(prop.values, true); map.set(prop.values, true);
@ -107,10 +105,6 @@ declare module "ts:prisma" {
"https://cdn.jsdelivr.net/npm/@types/react@18.2.0/jsx-runtime.d.ts" "https://cdn.jsdelivr.net/npm/@types/react@18.2.0/jsx-runtime.d.ts"
), ),
}, },
{
filePath: "site.d.ts",
content: p.site_dts.replaceAll("export declare const", "declare const"),
},
]); ]);
const propText = extractProp({ const propText = extractProp({

BIN
bun.lockb

Binary file not shown.

View File

@ -3,7 +3,8 @@
"version": "0.0.1", "version": "0.0.1",
"dependencies": { "dependencies": {
"@surfy/multipart-parser": "^1.0.2", "@surfy/multipart-parser": "^1.0.2",
"@swc/core": "^1.3.95", "@swc/core": "^1.4.17",
"woodpile": "^0.0.5",
"@types/mime": "^3.0.3", "@types/mime": "^3.0.3",
"@types/unzipper": "^0.10.8", "@types/unzipper": "^0.10.8",
"esbuild": "^0.20.2", "esbuild": "^0.20.2",

View File

@ -16,8 +16,8 @@ export const cache = {
export type WSData = { url: URL }; export type WSData = { url: URL };
export const createServer = async () => { export const createServer = async () => {
await serveAPI.init(); await serveAPI.init();
await serveStatic.init(); await serveStatic.init();
const { wsHandler } = await import("../../../app/srv/ws/handler"); const { wsHandler } = await import("../../../app/srv/ws/handler");
@ -28,6 +28,7 @@ export const createServer = async () => {
async fetch(req, server) { async fetch(req, server) {
const url = new URL(req.url) as URL; const url = new URL(req.url) as URL;
const prasi = {}; const prasi = {};
const handle = async (req: Request) => { const handle = async (req: Request) => {
if (wsHandler[url.pathname]) { if (wsHandler[url.pathname]) {
if ( if (

View File

@ -18,15 +18,25 @@ const web = {
else return "static"; else return "static";
}, },
}; };
if (!g.static_cache) {
g.static_cache = {};
}
const cache = { const cache = {
static: {} as Record< static: g.static_cache as Record<
string, string,
{ type: string; content: any; compression: "" | "br" } { type: string; content: any; compression: "" | "br" }
>, >,
}; };
export const serveStatic: any = { export const serveStatic = {
async init() { async init() {
if (g.mode === "dev") {
for (const k of Object.keys(cache.static)) {
delete cache.static[k];
}
}
await this.walk(); await this.walk();
if (g.mode === "dev") { if (g.mode === "dev") {
watch(dir.path(`app/static`), async (_, filename) => { watch(dir.path(`app/static`), async (_, filename) => {
@ -42,7 +52,7 @@ export const serveStatic: any = {
}; };
} }
} catch (e: any) { } catch (e: any) {
cache.static = {} cache.static = {};
} }
} }
}); });
@ -89,11 +99,12 @@ export const serveStatic: any = {
}); });
} }
if (g.mode === 'dev' && url.pathname.endsWith('.js')) { if (g.mode === "dev" && url.pathname.endsWith(".js")) {
await this.walk(); await this.walk();
} }
file = cache.static["/index.html"]; file = cache.static["/index.html"];
if (file) { if (file) {
return new Response(file.content, { return new Response(file.content, {
headers: { headers: {

View File

@ -32,7 +32,7 @@ export const g = global as unknown as {
}) => Promise<Response | undefined>; }) => Promise<Response | undefined>;
server_runtime: Record< server_runtime: Record<
string, string,
{ null | {
api: ReturnType<typeof apiProxy>; api: ReturnType<typeof apiProxy>;
db: ReturnType<typeof dbProxy>; db: ReturnType<typeof dbProxy>;
} }
@ -56,4 +56,5 @@ export const g = global as unknown as {
apiPrepared: boolean; apiPrepared: boolean;
Y: typeof Y; Y: typeof Y;
syncronize: typeof syncronize; syncronize: typeof syncronize;
static_cache: any;
}; };