@lunora/vite 1.0.0-alpha.1 → 1.0.0-alpha.100
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/LICENSE.md +6 -0
- package/README.md +15 -0
- package/__assets__/package-og.svg +1 -1
- package/dist/index.d.mts +351 -254
- package/dist/index.d.ts +351 -254
- package/dist/index.mjs +3 -153
- package/dist/packem_shared/CLASS_A_WIRING-QlSvg7Ku.mjs +33 -0
- package/dist/packem_shared/DEV_WORKER_ENV_VALUE-CdVg3e_J.mjs +1 -0
- package/dist/packem_shared/LUNORA_API_UPDATED_EVENT-CO2Rv2re.mjs +1 -0
- package/dist/packem_shared/STUDIO_PATH-CCACdfgp.mjs +1 -0
- package/dist/packem_shared/WORKER_STARTUP_HINT-Dsm2vG6Q.mjs +5 -0
- package/dist/packem_shared/checkLunoraProxy-n_jVuOU-.mjs +1 -0
- package/dist/packem_shared/codegenPlugin-Cw1lSUC0.mjs +3 -0
- package/dist/packem_shared/containerLogsPlugin-QOsfYAYL.mjs +1 -0
- package/dist/packem_shared/devStatePlugin-CMRBp9zZ.mjs +1 -0
- package/dist/packem_shared/devVariablesPlugin-0SvpKjRJ.mjs +1 -0
- package/dist/packem_shared/log-DTdeAemA.mjs +1 -0
- package/dist/packem_shared/logStreamPlugin-CFlQAr0R.mjs +3 -0
- package/dist/packem_shared/lunoraSolutionFinder-Dl5pUZK2.mjs +1 -0
- package/dist/packem_shared/planViteRemoteBindings-BDAdqq6D.mjs +1 -0
- package/dist/packem_shared/server-close-C8zGVydc.mjs +1 -0
- package/dist/packem_shared/wranglerValidatorPlugin-CHmNBKbO.mjs +3 -0
- package/package.json +9 -8
- package/dist/packem_shared/augmentWorkerStartupError-DhsXUW8k.mjs +0 -81
- package/dist/packem_shared/buildStudioUrl-5ppCdBHa.mjs +0 -210
- package/dist/packem_shared/codegenPlugin-BAyt6iWS.mjs +0 -210
- package/dist/packem_shared/createCommandProbe-Coo6bgVz.mjs +0 -36
- package/dist/packem_shared/devVariablesPlugin-BRWbWHhq.mjs +0 -20
- package/dist/packem_shared/frameworkComposePlugin-Cja0SF6x.mjs +0 -96
- package/dist/packem_shared/logStreamPlugin-CqvZ17kd.mjs +0 -61
- package/dist/packem_shared/planViteRemoteBindings-QN5ncUS1.mjs +0 -50
- package/dist/packem_shared/reconcileWranglerCrons-PxGwfCp_.mjs +0 -29
- package/dist/packem_shared/wranglerValidatorPlugin-Cf0nLP7-.mjs +0 -63
|
@@ -1,210 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { resolve, sep, join } from 'node:path';
|
|
3
|
-
import { createCodegenProject, refreshCodegenProject, runCodegen, CodegenDiagnosticError } from '@lunora/codegen';
|
|
4
|
-
import { inferLunoraBindings, reconcileWranglerBindings } from '@lunora/config';
|
|
5
|
-
import { reconcileWranglerCrons } from './reconcileWranglerCrons-PxGwfCp_.mjs';
|
|
6
|
-
|
|
7
|
-
const DEBOUNCE_MS = 100;
|
|
8
|
-
const TSCONFIG_VARIANT_RE = /[/\\]tsconfig\..+\.json$/u;
|
|
9
|
-
const formatExportGapOverlay = (gaps) => {
|
|
10
|
-
const lines = gaps.map((gap) => ` • ${gap.kind} "${gap.exportName}" — class ${gap.className} is not exported by your worker entry.`);
|
|
11
|
-
const hints = [...new Set(gaps.map((gap) => gap.module))].map((module) => ` export * from "./lunora/_generated/${module}";`);
|
|
12
|
-
return [
|
|
13
|
-
`[lunora] ${String(gaps.length)} declared ${gaps.length === 1 ? "binding is" : "bindings are"} not exported by your worker entry — \`wrangler deploy\` will fail.`,
|
|
14
|
-
...lines,
|
|
15
|
-
"",
|
|
16
|
-
"Add to your worker entry:",
|
|
17
|
-
...hints
|
|
18
|
-
].join("\n");
|
|
19
|
-
};
|
|
20
|
-
const reconcileBindingsSafely = async (options, logger, onExportGaps) => {
|
|
21
|
-
try {
|
|
22
|
-
const inferred = await inferLunoraBindings({ projectRoot: options.projectRoot, schemaDir: options.schemaDir });
|
|
23
|
-
const reconciled = reconcileWranglerBindings(options.projectRoot, inferred);
|
|
24
|
-
if (reconciled.changed) {
|
|
25
|
-
logger.info?.(`[lunora] inferred bindings → ${reconciled.added.join(", ")} (written to ${reconciled.wranglerPath ?? "wrangler.jsonc"})`);
|
|
26
|
-
}
|
|
27
|
-
for (const warning of reconciled.warnings) {
|
|
28
|
-
logger.warn(`[lunora] ${warning}`);
|
|
29
|
-
}
|
|
30
|
-
if (reconciled.exportGaps.length > 0) {
|
|
31
|
-
onExportGaps?.(reconciled.exportGaps);
|
|
32
|
-
}
|
|
33
|
-
} catch (error) {
|
|
34
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
35
|
-
logger.warn(`[lunora] binding inference skipped: ${message}`);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
const runCodegenSafely = (options, logger, overlay, project) => {
|
|
39
|
-
const schemaPath = join(options.projectRoot, options.schemaDir, "schema.ts");
|
|
40
|
-
if (!existsSync(schemaPath)) {
|
|
41
|
-
logger.warn(`[lunora] schema.ts not found at ${schemaPath} — codegen skipped`);
|
|
42
|
-
return void 0;
|
|
43
|
-
}
|
|
44
|
-
try {
|
|
45
|
-
const result = runCodegen({ apiSpec: options.apiSpec, lunoraDirectory: options.schemaDir, project, projectRoot: options.projectRoot });
|
|
46
|
-
try {
|
|
47
|
-
const reconciled = reconcileWranglerCrons(options.projectRoot, result.cronTriggers);
|
|
48
|
-
if (reconciled.changed) {
|
|
49
|
-
logger.info?.(`[lunora] synced ${result.cronTriggers.length.toFixed(0)} cron trigger(s) into ${reconciled.wranglerPath ?? "wrangler.jsonc"}`);
|
|
50
|
-
}
|
|
51
|
-
} catch (cronError) {
|
|
52
|
-
const message = cronError instanceof Error ? cronError.message : String(cronError);
|
|
53
|
-
logger.warn(`[lunora] cron trigger sync skipped: ${message}`);
|
|
54
|
-
}
|
|
55
|
-
for (const advisory of result.advisories) {
|
|
56
|
-
logger.warn(`[lunora] schema advisory [${advisory.level}] ${advisory.name}: ${advisory.detail} — ${advisory.remediation}`);
|
|
57
|
-
}
|
|
58
|
-
return resolve(result.outputDirectory);
|
|
59
|
-
} catch (error) {
|
|
60
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
61
|
-
logger.error(`[lunora] codegen failed: ${message}`);
|
|
62
|
-
overlay?.onError(error, message);
|
|
63
|
-
return void 0;
|
|
64
|
-
}
|
|
65
|
-
};
|
|
66
|
-
const codegenPlugin = (options) => {
|
|
67
|
-
const absoluteSchemaDirectory = resolve(options.projectRoot, options.schemaDir);
|
|
68
|
-
let absoluteGeneratedDirectory = resolve(options.projectRoot, options.generatedDir);
|
|
69
|
-
let debounceTimer;
|
|
70
|
-
let devServer;
|
|
71
|
-
return {
|
|
72
|
-
async buildStart() {
|
|
73
|
-
const logger = {
|
|
74
|
-
error: (message) => {
|
|
75
|
-
console.error(message);
|
|
76
|
-
},
|
|
77
|
-
info: (message) => {
|
|
78
|
-
console.info(message);
|
|
79
|
-
},
|
|
80
|
-
warn: (message) => {
|
|
81
|
-
console.warn(message);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
const outputDirectory = runCodegenSafely(options, logger);
|
|
85
|
-
if (outputDirectory !== void 0) {
|
|
86
|
-
absoluteGeneratedDirectory = outputDirectory;
|
|
87
|
-
}
|
|
88
|
-
await reconcileBindingsSafely(options, logger, (gaps) => {
|
|
89
|
-
devServer?.hot.send({
|
|
90
|
-
err: { loc: void 0, message: formatExportGapOverlay(gaps), stack: "" },
|
|
91
|
-
type: "error"
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
},
|
|
95
|
-
configureServer(server) {
|
|
96
|
-
devServer = server;
|
|
97
|
-
server.watcher.add(absoluteSchemaDirectory);
|
|
98
|
-
const serverLogger = {
|
|
99
|
-
error: (message) => {
|
|
100
|
-
server.config.logger.error(message);
|
|
101
|
-
},
|
|
102
|
-
info: (message) => {
|
|
103
|
-
server.config.logger.info(message);
|
|
104
|
-
},
|
|
105
|
-
warn: (message) => {
|
|
106
|
-
server.config.logger.warn(message);
|
|
107
|
-
}
|
|
108
|
-
};
|
|
109
|
-
const overlay = {
|
|
110
|
-
onError(error, message) {
|
|
111
|
-
const loc = error instanceof CodegenDiagnosticError ? { column: error.column, file: error.file, line: error.line } : void 0;
|
|
112
|
-
const overlayMessage = loc === void 0 ? `[lunora] codegen failed: ${message}
|
|
113
|
-
(see terminal for full stack trace and file location)` : `[lunora] codegen failed: ${message}`;
|
|
114
|
-
devServer?.hot.send({
|
|
115
|
-
err: {
|
|
116
|
-
loc,
|
|
117
|
-
message: overlayMessage,
|
|
118
|
-
stack: error instanceof Error ? error.stack ?? "" : ""
|
|
119
|
-
},
|
|
120
|
-
type: "error"
|
|
121
|
-
});
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
const isInside = (path, directory) => path === directory || path.startsWith(directory + sep);
|
|
125
|
-
let closed = false;
|
|
126
|
-
let cachedProject;
|
|
127
|
-
const invalidateGenerated = () => {
|
|
128
|
-
const environments = server.environments;
|
|
129
|
-
const graphs = [];
|
|
130
|
-
if (environments !== void 0) {
|
|
131
|
-
for (const environment of Object.values(environments)) {
|
|
132
|
-
if (environment.moduleGraph !== void 0) {
|
|
133
|
-
graphs.push(environment.moduleGraph);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
if (graphs.length === 0) {
|
|
138
|
-
graphs.push(server.moduleGraph);
|
|
139
|
-
}
|
|
140
|
-
for (const graph of graphs) {
|
|
141
|
-
for (const moduleEntry of graph.idToModuleMap.values()) {
|
|
142
|
-
if (moduleEntry.id && isInside(moduleEntry.id, absoluteGeneratedDirectory)) {
|
|
143
|
-
graph.invalidateModule(moduleEntry);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
};
|
|
148
|
-
const onChange = (file) => {
|
|
149
|
-
const normalized = resolve(file);
|
|
150
|
-
if (!isInside(normalized, absoluteSchemaDirectory)) {
|
|
151
|
-
return;
|
|
152
|
-
}
|
|
153
|
-
if (isInside(normalized, absoluteGeneratedDirectory)) {
|
|
154
|
-
return;
|
|
155
|
-
}
|
|
156
|
-
if (normalized.endsWith(`${sep}tsconfig.json`) || TSCONFIG_VARIANT_RE.test(normalized)) {
|
|
157
|
-
cachedProject = void 0;
|
|
158
|
-
return;
|
|
159
|
-
}
|
|
160
|
-
if (!normalized.endsWith(".ts")) {
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
163
|
-
if (normalized.includes(`${sep}__tests__${sep}`) || normalized.endsWith(".test.ts") || normalized.endsWith(".spec.ts")) {
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
if (debounceTimer) {
|
|
167
|
-
clearTimeout(debounceTimer);
|
|
168
|
-
}
|
|
169
|
-
debounceTimer = setTimeout(() => {
|
|
170
|
-
debounceTimer = void 0;
|
|
171
|
-
if (closed) {
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
if (cachedProject === void 0) {
|
|
175
|
-
cachedProject = createCodegenProject(absoluteSchemaDirectory);
|
|
176
|
-
} else {
|
|
177
|
-
refreshCodegenProject(cachedProject, absoluteSchemaDirectory);
|
|
178
|
-
}
|
|
179
|
-
const outputDirectory = runCodegenSafely(options, serverLogger, overlay, cachedProject);
|
|
180
|
-
if (outputDirectory === void 0) {
|
|
181
|
-
cachedProject = void 0;
|
|
182
|
-
return;
|
|
183
|
-
}
|
|
184
|
-
absoluteGeneratedDirectory = outputDirectory;
|
|
185
|
-
invalidateGenerated();
|
|
186
|
-
server.hot.send({ type: "full-reload" });
|
|
187
|
-
}, DEBOUNCE_MS);
|
|
188
|
-
};
|
|
189
|
-
server.watcher.on("add", onChange);
|
|
190
|
-
server.watcher.on("change", onChange);
|
|
191
|
-
server.watcher.on("unlink", onChange);
|
|
192
|
-
return () => {
|
|
193
|
-
server.httpServer?.once("close", () => {
|
|
194
|
-
closed = true;
|
|
195
|
-
cachedProject = void 0;
|
|
196
|
-
if (debounceTimer) {
|
|
197
|
-
clearTimeout(debounceTimer);
|
|
198
|
-
debounceTimer = void 0;
|
|
199
|
-
}
|
|
200
|
-
server.watcher.off("add", onChange);
|
|
201
|
-
server.watcher.off("change", onChange);
|
|
202
|
-
server.watcher.off("unlink", onChange);
|
|
203
|
-
});
|
|
204
|
-
};
|
|
205
|
-
},
|
|
206
|
-
name: "lunora:codegen"
|
|
207
|
-
};
|
|
208
|
-
};
|
|
209
|
-
|
|
210
|
-
export { codegenPlugin as default };
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
const DEV_WORKER_ENV_VAR = "WORKER_ENV";
|
|
2
|
-
const DEV_WORKER_ENV_VALUE = "development";
|
|
3
|
-
const withDevWorkerEnv = (options, isServe) => {
|
|
4
|
-
const userConfig = options.config;
|
|
5
|
-
return {
|
|
6
|
-
...options,
|
|
7
|
-
config: (workerConfig) => {
|
|
8
|
-
if (typeof userConfig === "function") {
|
|
9
|
-
const partial = userConfig(workerConfig);
|
|
10
|
-
if (partial) {
|
|
11
|
-
Object.assign(workerConfig, partial);
|
|
12
|
-
}
|
|
13
|
-
} else if (userConfig) {
|
|
14
|
-
Object.assign(workerConfig, userConfig);
|
|
15
|
-
}
|
|
16
|
-
if (isServe()) {
|
|
17
|
-
workerConfig.vars = { [DEV_WORKER_ENV_VAR]: DEV_WORKER_ENV_VALUE, ...workerConfig.vars };
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
};
|
|
21
|
-
};
|
|
22
|
-
const createCommandProbe = () => {
|
|
23
|
-
let command;
|
|
24
|
-
return {
|
|
25
|
-
isServe: () => command === "serve",
|
|
26
|
-
plugin: {
|
|
27
|
-
config(_userConfig, env) {
|
|
28
|
-
command = env.command;
|
|
29
|
-
},
|
|
30
|
-
enforce: "pre",
|
|
31
|
-
name: "lunora:command-probe"
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
export { DEV_WORKER_ENV_VALUE, DEV_WORKER_ENV_VAR, createCommandProbe, withDevWorkerEnv };
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { ensureDevVariables, createConfirm } from '@lunora/config';
|
|
2
|
-
|
|
3
|
-
const devVariablesPlugin = (options) => {
|
|
4
|
-
return {
|
|
5
|
-
apply: "serve",
|
|
6
|
-
async configResolved() {
|
|
7
|
-
await ensureDevVariables({
|
|
8
|
-
confirm: createConfirm("[lunora] "),
|
|
9
|
-
cwd: options.projectRoot,
|
|
10
|
-
info: (message) => {
|
|
11
|
-
console.info(`[lunora] ${message}`);
|
|
12
|
-
}
|
|
13
|
-
});
|
|
14
|
-
},
|
|
15
|
-
enforce: "pre",
|
|
16
|
-
name: "lunora:dev-vars"
|
|
17
|
-
};
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
export { devVariablesPlugin as default };
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { existsSync } from 'node:fs';
|
|
2
|
-
import { resolve, join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
const LUNORA_WORKER_VIRTUAL_ID = "virtual:lunora/worker";
|
|
5
|
-
const RESOLVED_VIRTUAL_PREFIX = "\0";
|
|
6
|
-
const RESOLVED_LUNORA_WORKER_ID = `${RESOLVED_VIRTUAL_PREFIX}${LUNORA_WORKER_VIRTUAL_ID}`;
|
|
7
|
-
const TRAILING_SLASH = /\/$/;
|
|
8
|
-
const CLASS_A_WIRING = {
|
|
9
|
-
"react-router": {
|
|
10
|
-
// `@react-router/dev` provides `virtual:react-router/server-build`; the
|
|
11
|
-
// runtime helper turns it into a `(request) => Promise<Response>`, which
|
|
12
|
-
// is exactly the `httpRouter.fetch` contract. Needs a named import.
|
|
13
|
-
handler: '{ fetch: (request) => createRequestHandler(() => import("virtual:react-router/server-build"), import.meta.env.MODE)(request) }',
|
|
14
|
-
imports: 'import { createRequestHandler } from "react-router";'
|
|
15
|
-
},
|
|
16
|
-
"solid-start": {
|
|
17
|
-
// SolidStart's `cloudflare-module` preset default-exports a fetch
|
|
18
|
-
// handler — structurally an `HttpRouterLike` already.
|
|
19
|
-
handler: "ssrModule.default",
|
|
20
|
-
imports: 'import * as ssrModule from "@solidjs/start/server-handler";'
|
|
21
|
-
},
|
|
22
|
-
"tanstack-start": {
|
|
23
|
-
// TanStack Start's server entry default-exports a `{ fetch }` handler.
|
|
24
|
-
handler: "ssrModule.default",
|
|
25
|
-
imports: 'import * as ssrModule from "@tanstack/react-start/server-entry";'
|
|
26
|
-
},
|
|
27
|
-
"tanstack-start-solid": {
|
|
28
|
-
// TanStack Start (Solid)'s server entry default-exports a `{ fetch }`
|
|
29
|
-
// handler — same shape as the React variant, different package.
|
|
30
|
-
handler: "ssrModule.default",
|
|
31
|
-
imports: 'import * as ssrModule from "@tanstack/solid-start/server-entry";'
|
|
32
|
-
}
|
|
33
|
-
};
|
|
34
|
-
const isAutoComposable = (context) => {
|
|
35
|
-
const detected = context.framework;
|
|
36
|
-
return detected?.class === "A" && CLASS_A_WIRING[detected.framework] !== void 0;
|
|
37
|
-
};
|
|
38
|
-
const isWorkerVirtualActive = (context) => isAutoComposable(context);
|
|
39
|
-
const buildWorkerEntrySource = (framework, generatedImportBase, hasContainers = false) => {
|
|
40
|
-
const wiring = CLASS_A_WIRING[framework];
|
|
41
|
-
if (wiring === void 0) {
|
|
42
|
-
throw new Error(`[lunora] no class-A worker wiring for framework "${framework}"`);
|
|
43
|
-
}
|
|
44
|
-
const containersReexport = hasContainers ? `
|
|
45
|
-
export * from "${generatedImportBase}/containers";
|
|
46
|
-
` : "";
|
|
47
|
-
return `// Generated by @lunora/vite — class-A worker composition (PLAN4 M2).
|
|
48
|
-
// Do not edit: emitted from the detected framework (${framework}). Point your
|
|
49
|
-
// wrangler \`main\` here (or re-export it) instead of hand-wiring createWorker.
|
|
50
|
-
import { composeWorker } from "@lunora/runtime";
|
|
51
|
-
${wiring.imports}
|
|
52
|
-
import { LUNORA_FUNCTIONS } from "${generatedImportBase}/functions";
|
|
53
|
-
import { openApiSpec } from "${generatedImportBase}/openapi";
|
|
54
|
-
import { createShardDO } from "${generatedImportBase}/shard";
|
|
55
|
-
|
|
56
|
-
export const ShardDO = createShardDO();
|
|
57
|
-
${containersReexport}
|
|
58
|
-
|
|
59
|
-
let worker;
|
|
60
|
-
|
|
61
|
-
export default {
|
|
62
|
-
async fetch(request, env, context) {
|
|
63
|
-
worker ??= composeWorker({
|
|
64
|
-
functions: LUNORA_FUNCTIONS,
|
|
65
|
-
httpRouter: ${wiring.handler},
|
|
66
|
-
openApiSpec,
|
|
67
|
-
routes: {},
|
|
68
|
-
shardDO: env.SHARD,
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
return worker.fetch(request, env, context);
|
|
72
|
-
},
|
|
73
|
-
};
|
|
74
|
-
`;
|
|
75
|
-
};
|
|
76
|
-
const frameworkComposePlugin = (options, context) => {
|
|
77
|
-
const generatedImportBase = resolve(options.projectRoot, options.generatedDir.replace(TRAILING_SLASH, ""));
|
|
78
|
-
return {
|
|
79
|
-
load(id) {
|
|
80
|
-
if (id === RESOLVED_LUNORA_WORKER_ID && isWorkerVirtualActive(context) && context.framework !== void 0) {
|
|
81
|
-
const hasContainers = existsSync(join(generatedImportBase, "containers.ts"));
|
|
82
|
-
return buildWorkerEntrySource(context.framework.framework, generatedImportBase, hasContainers);
|
|
83
|
-
}
|
|
84
|
-
return void 0;
|
|
85
|
-
},
|
|
86
|
-
name: "lunora:framework-compose",
|
|
87
|
-
resolveId(id) {
|
|
88
|
-
if (id === LUNORA_WORKER_VIRTUAL_ID && isWorkerVirtualActive(context)) {
|
|
89
|
-
return RESOLVED_LUNORA_WORKER_ID;
|
|
90
|
-
}
|
|
91
|
-
return void 0;
|
|
92
|
-
}
|
|
93
|
-
};
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
export { CLASS_A_WIRING, LUNORA_WORKER_VIRTUAL_ID, RESOLVED_LUNORA_WORKER_ID, buildWorkerEntrySource, frameworkComposePlugin, isAutoComposable };
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { LUNORA_EVENT_SOURCE, formatLunoraEvent } from '@lunora/config';
|
|
2
|
-
|
|
3
|
-
const LUNORA_MARKER = `"source":"${LUNORA_EVENT_SOURCE}"`;
|
|
4
|
-
const ANSI = {
|
|
5
|
-
error: "\x1B[31m",
|
|
6
|
-
info: "\x1B[36m",
|
|
7
|
-
reset: "\x1B[0m",
|
|
8
|
-
warn: "\x1B[33m"
|
|
9
|
-
};
|
|
10
|
-
const decorate = (text, level, colour) => colour ? `${ANSI[level]}[lunora]${ANSI.reset} ${text}` : `[lunora] ${text}`;
|
|
11
|
-
const chunkToText = (chunk) => {
|
|
12
|
-
if (typeof chunk === "string") {
|
|
13
|
-
return chunk;
|
|
14
|
-
}
|
|
15
|
-
return Buffer.isBuffer(chunk) ? chunk.toString("utf8") : void 0;
|
|
16
|
-
};
|
|
17
|
-
const rewriteChunk = (text, colour) => text.split("\n").map((segment) => {
|
|
18
|
-
const formatted = formatLunoraEvent(segment);
|
|
19
|
-
return formatted ? decorate(formatted.text, formatted.level, colour) : segment;
|
|
20
|
-
}).join("\n");
|
|
21
|
-
const wrapWrite = (original, colour) => (...args) => {
|
|
22
|
-
try {
|
|
23
|
-
const text = chunkToText(args[0]);
|
|
24
|
-
if (text?.includes(LUNORA_MARKER)) {
|
|
25
|
-
return original(rewriteChunk(text, colour), ...args.slice(1));
|
|
26
|
-
}
|
|
27
|
-
} catch {
|
|
28
|
-
}
|
|
29
|
-
return original(...args);
|
|
30
|
-
};
|
|
31
|
-
const patchStream = (stream) => {
|
|
32
|
-
const original = stream.write.bind(stream);
|
|
33
|
-
stream.write = wrapWrite(original, stream.isTTY === true);
|
|
34
|
-
return () => {
|
|
35
|
-
stream.write = original;
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
const logStreamPlugin = () => {
|
|
39
|
-
let restore;
|
|
40
|
-
return {
|
|
41
|
-
apply: "serve",
|
|
42
|
-
configureServer(server) {
|
|
43
|
-
if (restore) {
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const restoreStdout = patchStream(process.stdout);
|
|
47
|
-
const restoreStderr = patchStream(process.stderr);
|
|
48
|
-
restore = () => {
|
|
49
|
-
restoreStdout();
|
|
50
|
-
restoreStderr();
|
|
51
|
-
restore = void 0;
|
|
52
|
-
};
|
|
53
|
-
server.httpServer?.once("close", () => {
|
|
54
|
-
restore?.();
|
|
55
|
-
});
|
|
56
|
-
},
|
|
57
|
-
name: "lunora:log-stream"
|
|
58
|
-
};
|
|
59
|
-
};
|
|
60
|
-
|
|
61
|
-
export { logStreamPlugin as default };
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
import { resolveRemoteEnabled, readProjectRemotePreference, materializeRemoteWranglerConfig } from '@lunora/config';
|
|
2
|
-
|
|
3
|
-
const noopCleanup = () => {
|
|
4
|
-
};
|
|
5
|
-
const planViteRemoteBindings = (options) => {
|
|
6
|
-
const readPreference = options.readPreference ?? readProjectRemotePreference;
|
|
7
|
-
const enabled = resolveRemoteEnabled({
|
|
8
|
-
configPreference: readPreference(options.projectRoot),
|
|
9
|
-
envValue: options.remoteEnv ?? process.env["LUNORA_REMOTE"]
|
|
10
|
-
});
|
|
11
|
-
if (!enabled) {
|
|
12
|
-
return { cleanup: noopCleanup, enabled: false };
|
|
13
|
-
}
|
|
14
|
-
const materialize = options.materialize ?? materializeRemoteWranglerConfig;
|
|
15
|
-
const result = materialize({ enabled: true, projectRoot: options.projectRoot });
|
|
16
|
-
return {
|
|
17
|
-
// The materializer always returns an idempotent, never-throwing `cleanup`.
|
|
18
|
-
cleanup: result.cleanup,
|
|
19
|
-
configPath: result.configPath,
|
|
20
|
-
enabled: true,
|
|
21
|
-
reason: result.reason
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
const withRemoteBindings = (options, isServe, plan) => {
|
|
25
|
-
if (!plan.enabled || plan.configPath === void 0) {
|
|
26
|
-
return options;
|
|
27
|
-
}
|
|
28
|
-
const existing = options;
|
|
29
|
-
if (typeof existing.configPath === "string") {
|
|
30
|
-
return options;
|
|
31
|
-
}
|
|
32
|
-
if (!isServe()) {
|
|
33
|
-
return options;
|
|
34
|
-
}
|
|
35
|
-
return { ...options, configPath: plan.configPath };
|
|
36
|
-
};
|
|
37
|
-
const remoteBindingsCleanupPlugin = (cleanup) => {
|
|
38
|
-
return {
|
|
39
|
-
buildEnd() {
|
|
40
|
-
cleanup();
|
|
41
|
-
},
|
|
42
|
-
closeBundle() {
|
|
43
|
-
cleanup();
|
|
44
|
-
},
|
|
45
|
-
enforce: "pre",
|
|
46
|
-
name: "lunora:remote-bindings-cleanup"
|
|
47
|
-
};
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
export { planViteRemoteBindings, remoteBindingsCleanupPlugin, withRemoteBindings };
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { writeFileSync } from 'node:fs';
|
|
2
|
-
import { findWranglerFile, readWranglerJsonc } from '@lunora/config';
|
|
3
|
-
import { modify, applyEdits } from 'jsonc-parser';
|
|
4
|
-
|
|
5
|
-
const sameTriggers = (a, b) => a.length === b.length && a.every((value, index) => value === b[index]);
|
|
6
|
-
const reconcileWranglerCrons = (projectRoot, cronTriggers) => {
|
|
7
|
-
const wranglerPath = findWranglerFile(projectRoot);
|
|
8
|
-
if (!wranglerPath) {
|
|
9
|
-
return { changed: false, reason: "wrangler.jsonc not found" };
|
|
10
|
-
}
|
|
11
|
-
const { parsed, text } = readWranglerJsonc(wranglerPath);
|
|
12
|
-
if (parsed === void 0) {
|
|
13
|
-
return { changed: false, reason: `failed to parse ${wranglerPath} as JSONC`, wranglerPath };
|
|
14
|
-
}
|
|
15
|
-
const existing = Array.isArray(parsed.triggers?.crons) ? parsed.triggers.crons.filter((value) => typeof value === "string") : [];
|
|
16
|
-
if (sameTriggers(existing, cronTriggers)) {
|
|
17
|
-
return { changed: false, reason: "triggers.crons already in sync", wranglerPath };
|
|
18
|
-
}
|
|
19
|
-
const edits = modify(text, ["triggers", "crons"], [...cronTriggers], {
|
|
20
|
-
formattingOptions: { insertSpaces: true, tabSize: 4 }
|
|
21
|
-
});
|
|
22
|
-
if (edits.length === 0) {
|
|
23
|
-
return { changed: false, reason: "no structural edit produced", wranglerPath };
|
|
24
|
-
}
|
|
25
|
-
writeFileSync(wranglerPath, applyEdits(text, edits), "utf8");
|
|
26
|
-
return { changed: true, wranglerPath };
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export { reconcileWranglerCrons };
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { spawnSync } from 'node:child_process';
|
|
2
|
-
import { validateWranglerProject, readWranglerJsonc } from '@lunora/config';
|
|
3
|
-
|
|
4
|
-
const isLocalImagePath = (image) => image.startsWith("./") || image.startsWith("../") || image.startsWith("/") || image.includes("Dockerfile");
|
|
5
|
-
const probeDocker = () => {
|
|
6
|
-
try {
|
|
7
|
-
return spawnSync("docker", ["info"], { stdio: "ignore" }).status === 0;
|
|
8
|
-
} catch {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
};
|
|
12
|
-
const warnWhenDockerMissing = (wranglerPath, dockerAvailable = probeDocker) => {
|
|
13
|
-
const { parsed } = readWranglerJsonc(wranglerPath);
|
|
14
|
-
const needsDocker = (parsed?.containers ?? []).some((entry) => typeof entry?.image === "string" && isLocalImagePath(entry.image));
|
|
15
|
-
if (!needsDocker || dockerAvailable()) {
|
|
16
|
-
return;
|
|
17
|
-
}
|
|
18
|
-
console.warn(
|
|
19
|
-
"[lunora] wrangler.jsonc declares containers built from a local Dockerfile, but no Docker-compatible engine is running. Start Docker (or Colima) before `vite dev`, or the container instances will fail to start."
|
|
20
|
-
);
|
|
21
|
-
};
|
|
22
|
-
const formatError = (wranglerPath, problems) => {
|
|
23
|
-
const lines = [
|
|
24
|
-
"[lunora] wrangler configuration is missing bindings required by your schema.",
|
|
25
|
-
` file: ${wranglerPath}`,
|
|
26
|
-
"",
|
|
27
|
-
...problems.map((problem) => ` - ${problem}`),
|
|
28
|
-
"",
|
|
29
|
-
" Update your wrangler.jsonc and restart the dev server."
|
|
30
|
-
];
|
|
31
|
-
return new Error(lines.join("\n"));
|
|
32
|
-
};
|
|
33
|
-
const wranglerValidatorPlugin = (options) => {
|
|
34
|
-
return {
|
|
35
|
-
configResolved() {
|
|
36
|
-
const result = validateWranglerProject({
|
|
37
|
-
projectRoot: options.projectRoot,
|
|
38
|
-
schemaDir: options.schemaDir
|
|
39
|
-
});
|
|
40
|
-
if (!result.wranglerPath) {
|
|
41
|
-
throw new Error(
|
|
42
|
-
[
|
|
43
|
-
"[lunora] wrangler.jsonc not found.",
|
|
44
|
-
` searched in: ${options.projectRoot}`,
|
|
45
|
-
" create a wrangler.jsonc declaring at least the SHARD durable object binding."
|
|
46
|
-
].join("\n")
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
if (result.report.warnings.length > 0) {
|
|
50
|
-
for (const warning of result.report.warnings) {
|
|
51
|
-
console.warn(`[lunora] wrangler validator: ${warning}`);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
if (result.problems.length > 0) {
|
|
55
|
-
throw formatError(result.wranglerPath, result.problems);
|
|
56
|
-
}
|
|
57
|
-
warnWhenDockerMissing(result.wranglerPath);
|
|
58
|
-
},
|
|
59
|
-
name: "lunora:wrangler-validator"
|
|
60
|
-
};
|
|
61
|
-
};
|
|
62
|
-
|
|
63
|
-
export { warnWhenDockerMissing, wranglerValidatorPlugin };
|