@junejs/server 0.1.0-dev.4 → 0.1.0-dev.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapter.d.ts +56 -0
- package/dist/adapter.js +211 -0
- package/dist/agent-discover.d.ts +5 -0
- package/dist/agent-discover.js +75 -0
- package/dist/agent-durable.d.ts +72 -0
- package/dist/agent-durable.js +141 -0
- package/dist/agent-native.d.ts +40 -0
- package/dist/agent-native.js +179 -0
- package/dist/app.d.ts +15 -0
- package/dist/app.js +252 -0
- package/dist/blob.d.ts +23 -0
- package/dist/blob.js +78 -0
- package/dist/build.d.ts +40 -0
- package/dist/build.js +582 -0
- package/dist/client-bundle.d.ts +15 -0
- package/dist/client-bundle.js +91 -0
- package/dist/config-loader.d.ts +9 -0
- package/dist/config-loader.js +25 -0
- package/dist/content.d.ts +39 -0
- package/dist/content.js +201 -0
- package/dist/css-modules-ambient.d.ts +10 -0
- package/dist/css-modules.d.ts +24 -0
- package/dist/css-modules.js +127 -0
- package/dist/css.d.ts +14 -0
- package/dist/css.js +114 -0
- package/dist/d1-BkdUg9kg.d.ts +24 -0
- package/dist/d1-migrate.d.ts +31 -0
- package/dist/d1-migrate.js +156 -0
- package/dist/d1.d.ts +2 -0
- package/dist/d1.js +32 -0
- package/dist/db.d.ts +11 -0
- package/dist/db.js +20 -0
- package/dist/deploy.d.ts +25 -0
- package/dist/deploy.js +158 -0
- package/dist/dev-reload.d.ts +5 -0
- package/dist/dev-reload.js +123 -0
- package/dist/dev.d.ts +13 -0
- package/dist/dev.js +55 -0
- package/dist/host.d.ts +25 -0
- package/dist/host.js +107 -0
- package/dist/index.d.ts +26 -0
- package/dist/index.js +26 -0
- package/dist/instrumentation.d.ts +4 -0
- package/dist/instrumentation.js +15 -0
- package/dist/island-registry.d.ts +8 -0
- package/dist/island-registry.js +117 -0
- package/dist/kv.d.ts +8 -0
- package/dist/kv.js +35 -0
- package/dist/migrate-BbWt-PuB.d.ts +32 -0
- package/dist/migrate.d.ts +2 -0
- package/dist/migrate.js +115 -0
- package/dist/mysql-driver-BsrWVqbp.d.ts +12 -0
- package/dist/mysql-driver.d.ts +2 -0
- package/dist/mysql-driver.js +52 -0
- package/dist/negotiate.d.ts +11 -0
- package/dist/negotiate.js +39 -0
- package/dist/pipeline-B2V6tKfO.d.ts +49 -0
- package/dist/pipeline.d.ts +2 -0
- package/dist/pipeline.js +235 -0
- package/dist/postgres-driver-DkkBFikA.d.ts +15 -0
- package/dist/postgres-driver.d.ts +2 -0
- package/dist/postgres-driver.js +50 -0
- package/dist/resources.d.ts +15 -0
- package/dist/resources.js +31 -0
- package/dist/router.d.ts +32 -0
- package/dist/router.js +183 -0
- package/dist/rsc-build.d.ts +17 -0
- package/dist/rsc-build.js +68 -0
- package/dist/rsc-bundle.d.ts +8 -0
- package/dist/rsc-bundle.js +39 -0
- package/dist/rsc-manifest.d.ts +8 -0
- package/dist/rsc-manifest.js +67 -0
- package/dist/rsc-runtime/flight-render-routes.d.ts +5 -0
- package/dist/rsc-runtime/flight-render-routes.js +26 -0
- package/dist/rsc-runtime/flight-render.d.ts +4 -0
- package/dist/rsc-runtime/flight-render.js +21 -0
- package/dist/rsc-runtime/flight-to-html.d.ts +4 -0
- package/dist/rsc-runtime/flight-to-html.js +32 -0
- package/dist/rsc-runtime/rsc-worker.d.ts +6 -0
- package/dist/rsc-runtime/rsc-worker.js +46 -0
- package/dist/rsc-runtime/rsdw-shims.d.ts +42 -0
- package/dist/segment.d.ts +14 -0
- package/dist/segment.js +33 -0
- package/dist/sqlite-driver.d.ts +25 -0
- package/dist/sqlite-driver.js +102 -0
- package/dist/static-files.d.ts +6 -0
- package/dist/static-files.js +56 -0
- package/dist/tsconfig-jsx.d.ts +15 -0
- package/dist/tsconfig-jsx.js +35 -0
- package/dist/turso-driver-C1wEigMZ.d.ts +22 -0
- package/dist/turso-driver.d.ts +2 -0
- package/dist/turso-driver.js +68 -0
- package/dist/worker.d.ts +55 -0
- package/dist/worker.js +211 -0
- package/package.json +115 -23
- package/src/adapter.ts +20 -7
- package/src/agent-discover.ts +5 -2
- package/src/agent-durable.ts +108 -4
- package/src/app.ts +74 -2
- package/src/build.ts +52 -3
- package/src/client-bundle.ts +3 -1
- package/src/index.ts +4 -0
- package/src/rsc-bundle.ts +3 -2
- package/src/static-files.ts +84 -0
- package/src/worker.ts +46 -29
package/dist/host.js
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { openLocalSqlite } from "./sqlite-driver.js";
|
|
2
|
+
import { spawn } from "node:child_process";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { Readable } from "node:stream";
|
|
5
|
+
//#region src/host.ts
|
|
6
|
+
function bunHost() {
|
|
7
|
+
return {
|
|
8
|
+
name: "bun",
|
|
9
|
+
serve(handler, opts) {
|
|
10
|
+
const server = Bun.serve({
|
|
11
|
+
port: opts.port,
|
|
12
|
+
fetch: handler,
|
|
13
|
+
idleTimeout: 0
|
|
14
|
+
});
|
|
15
|
+
return {
|
|
16
|
+
port: server.port ?? opts.port,
|
|
17
|
+
stop: (force) => void server.stop(force)
|
|
18
|
+
};
|
|
19
|
+
},
|
|
20
|
+
spawnModule(entry, args, opts) {
|
|
21
|
+
const cmd = [
|
|
22
|
+
"bun",
|
|
23
|
+
...(opts.conditions ?? []).flatMap((c) => ["--conditions", c]),
|
|
24
|
+
entry,
|
|
25
|
+
...args
|
|
26
|
+
];
|
|
27
|
+
const child = Bun.spawn({
|
|
28
|
+
cmd,
|
|
29
|
+
cwd: process.cwd(),
|
|
30
|
+
stdout: "pipe",
|
|
31
|
+
stderr: "pipe"
|
|
32
|
+
});
|
|
33
|
+
return {
|
|
34
|
+
stdout: child.stdout,
|
|
35
|
+
stderrText: () => new Response(child.stderr).text(),
|
|
36
|
+
exited: child.exited
|
|
37
|
+
};
|
|
38
|
+
},
|
|
39
|
+
openDb: openLocalSqlite
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function nodeHost() {
|
|
43
|
+
return {
|
|
44
|
+
name: "node",
|
|
45
|
+
serve(handler, opts) {
|
|
46
|
+
const server = createServer(async (req, res) => {
|
|
47
|
+
try {
|
|
48
|
+
const hints = opts.earlyHints?.();
|
|
49
|
+
if (hints?.length && req.method === "GET" && (req.headers.accept ?? "").includes("text/html")) res.writeEarlyHints({ link: hints });
|
|
50
|
+
const url = `http://${req.headers.host ?? "localhost"}${req.url ?? "/"}`;
|
|
51
|
+
const hasBody = req.method !== "GET" && req.method !== "HEAD";
|
|
52
|
+
const response = await handler(new Request(url, {
|
|
53
|
+
method: req.method,
|
|
54
|
+
headers: req.headers,
|
|
55
|
+
body: hasBody ? Readable.toWeb(req) : void 0,
|
|
56
|
+
duplex: "half"
|
|
57
|
+
}));
|
|
58
|
+
res.writeHead(response.status, Object.fromEntries(response.headers.entries()));
|
|
59
|
+
if (response.body) Readable.fromWeb(response.body).pipe(res);
|
|
60
|
+
else res.end();
|
|
61
|
+
} catch (err) {
|
|
62
|
+
console.error("[june:node] request failed", err);
|
|
63
|
+
if (!res.headersSent) res.writeHead(500);
|
|
64
|
+
res.end("Internal Server Error");
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
server.listen(opts.port);
|
|
68
|
+
return {
|
|
69
|
+
get port() {
|
|
70
|
+
const addr = server.address();
|
|
71
|
+
return typeof addr === "object" && addr ? addr.port : opts.port;
|
|
72
|
+
},
|
|
73
|
+
stop(force) {
|
|
74
|
+
server.close();
|
|
75
|
+
if (force) server.closeAllConnections?.();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
spawnModule(entry, args, opts) {
|
|
80
|
+
const child = spawn(process.execPath, [
|
|
81
|
+
"--import",
|
|
82
|
+
"tsx",
|
|
83
|
+
...(opts.conditions ?? []).flatMap((c) => ["--conditions", c]),
|
|
84
|
+
entry,
|
|
85
|
+
...args
|
|
86
|
+
], {
|
|
87
|
+
cwd: process.cwd(),
|
|
88
|
+
stdio: [
|
|
89
|
+
"ignore",
|
|
90
|
+
"pipe",
|
|
91
|
+
"pipe"
|
|
92
|
+
]
|
|
93
|
+
});
|
|
94
|
+
const errChunks = [];
|
|
95
|
+
child.stderr.on("data", (c) => errChunks.push(c));
|
|
96
|
+
return {
|
|
97
|
+
stdout: Readable.toWeb(child.stdout),
|
|
98
|
+
stderrText: async () => Buffer.concat(errChunks).toString(),
|
|
99
|
+
exited: new Promise((resolve) => child.on("exit", (code) => resolve(code ?? 0)))
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
openDb: openLocalSqlite
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
const host = typeof Bun !== "undefined" ? bunHost() : nodeHost();
|
|
106
|
+
//#endregion
|
|
107
|
+
export { host };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AdapterCapabilities, AdapterEmitContext, AdapterEntry, JuneAdapter, ResourcePlan, deno, staticSite, vercel, workers } from "./adapter.js";
|
|
2
|
+
import { CreateAppOptions, JuneApp, createApp } from "./app.js";
|
|
3
|
+
import { R2Bucket, localBlob, r2 } from "./blob.js";
|
|
4
|
+
import { a as Pipeline, d as createPipeline, n as LayoutComponent, o as PipelineConfig, s as Resolved, u as RouteResolver } from "./pipeline-B2V6tKfO.js";
|
|
5
|
+
import { WorkerManifest, createWorker } from "./worker.js";
|
|
6
|
+
import { BuildResult, buildManifest, freezeConfig, generateContent, juneBuild, scanRoutes } from "./build.js";
|
|
7
|
+
import { loadJuneConfig } from "./config-loader.js";
|
|
8
|
+
import { ContentEntry, collection, entry } from "./content.js";
|
|
9
|
+
import { a as blockedMessage, c as migrateApp, l as readMigrations, n as MigrateResult, o as classify, r as Migration, s as migrate, u as typesApp } from "./migrate-BbWt-PuB.js";
|
|
10
|
+
import { D1Exec, migrateD1, resolveD1Database, wranglerD1 } from "./d1-migrate.js";
|
|
11
|
+
import { n as d1, t as D1Database } from "./d1-BkdUg9kg.js";
|
|
12
|
+
import { r as postgres } from "./postgres-driver-DkkBFikA.js";
|
|
13
|
+
import { n as mysql } from "./mysql-driver-BsrWVqbp.js";
|
|
14
|
+
import { i as turso } from "./turso-driver-C1wEigMZ.js";
|
|
15
|
+
import { sqlite } from "./db.js";
|
|
16
|
+
import { DeployResult, juneDeploy } from "./deploy.js";
|
|
17
|
+
import { JuneDb, JuneHost, RunResult, ServeHandle, SpawnedModule, host } from "./host.js";
|
|
18
|
+
import { DevServer, DevServerOptions, startDevServer } from "./dev.js";
|
|
19
|
+
import { memoryKv, redisKv } from "./kv.js";
|
|
20
|
+
import { ResourceFlags, WorkerEnv, bindWorkerResources, memoizeResources } from "./resources.js";
|
|
21
|
+
import { MatchOptions, RouteMatch, RouteTreeMatch, SegmentMatch, listRoutes, matchRoute, matchRouteTree, resolveNotFound } from "./router.js";
|
|
22
|
+
import { installAsyncContext } from "./instrumentation.js";
|
|
23
|
+
import { Negotiated, negotiate } from "./negotiate.js";
|
|
24
|
+
import { blob, currentServices, db, kv } from "@junejs/db";
|
|
25
|
+
import { I18nConfig, LOCALE_COOKIE, LocaleConfig, LocaleMatch, dirForLang, localeDir, localeHref, matchPinnedLocale, negotiateLocale, parseAcceptLanguage, resolveRequestLocale } from "@junejs/core/i18n";
|
|
26
|
+
export { type AdapterCapabilities, type AdapterEmitContext, type AdapterEntry, type BuildResult, type ContentEntry, type CreateAppOptions, type D1Database, type D1Exec, type DeployResult, type DevServer, type DevServerOptions, type I18nConfig, type JuneAdapter, type JuneApp, type JuneDb, type JuneHost, LOCALE_COOKIE, type LayoutComponent, type LocaleConfig, type LocaleMatch, type MatchOptions, type MigrateResult, type Migration, type Negotiated, type Pipeline, type PipelineConfig, type R2Bucket, type Resolved, type ResourceFlags, type ResourcePlan, type RouteMatch, type RouteResolver, type RouteTreeMatch, type RunResult, type SegmentMatch, type ServeHandle, type SpawnedModule, type WorkerEnv, type WorkerManifest, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, currentServices, d1, db, deno, dirForLang, entry, freezeConfig, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { deno, staticSite, vercel, workers } from "./adapter.js";
|
|
2
|
+
import { listRoutes, matchRoute, matchRouteTree, resolveNotFound } from "./router.js";
|
|
3
|
+
import { negotiate } from "./negotiate.js";
|
|
4
|
+
import { createPipeline } from "./pipeline.js";
|
|
5
|
+
import { d1 } from "./d1.js";
|
|
6
|
+
import { bindWorkerResources, memoizeResources } from "./resources.js";
|
|
7
|
+
import { createApp } from "./app.js";
|
|
8
|
+
import { localBlob, r2 } from "./blob.js";
|
|
9
|
+
import { loadJuneConfig } from "./config-loader.js";
|
|
10
|
+
import { collection, entry } from "./content.js";
|
|
11
|
+
import { createWorker } from "./worker.js";
|
|
12
|
+
import { buildManifest, freezeConfig, generateContent, juneBuild, scanRoutes } from "./build.js";
|
|
13
|
+
import { blockedMessage, classify, migrate, migrateApp, readMigrations, typesApp } from "./migrate.js";
|
|
14
|
+
import { migrateD1, resolveD1Database, wranglerD1 } from "./d1-migrate.js";
|
|
15
|
+
import { postgres } from "./postgres-driver.js";
|
|
16
|
+
import { mysql } from "./mysql-driver.js";
|
|
17
|
+
import { turso } from "./turso-driver.js";
|
|
18
|
+
import { sqlite } from "./db.js";
|
|
19
|
+
import { juneDeploy } from "./deploy.js";
|
|
20
|
+
import { installAsyncContext } from "./instrumentation.js";
|
|
21
|
+
import { host } from "./host.js";
|
|
22
|
+
import { startDevServer } from "./dev.js";
|
|
23
|
+
import { memoryKv, redisKv } from "./kv.js";
|
|
24
|
+
import { blob, currentServices, db, kv } from "@junejs/db";
|
|
25
|
+
import { LOCALE_COOKIE, dirForLang, localeDir, localeHref, matchPinnedLocale, negotiateLocale, parseAcceptLanguage, resolveRequestLocale } from "@junejs/core/i18n";
|
|
26
|
+
export { LOCALE_COOKIE, bindWorkerResources, blob, blockedMessage, buildManifest, classify, collection, createApp, createPipeline, createWorker, currentServices, d1, db, deno, dirForLang, entry, freezeConfig, generateContent, host, installAsyncContext, juneBuild, juneDeploy, kv, listRoutes, loadJuneConfig, localBlob, localeDir, localeHref, matchPinnedLocale, matchRoute, matchRouteTree, memoizeResources, memoryKv, migrate, migrateApp, migrateD1, mysql, negotiate, negotiateLocale, parseAcceptLanguage, postgres, r2, readMigrations, redisKv, resolveD1Database, resolveNotFound, resolveRequestLocale, scanRoutes, sqlite, startDevServer, staticSite, turso, typesApp, vercel, workers, wranglerD1 };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { installTraceContext } from "@junejs/core/instrumentation";
|
|
2
|
+
//#region src/instrumentation.ts
|
|
3
|
+
let installed = false;
|
|
4
|
+
async function installAsyncContext() {
|
|
5
|
+
if (installed) return true;
|
|
6
|
+
try {
|
|
7
|
+
installTraceContext(new (await (import("node:async_hooks"))).AsyncLocalStorage());
|
|
8
|
+
installed = true;
|
|
9
|
+
return true;
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
export { installAsyncContext };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
//#region src/island-registry.d.ts
|
|
2
|
+
declare const ISLAND_REGISTRY_FILE = "_islands.gen.ts";
|
|
3
|
+
declare function walk(dir: string): string[];
|
|
4
|
+
declare function firstStatementIsDirective(src: string, directive: string): boolean;
|
|
5
|
+
declare function exportNames(src: string): string[];
|
|
6
|
+
declare function generateIslandRegistry(appDir: string): Promise<number>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { ISLAND_REGISTRY_FILE, exportNames, firstStatementIsDirective, generateIslandRegistry, walk };
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
3
|
+
//#region src/island-registry.ts
|
|
4
|
+
const ISLAND_REGISTRY_FILE = "_islands.gen.ts";
|
|
5
|
+
function walk(dir) {
|
|
6
|
+
return readdirSync(dir).flatMap((name) => {
|
|
7
|
+
const p = join(dir, name);
|
|
8
|
+
return statSync(p).isDirectory() ? walk(p) : [p];
|
|
9
|
+
});
|
|
10
|
+
}
|
|
11
|
+
function firstStatementIsDirective(src, directive) {
|
|
12
|
+
for (const line of src.split("\n")) {
|
|
13
|
+
const t = line.trim();
|
|
14
|
+
if (!t || t.startsWith("//")) continue;
|
|
15
|
+
return t.startsWith(`"${directive}"`) || t.startsWith(`'${directive}'`);
|
|
16
|
+
}
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
function exportNames(src) {
|
|
20
|
+
const names = [];
|
|
21
|
+
for (const line of src.split("\n")) {
|
|
22
|
+
const t = line.trim();
|
|
23
|
+
const decl = t.match(/^export\s+(?:async\s+)?(?:function|const|let|var|class)\s+([A-Za-z0-9_$]+)/);
|
|
24
|
+
if (decl) names.push(decl[1]);
|
|
25
|
+
const named = t.match(/^export\s*\{([^}]*)\}/);
|
|
26
|
+
if (named) for (const part of named[1].split(",")) {
|
|
27
|
+
const e = part.split(" as ").pop()?.trim();
|
|
28
|
+
if (e) names.push(e);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
return names;
|
|
32
|
+
}
|
|
33
|
+
function importMap(program) {
|
|
34
|
+
const map = /* @__PURE__ */ new Map();
|
|
35
|
+
for (const n of program.body) {
|
|
36
|
+
if (n.type !== "ImportDeclaration" || typeof n.source?.value !== "string") continue;
|
|
37
|
+
const spec = n.source.value;
|
|
38
|
+
for (const s of n.specifiers ?? []) if (s.type === "ImportSpecifier") map.set(s.local.name, {
|
|
39
|
+
spec,
|
|
40
|
+
imported: s.imported.name,
|
|
41
|
+
kind: "named"
|
|
42
|
+
});
|
|
43
|
+
else if (s.type === "ImportDefaultSpecifier") map.set(s.local.name, {
|
|
44
|
+
spec,
|
|
45
|
+
imported: "default",
|
|
46
|
+
kind: "default"
|
|
47
|
+
});
|
|
48
|
+
else if (s.type === "ImportNamespaceSpecifier") map.set(s.local.name, {
|
|
49
|
+
spec,
|
|
50
|
+
imported: "*",
|
|
51
|
+
kind: "namespace"
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
return map;
|
|
55
|
+
}
|
|
56
|
+
function hasClientDirective(openingElement) {
|
|
57
|
+
return (openingElement.attributes ?? []).some((a) => a.type === "JSXAttribute" && a.name?.type === "JSXNamespacedName" && a.name.namespace?.name === "client");
|
|
58
|
+
}
|
|
59
|
+
function islandTags(program) {
|
|
60
|
+
const tags = [];
|
|
61
|
+
(function walkNode(node) {
|
|
62
|
+
if (!node || typeof node !== "object") return;
|
|
63
|
+
if (Array.isArray(node)) return node.forEach(walkNode);
|
|
64
|
+
if (node.type === "JSXElement" && node.openingElement?.name?.type === "JSXIdentifier" && hasClientDirective(node.openingElement)) tags.push(node.openingElement.name.name);
|
|
65
|
+
for (const k in node) if (k !== "type") walkNode(node[k]);
|
|
66
|
+
})(program);
|
|
67
|
+
return tags;
|
|
68
|
+
}
|
|
69
|
+
function resolveLocalModule(fromFile, spec) {
|
|
70
|
+
const base = resolve(dirname(fromFile), spec);
|
|
71
|
+
const exts = [
|
|
72
|
+
".tsx",
|
|
73
|
+
".ts",
|
|
74
|
+
".jsx",
|
|
75
|
+
".js"
|
|
76
|
+
];
|
|
77
|
+
for (const e of exts) if (existsSync(base + e)) return base + e;
|
|
78
|
+
for (const e of exts) if (existsSync(join(base, "index" + e))) return join(base, "index" + e);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
async function generateIslandRegistry(appDir) {
|
|
82
|
+
const { parseSync } = await import("oxc-parser");
|
|
83
|
+
const seen = /* @__PURE__ */ new Map();
|
|
84
|
+
const entries = [];
|
|
85
|
+
for (const f of walk(appDir)) {
|
|
86
|
+
if (!/\.(tsx|jsx)$/.test(f) || f.endsWith("_islands.gen.ts") || f.endsWith(".gen.ts")) continue;
|
|
87
|
+
const src = readFileSync(f, "utf8");
|
|
88
|
+
if (!src.includes("client:")) continue;
|
|
89
|
+
const { program } = parseSync(f, src);
|
|
90
|
+
const ast = program;
|
|
91
|
+
const imports = importMap(ast);
|
|
92
|
+
const rel = "./" + relative(appDir, f);
|
|
93
|
+
for (const tag of islandTags(ast)) {
|
|
94
|
+
const imp = imports.get(tag);
|
|
95
|
+
if (!imp) throw new Error(`[june] <${tag} client:*/> in ${rel}: an island must be an IMPORTED component (a named import), not a local definition.`);
|
|
96
|
+
if (imp.kind !== "named") throw new Error(`[june] <${tag} client:*/> in ${rel}: island imports must be NAMED imports (a named \`{ ${tag} }\` import; default/namespace imports aren't supported as islands).`);
|
|
97
|
+
const name = imp.imported;
|
|
98
|
+
const isRelative = imp.spec.startsWith(".");
|
|
99
|
+
const loaderSpec = isRelative ? "./" + relative(appDir, resolve(dirname(f), imp.spec)) : imp.spec;
|
|
100
|
+
if (isRelative) {
|
|
101
|
+
const modFile = resolveLocalModule(f, imp.spec);
|
|
102
|
+
if (modFile && !firstStatementIsDirective(readFileSync(modFile, "utf8"), "use client")) throw new Error(`[june] island "${name}" (./${relative(appDir, modFile)}) is used with client:* but its module is not "use client". An island must be a client module.`);
|
|
103
|
+
}
|
|
104
|
+
const prev = seen.get(name);
|
|
105
|
+
if (prev && prev !== loaderSpec) throw new Error(`[june] duplicate island name "${name}" from different modules ("${prev}" and "${loaderSpec}"). Island names must be unique.`);
|
|
106
|
+
if (seen.has(name)) continue;
|
|
107
|
+
seen.set(name, loaderSpec);
|
|
108
|
+
entries.push(` ${JSON.stringify(name)}: () => import(${JSON.stringify(loaderSpec)}).then((m) => m.${name}),`);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
entries.sort();
|
|
112
|
+
const out = "// AUTO-GENERATED by June — do not edit. Lazy loaders for client:* islands.\n// Maps each island name to a loader that resolves its component; hydrateIslands uses it.\nexport const ISLAND_LOADERS: Record<string, () => Promise<unknown>> = {\n" + (entries.length ? entries.join("\n") + "\n" : "") + "};\n";
|
|
113
|
+
writeFileSync(join(appDir, ISLAND_REGISTRY_FILE), out);
|
|
114
|
+
return entries.length;
|
|
115
|
+
}
|
|
116
|
+
//#endregion
|
|
117
|
+
export { ISLAND_REGISTRY_FILE, exportNames, firstStatementIsDirective, generateIslandRegistry, walk };
|
package/dist/kv.d.ts
ADDED
package/dist/kv.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { memory, redis } from "@junejs/core/cache";
|
|
2
|
+
//#region src/kv.ts
|
|
3
|
+
function kvOver(store) {
|
|
4
|
+
return {
|
|
5
|
+
async get(key) {
|
|
6
|
+
return (await store.get(key))?.value ?? null;
|
|
7
|
+
},
|
|
8
|
+
async put(key, value, opts) {
|
|
9
|
+
const expiresAt = opts?.ttl ? Date.now() + opts.ttl * 1e3 : null;
|
|
10
|
+
await store.set(key, {
|
|
11
|
+
value,
|
|
12
|
+
expiresAt,
|
|
13
|
+
staleUntil: expiresAt,
|
|
14
|
+
tags: []
|
|
15
|
+
});
|
|
16
|
+
},
|
|
17
|
+
async delete(key) {
|
|
18
|
+
await store.delete(key);
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function memoryKv() {
|
|
23
|
+
return {
|
|
24
|
+
kind: "memory",
|
|
25
|
+
open: async () => kvOver(await memory().connect())
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
function redisKv(opts) {
|
|
29
|
+
return {
|
|
30
|
+
kind: "redis",
|
|
31
|
+
open: async () => kvOver(await redis(opts).connect())
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
//#endregion
|
|
35
|
+
export { memoryKv, redisKv };
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { JuneConfig } from "@junejs/core/config";
|
|
2
|
+
import { JuneDb } from "@junejs/core/resources";
|
|
3
|
+
//#region src/migrate.d.ts
|
|
4
|
+
type Migration = {
|
|
5
|
+
id: string;
|
|
6
|
+
sql: string;
|
|
7
|
+
};
|
|
8
|
+
type ClassifiedMigration = Migration & {
|
|
9
|
+
destructive: boolean;
|
|
10
|
+
reasons: string[];
|
|
11
|
+
};
|
|
12
|
+
type MigrateResult = {
|
|
13
|
+
applied: string[];
|
|
14
|
+
blocked: ClassifiedMigration | null;
|
|
15
|
+
};
|
|
16
|
+
declare function classify(sql: string): {
|
|
17
|
+
destructive: boolean;
|
|
18
|
+
reasons: string[];
|
|
19
|
+
};
|
|
20
|
+
declare function readMigrations(dir: string): Promise<Migration[]>;
|
|
21
|
+
declare function appliedIds(db: JuneDb): Promise<Set<string>>;
|
|
22
|
+
declare function migrate(db: JuneDb, dir: string, opts?: {
|
|
23
|
+
allowDestructive?: boolean;
|
|
24
|
+
now?: string;
|
|
25
|
+
}): Promise<MigrateResult>;
|
|
26
|
+
declare function migrateApp(root: string, config: JuneConfig, opts?: {
|
|
27
|
+
allowDestructive?: boolean;
|
|
28
|
+
}): Promise<MigrateResult | null>;
|
|
29
|
+
declare function typesApp(root: string, config: JuneConfig): Promise<string | null>;
|
|
30
|
+
declare function blockedMessage(m: ClassifiedMigration): string;
|
|
31
|
+
//#endregion
|
|
32
|
+
export { blockedMessage as a, migrateApp as c, appliedIds as i, readMigrations as l, MigrateResult as n, classify as o, Migration as r, migrate as s, ClassifiedMigration as t, typesApp as u };
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import { a as blockedMessage, c as migrateApp, i as appliedIds, l as readMigrations, n as MigrateResult, o as classify, r as Migration, s as migrate, t as ClassifiedMigration, u as typesApp } from "./migrate-BbWt-PuB.js";
|
|
2
|
+
export { ClassifiedMigration, MigrateResult, Migration, appliedIds, blockedMessage, classify, migrate, migrateApp, readMigrations, typesApp };
|
package/dist/migrate.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { existsSync } from "node:fs";
|
|
2
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
//#region src/migrate.ts
|
|
5
|
+
const DESTRUCTIVE = [
|
|
6
|
+
{
|
|
7
|
+
re: /\bdrop\s+table\b/i,
|
|
8
|
+
label: "DROP TABLE"
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
re: /\bdrop\s+index\b/i,
|
|
12
|
+
label: "DROP INDEX"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
re: /\bdrop\s+view\b/i,
|
|
16
|
+
label: "DROP VIEW"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
re: /\bdrop\s+trigger\b/i,
|
|
20
|
+
label: "DROP TRIGGER"
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
re: /\bdelete\s+from\b/i,
|
|
24
|
+
label: "DELETE"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
re: /\btruncate\b/i,
|
|
28
|
+
label: "TRUNCATE"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
re: /\balter\s+table\b[\s\S]*?\bdrop\b/i,
|
|
32
|
+
label: "ALTER TABLE … DROP"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
re: /\balter\s+table\b[\s\S]*?\brename\b/i,
|
|
36
|
+
label: "ALTER TABLE … RENAME"
|
|
37
|
+
}
|
|
38
|
+
];
|
|
39
|
+
function stripComments(sql) {
|
|
40
|
+
return sql.replace(/--[^\n]*/g, "").replace(/\/\*[\s\S]*?\*\//g, "");
|
|
41
|
+
}
|
|
42
|
+
function classify(sql) {
|
|
43
|
+
const clean = stripComments(sql);
|
|
44
|
+
const reasons = DESTRUCTIVE.filter((d) => d.re.test(clean)).map((d) => d.label);
|
|
45
|
+
return {
|
|
46
|
+
destructive: reasons.length > 0,
|
|
47
|
+
reasons
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
async function readMigrations(dir) {
|
|
51
|
+
if (!existsSync(dir)) return [];
|
|
52
|
+
const files = (await readdir(dir)).filter((f) => f.endsWith(".sql")).sort();
|
|
53
|
+
return Promise.all(files.map(async (id) => ({
|
|
54
|
+
id,
|
|
55
|
+
sql: await readFile(join(dir, id), "utf8")
|
|
56
|
+
})));
|
|
57
|
+
}
|
|
58
|
+
async function ensureLedger(db) {
|
|
59
|
+
await db.exec(`create table if not exists _june_migrations (id text primary key, applied_at text not null)`);
|
|
60
|
+
}
|
|
61
|
+
async function appliedIds(db) {
|
|
62
|
+
await ensureLedger(db);
|
|
63
|
+
const rows = await db.query("select id from _june_migrations");
|
|
64
|
+
return new Set(rows.map((r) => r.id));
|
|
65
|
+
}
|
|
66
|
+
async function migrate(db, dir, opts = {}) {
|
|
67
|
+
const already = await appliedIds(db);
|
|
68
|
+
const pending = (await readMigrations(dir)).filter((m) => !already.has(m.id));
|
|
69
|
+
const stamp = opts.now ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
70
|
+
const applied = [];
|
|
71
|
+
for (const m of pending) {
|
|
72
|
+
const c = classify(m.sql);
|
|
73
|
+
if (c.destructive && !opts.allowDestructive) return {
|
|
74
|
+
applied,
|
|
75
|
+
blocked: {
|
|
76
|
+
...m,
|
|
77
|
+
...c
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
await db.exec(m.sql);
|
|
81
|
+
await db.run("insert into _june_migrations (id, applied_at) values (?, ?)", [m.id, stamp]);
|
|
82
|
+
applied.push(m.id);
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
applied,
|
|
86
|
+
blocked: null
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
async function migrateApp(root, config, opts = {}) {
|
|
90
|
+
const factory = config.resources?.db;
|
|
91
|
+
if (!factory) return null;
|
|
92
|
+
const db = await factory.open();
|
|
93
|
+
try {
|
|
94
|
+
return await migrate(db, join(root, "db", "migrations"), opts);
|
|
95
|
+
} finally {
|
|
96
|
+
await db.close();
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async function typesApp(root, config) {
|
|
100
|
+
const factory = config.resources?.db;
|
|
101
|
+
const emit = config.dataLayer?.emitTypes;
|
|
102
|
+
if (!factory || !emit) return null;
|
|
103
|
+
const db = await factory.open();
|
|
104
|
+
try {
|
|
105
|
+
await migrate(db, join(root, "db", "migrations"), { allowDestructive: false });
|
|
106
|
+
return await emit(db);
|
|
107
|
+
} finally {
|
|
108
|
+
await db.close();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function blockedMessage(m) {
|
|
112
|
+
return `migration ${m.id} is destructive (${m.reasons.join(", ")}) and was NOT applied.\n review it, then apply explicitly: june db migrate --allow-destructive`;
|
|
113
|
+
}
|
|
114
|
+
//#endregion
|
|
115
|
+
export { appliedIds, blockedMessage, classify, migrate, migrateApp, readMigrations, typesApp };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DbFactory, JuneDb } from "@junejs/core/resources";
|
|
2
|
+
//#region src/mysql-driver.d.ts
|
|
3
|
+
interface MysqlConnLike {
|
|
4
|
+
query(sql: string, params?: unknown[]): Promise<[unknown, unknown]>;
|
|
5
|
+
end(): Promise<void>;
|
|
6
|
+
}
|
|
7
|
+
declare function mysqlJuneDb(conn: MysqlConnLike): JuneDb;
|
|
8
|
+
declare function mysql(opts: {
|
|
9
|
+
url: string;
|
|
10
|
+
}): DbFactory;
|
|
11
|
+
//#endregion
|
|
12
|
+
export { mysql as n, mysqlJuneDb as r, MysqlConnLike as t };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
//#region src/mysql-driver.ts
|
|
2
|
+
function mysqlJuneDb(conn) {
|
|
3
|
+
const self = {
|
|
4
|
+
dialect: "mysql",
|
|
5
|
+
async query(sql, params = []) {
|
|
6
|
+
return (await conn.query(sql, params))[0];
|
|
7
|
+
},
|
|
8
|
+
async get(sql, params = []) {
|
|
9
|
+
return (await conn.query(sql, params))[0][0] ?? void 0;
|
|
10
|
+
},
|
|
11
|
+
async run(sql, params = []) {
|
|
12
|
+
const header = (await conn.query(sql, params))[0];
|
|
13
|
+
return {
|
|
14
|
+
changes: header.affectedRows ?? 0,
|
|
15
|
+
lastInsertRowid: header.insertId ?? 0
|
|
16
|
+
};
|
|
17
|
+
},
|
|
18
|
+
async exec(sql) {
|
|
19
|
+
await conn.query(sql);
|
|
20
|
+
},
|
|
21
|
+
async transaction(fn) {
|
|
22
|
+
await conn.query("begin");
|
|
23
|
+
try {
|
|
24
|
+
const out = await fn(self);
|
|
25
|
+
await conn.query("commit");
|
|
26
|
+
return out;
|
|
27
|
+
} catch (e) {
|
|
28
|
+
await conn.query("rollback");
|
|
29
|
+
throw e;
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
async close() {
|
|
33
|
+
await conn.end();
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
return self;
|
|
37
|
+
}
|
|
38
|
+
function mysql(opts) {
|
|
39
|
+
return {
|
|
40
|
+
kind: "mysql",
|
|
41
|
+
open: async () => {
|
|
42
|
+
const mod = await import("mysql2/promise");
|
|
43
|
+
const create = (mod.default ?? mod).createConnection;
|
|
44
|
+
return mysqlJuneDb(await create({
|
|
45
|
+
uri: opts.url,
|
|
46
|
+
multipleStatements: true
|
|
47
|
+
}));
|
|
48
|
+
}
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
//#endregion
|
|
52
|
+
export { mysql, mysqlJuneDb };
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { RenderTarget } from "@junejs/core/route";
|
|
2
|
+
import { FRAGMENT_ACCEPT, SEGMENT_HEADER, TITLE_HEADER, encodeTitle } from "@junejs/core/nav-protocol";
|
|
3
|
+
//#region src/negotiate.d.ts
|
|
4
|
+
type Negotiated = {
|
|
5
|
+
target: RenderTarget;
|
|
6
|
+
pathname: string;
|
|
7
|
+
speculative: boolean;
|
|
8
|
+
};
|
|
9
|
+
declare function negotiate(url: URL, request: Request, basePath?: string): Negotiated;
|
|
10
|
+
//#endregion
|
|
11
|
+
export { FRAGMENT_ACCEPT, Negotiated, SEGMENT_HEADER, TITLE_HEADER, encodeTitle, negotiate };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { FRAGMENT_ACCEPT, SEGMENT_HEADER, TITLE_HEADER, encodeTitle } from "@junejs/core/nav-protocol";
|
|
2
|
+
//#region src/negotiate.ts
|
|
3
|
+
const EXT_TARGET = {
|
|
4
|
+
".json": "json",
|
|
5
|
+
".md": "md"
|
|
6
|
+
};
|
|
7
|
+
const ACCEPT_TARGET = [
|
|
8
|
+
[/text\/vnd\.june\.fragment\+html/, "fragment"],
|
|
9
|
+
[/text\/markdown/, "md"],
|
|
10
|
+
[/application\/json/, "json"]
|
|
11
|
+
];
|
|
12
|
+
function negotiate(url, request, basePath) {
|
|
13
|
+
const original = basePath ?? url.pathname;
|
|
14
|
+
let pathname = original;
|
|
15
|
+
let target = null;
|
|
16
|
+
for (const [ext, t] of Object.entries(EXT_TARGET)) if (pathname.endsWith(ext)) {
|
|
17
|
+
target = t;
|
|
18
|
+
pathname = pathname.slice(0, -ext.length) || "/";
|
|
19
|
+
break;
|
|
20
|
+
}
|
|
21
|
+
if (pathname === "/index") pathname = "/";
|
|
22
|
+
else if (target && pathname === "/") pathname = original;
|
|
23
|
+
if (!target) {
|
|
24
|
+
const accept = request.headers.get("accept") ?? "";
|
|
25
|
+
for (const [re, t] of ACCEPT_TARGET) if (re.test(accept)) {
|
|
26
|
+
target = t;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const purpose = request.headers.get("sec-purpose") ?? "";
|
|
31
|
+
const speculative = /prefetch|prerender/.test(purpose);
|
|
32
|
+
return {
|
|
33
|
+
target: target ?? "view",
|
|
34
|
+
pathname,
|
|
35
|
+
speculative
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
//#endregion
|
|
39
|
+
export { FRAGMENT_ACCEPT, SEGMENT_HEADER, TITLE_HEADER, encodeTitle, negotiate };
|