@junejs/server 0.1.0-dev.4 → 0.1.0-dev.5

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.
Files changed (96) hide show
  1. package/dist/adapter.d.ts +55 -0
  2. package/dist/adapter.js +204 -0
  3. package/dist/agent-discover.d.ts +5 -0
  4. package/dist/agent-discover.js +75 -0
  5. package/dist/agent-durable.d.ts +60 -0
  6. package/dist/agent-durable.js +106 -0
  7. package/dist/agent-native.d.ts +40 -0
  8. package/dist/agent-native.js +179 -0
  9. package/dist/app.d.ts +15 -0
  10. package/dist/app.js +218 -0
  11. package/dist/blob.d.ts +23 -0
  12. package/dist/blob.js +78 -0
  13. package/dist/build.d.ts +40 -0
  14. package/dist/build.js +551 -0
  15. package/dist/client-bundle.d.ts +15 -0
  16. package/dist/client-bundle.js +91 -0
  17. package/dist/config-loader.d.ts +9 -0
  18. package/dist/config-loader.js +25 -0
  19. package/dist/content.d.ts +39 -0
  20. package/dist/content.js +201 -0
  21. package/dist/css-modules-ambient.d.ts +10 -0
  22. package/dist/css-modules.d.ts +24 -0
  23. package/dist/css-modules.js +127 -0
  24. package/dist/css.d.ts +14 -0
  25. package/dist/css.js +114 -0
  26. package/dist/d1-BkdUg9kg.d.ts +24 -0
  27. package/dist/d1-migrate.d.ts +31 -0
  28. package/dist/d1-migrate.js +156 -0
  29. package/dist/d1.d.ts +2 -0
  30. package/dist/d1.js +32 -0
  31. package/dist/db.d.ts +11 -0
  32. package/dist/db.js +20 -0
  33. package/dist/deploy.d.ts +25 -0
  34. package/dist/deploy.js +158 -0
  35. package/dist/dev-reload.d.ts +5 -0
  36. package/dist/dev-reload.js +123 -0
  37. package/dist/dev.d.ts +13 -0
  38. package/dist/dev.js +55 -0
  39. package/dist/host.d.ts +25 -0
  40. package/dist/host.js +107 -0
  41. package/dist/index.d.ts +26 -0
  42. package/dist/index.js +26 -0
  43. package/dist/instrumentation.d.ts +4 -0
  44. package/dist/instrumentation.js +15 -0
  45. package/dist/island-registry.d.ts +8 -0
  46. package/dist/island-registry.js +117 -0
  47. package/dist/kv.d.ts +8 -0
  48. package/dist/kv.js +35 -0
  49. package/dist/migrate-BbWt-PuB.d.ts +32 -0
  50. package/dist/migrate.d.ts +2 -0
  51. package/dist/migrate.js +115 -0
  52. package/dist/mysql-driver-BsrWVqbp.d.ts +12 -0
  53. package/dist/mysql-driver.d.ts +2 -0
  54. package/dist/mysql-driver.js +52 -0
  55. package/dist/negotiate.d.ts +11 -0
  56. package/dist/negotiate.js +39 -0
  57. package/dist/pipeline-B2V6tKfO.d.ts +49 -0
  58. package/dist/pipeline.d.ts +2 -0
  59. package/dist/pipeline.js +235 -0
  60. package/dist/postgres-driver-DkkBFikA.d.ts +15 -0
  61. package/dist/postgres-driver.d.ts +2 -0
  62. package/dist/postgres-driver.js +50 -0
  63. package/dist/resources.d.ts +15 -0
  64. package/dist/resources.js +31 -0
  65. package/dist/router.d.ts +32 -0
  66. package/dist/router.js +183 -0
  67. package/dist/rsc-build.d.ts +17 -0
  68. package/dist/rsc-build.js +68 -0
  69. package/dist/rsc-bundle.d.ts +8 -0
  70. package/dist/rsc-bundle.js +39 -0
  71. package/dist/rsc-manifest.d.ts +8 -0
  72. package/dist/rsc-manifest.js +67 -0
  73. package/dist/rsc-runtime/flight-render-routes.d.ts +5 -0
  74. package/dist/rsc-runtime/flight-render-routes.js +26 -0
  75. package/dist/rsc-runtime/flight-render.d.ts +4 -0
  76. package/dist/rsc-runtime/flight-render.js +21 -0
  77. package/dist/rsc-runtime/flight-to-html.d.ts +4 -0
  78. package/dist/rsc-runtime/flight-to-html.js +32 -0
  79. package/dist/rsc-runtime/rsc-worker.d.ts +6 -0
  80. package/dist/rsc-runtime/rsc-worker.js +46 -0
  81. package/dist/rsc-runtime/rsdw-shims.d.ts +42 -0
  82. package/dist/segment.d.ts +14 -0
  83. package/dist/segment.js +33 -0
  84. package/dist/sqlite-driver.d.ts +25 -0
  85. package/dist/sqlite-driver.js +102 -0
  86. package/dist/tsconfig-jsx.d.ts +15 -0
  87. package/dist/tsconfig-jsx.js +35 -0
  88. package/dist/turso-driver-C1wEigMZ.d.ts +22 -0
  89. package/dist/turso-driver.d.ts +2 -0
  90. package/dist/turso-driver.js +68 -0
  91. package/dist/worker.d.ts +55 -0
  92. package/dist/worker.js +209 -0
  93. package/package.json +115 -23
  94. package/src/adapter.ts +4 -4
  95. package/src/client-bundle.ts +3 -1
  96. package/src/rsc-bundle.ts +3 -2
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 };
@@ -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, 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, 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, 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, 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,4 @@
1
+ //#region src/instrumentation.d.ts
2
+ declare function installAsyncContext(): Promise<boolean>;
3
+ //#endregion
4
+ export { installAsyncContext };
@@ -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
@@ -0,0 +1,8 @@
1
+ import { KvFactory } from "@junejs/core/resources";
2
+ //#region src/kv.d.ts
3
+ declare function memoryKv(): KvFactory;
4
+ declare function redisKv(opts: {
5
+ url: string;
6
+ }): KvFactory;
7
+ //#endregion
8
+ export { memoryKv, redisKv };
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 };
@@ -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,2 @@
1
+ import { n as mysql, r as mysqlJuneDb, t as MysqlConnLike } from "./mysql-driver-BsrWVqbp.js";
2
+ export { MysqlConnLike, mysql, mysqlJuneDb };
@@ -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 };