@lunora/vite 1.0.0-alpha.4 → 1.0.0-alpha.40

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/index.d.mts CHANGED
@@ -14,6 +14,15 @@ type CloudflarePluginOptions = Record<string, unknown>;
14
14
  */
15
15
  type OverlayPluginOptions = NonNullable<Parameters<typeof errorOverlayPlugin>[0]>;
16
16
  interface LunoraPluginOptions {
17
+ /**
18
+ * Allow a client-named NON-default shard / cross-shard fan-out WITHOUT an
19
+ * `authorizeShard`/`authorizeFanOut` callback. The auto-composed class-A worker
20
+ * (`virtual:lunora/worker`) default-denies such access (403 `FORBIDDEN_SHARD`);
21
+ * set this `true` to opt into open access — only safe when every table is
22
+ * protected by per-row RLS. A production sharded app should configure
23
+ * `authorizeShard` instead (via a hand-written class-B worker). Defaults to `false`.
24
+ */
25
+ allowUnauthenticatedShardAccess?: boolean;
17
26
  /**
18
27
  * Which machine-readable API spec(s) codegen emits into `_generated/`.
19
28
  * `"openapi"` (default) writes `openapi.json` (OpenAPI 3.1; RPC + REST),
@@ -43,6 +52,7 @@ interface LunoraPluginOptions {
43
52
  }
44
53
  /** Resolved options after merging defaults. */
45
54
  interface ResolvedLunoraPluginOptions {
55
+ allowUnauthenticatedShardAccess: boolean;
46
56
  apiSpec: NonNullable<CodegenOptions["apiSpec"]>;
47
57
  cloudflare: false | CloudflarePluginOptions;
48
58
  generatedDir: string;
@@ -63,6 +73,23 @@ type LunoraPlugins = Plugin[];
63
73
  * inside the lunora schema directory.
64
74
  */
65
75
  declare const codegenPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
76
+ /**
77
+ * Dev-only plugin that tails the local dev containers' own stdout/stderr in the
78
+ * Vite terminal.
79
+ *
80
+ * `@cloudflare/vite-plugin` builds and runs each declared container locally via
81
+ * Docker (image `cloudflare-dev/&lt;class>:&lt;id>`) but only forwards the *worker's*
82
+ * console — the container process's own output is otherwise invisible. This
83
+ * plugin attaches to those Docker log streams (via `@lunora/config`'s
84
+ * `streamContainerLogs`, which lazy-loads `dockerode`) and prints each line
85
+ * through Vite's logger, branded and tagged `container:&lt;name>`.
86
+ *
87
+ * A no-op when the project declares no containers (the common case): discovery
88
+ * returns an empty list, so `dockerode` is never imported and no Docker work
89
+ * starts. Set `LUNORA_CONTAINER_LOGS=0` to opt out. A missing/stopped Docker
90
+ * engine degrades to a single warning rather than breaking dev.
91
+ */
92
+ declare const containerLogsPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
66
93
  interface ReconcileResult {
67
94
  /** `true` when `wrangler.jsonc` was rewritten. */
68
95
  changed: boolean;
@@ -87,13 +114,20 @@ interface ReconcileResult {
87
114
  */
88
115
  declare const reconcileWranglerCrons: (projectRoot: string, cronTriggers: ReadonlyArray<string>) => ReconcileResult;
89
116
  /**
90
- * Dev-only Vite plugin that offers to scaffold `.dev.vars` before the worker
91
- * boots. `@cloudflare/vite-plugin` loads `.dev.vars` into the worker's `env`,
92
- * but the file is gitignored — so a fresh clone has none and the worker throws
93
- * on the first required secret (e.g. `AUTH_SECRET is required`). When a
94
- * `.dev.vars.example` exists, we prompt to generate `.dev.vars` from it with
95
- * secrets auto-filled. Identical behaviour to `lunora dev` — both call
96
- * `ensureDevVariables` from `@lunora/config`.
117
+ * Dev-only Vite plugin that prepares `.dev.vars` before the worker boots.
118
+ * `@cloudflare/vite-plugin` loads `.dev.vars` into the worker's `env`, but the
119
+ * file is gitignored — so a fresh clone has none and the worker throws on the
120
+ * first required secret (e.g. `AUTH_SECRET is required`). Two steps, both shared
121
+ * with `lunora dev` via `@lunora/config`.
122
+ *
123
+ * First, {@link ensureDevVariables}: when a `.dev.vars.example` exists, prompt
124
+ * to generate `.dev.vars` from it with secrets auto-filled. Second,
125
+ * {@link fillDevSecrets}: fill any empty/placeholder secret already in
126
+ * `.dev.vars` (a `lunora add`-scaffolded project writes secrets blank) and
127
+ * ensure `LUNORA_ADMIN_TOKEN` is present + generated — so the worker boots with
128
+ * working secrets and the Studio authenticates without its login gate. No
129
+ * prompt: it only generates locally-derivable values and never overwrites a real
130
+ * one.
97
131
  *
98
132
  * Runs in `configResolved` (awaited by Vite) so it completes before the
99
133
  * Cloudflare plugin reads the file. Non-interactive runs decline silently.
@@ -198,7 +232,7 @@ declare const isAutoComposable: (context: LunoraPluginContext) => boolean;
198
232
  * virtual module id. Absolute paths are resolved correctly in all environments
199
233
  * (Vite 8 + rolldown 1.x confirmed).
200
234
  */
201
- declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedImportBase: string, hasContainers?: boolean) => string;
235
+ declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedImportBase: string, hasContainers?: boolean, useUmbrella?: boolean, allowUnauthenticatedShardAccess?: boolean) => string;
202
236
  /**
203
237
  * Vite plugin that auto-composes a detected class-A meta-framework's SSR
204
238
  * handler with Lunora into one Cloudflare Worker (PLAN4 §2.4 / §3 class-A row).
@@ -281,6 +315,35 @@ declare const withRemoteBindings: (options: CloudflarePluginOptions, isServe: ()
281
315
  * the build/close path). Idempotent cleanup means firing on both is safe.
282
316
  */
283
317
  declare const remoteBindingsCleanupPlugin: (cleanup: () => void) => Plugin;
318
+ /**
319
+ * A `@visulima/vite-overlay` solution finder. Derived from the overlay's own
320
+ * options type so the shape can't drift from the installed package. The overlay
321
+ * runs every finder it's given (custom finders first, then its built-ins),
322
+ * sorted by `priority` descending, and shows the first non-`undefined` result.
323
+ *
324
+ * Note: this type (and {@link Solution}) is re-exported from `@lunora/vite` and
325
+ * intentionally tracks the installed `@visulima/vite-overlay` — if a future
326
+ * overlay release changes the finder contract, that surfaces here as a compile
327
+ * error rather than a silent drift.
328
+ */
329
+ type SolutionFinder = NonNullable<OverlayPluginOptions["solutionFinders"]>[number];
330
+ /** What a finder may return: a Markdown-rendered `{ header?, body }`, or `undefined` to defer. */
331
+ type Solution = NonNullable<Awaited<ReturnType<SolutionFinder["handle"]>>>;
332
+ /**
333
+ * Lunora's solution finder for the dev error overlay. A single finder that
334
+ * delegates to `@lunora/codegen`'s shared rule table (the same table the
335
+ * standalone `lunora dev` CLI prints to the terminal) and returns the first
336
+ * match — so one `priority` slot covers every Lunora rule and the overlay's
337
+ * built-in finders still run for anything we don't recognize (we return
338
+ * `undefined`).
339
+ *
340
+ * Priority is high so a Lunora-specific hint wins over the overlay's generic
341
+ * finder for the same error; a user's own finder can still outrank it with a
342
+ * higher `priority`.
343
+ */
344
+ declare const lunoraSolutionFinder: SolutionFinder;
345
+ /** The finders Lunora injects into the overlay by default. */
346
+ declare const lunoraSolutionFinders: ReadonlyArray<SolutionFinder>;
284
347
  /** Dev-server path the studio SPA is served from. */
285
348
  declare const STUDIO_PATH = "/__lunora";
286
349
  /** Static asset routes the studio document references. */
@@ -358,6 +421,17 @@ declare const withWorkerStartupHint: (plugins: ReadonlyArray<Plugin>) => Plugin[
358
421
  */
359
422
  declare const wranglerValidatorPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
360
423
  /**
424
+ * Resolve the `overlay` toggle into the overlay plugin's options — or `false` to
425
+ * skip it. Lunora's solution finders are **prepended** so they run before the
426
+ * overlay's built-ins; a user's own finders are appended and can still win per
427
+ * error via a strictly higher `priority` (equal priority keeps Lunora first,
428
+ * since the overlay sorts stably). Lunora also forwards both `error` AND `warn`
429
+ * console calls by default (the overlay's own default is `["error"]` only) so
430
+ * Lunora's branded `warn` advisories surface in the browser too — the user can
431
+ * override `forwardedConsoleMethods`.
432
+ */
433
+ declare const resolveOverlayOption: (overlay: LunoraPluginOptions["overlay"]) => false | OverlayPluginOptions;
434
+ /**
361
435
  * Lunora Vite plugin. Returns a flat array of Vite plugins that:
362
436
  *
363
437
  * 1. Run `@lunora/codegen` on startup + on schema file changes.
@@ -371,4 +445,4 @@ declare const wranglerValidatorPlugin: (options: ResolvedLunoraPluginOptions) =>
371
445
  */
372
446
  declare const lunora: (options?: LunoraPluginOptions) => LunoraPlugins;
373
447
  declare const VERSION = "0.0.0";
374
- export { CLASS_A_WIRING, type ClassAWiring, type CloudflarePluginOptions, DEV_WORKER_ENV_VALUE, DEV_WORKER_ENV_VAR, LUNORA_WORKER_VIRTUAL_ID, type LunoraPluginOptions, type LunoraPlugins, type OverlayPluginOptions, type PlanViteRemoteOptions, type ReconcileResult, type ResolvedLunoraPluginOptions, STUDIO_PATH, VERSION, type ViteRemotePlan, WORKER_STARTUP_HINT, augmentWorkerStartupError, buildStudioUrl, buildWorkerEntrySource, codegenPlugin, createCommandProbe, devVariablesPlugin, frameworkComposePlugin, isAutoComposable, isWorkerEntryEvalError, logStreamPlugin, lunora, planViteRemoteBindings, reconcileWranglerCrons, remoteBindingsCleanupPlugin, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
448
+ export { CLASS_A_WIRING, type ClassAWiring, type CloudflarePluginOptions, DEV_WORKER_ENV_VALUE, DEV_WORKER_ENV_VAR, LUNORA_WORKER_VIRTUAL_ID, type LunoraPluginOptions, type LunoraPlugins, type OverlayPluginOptions, type PlanViteRemoteOptions, type ReconcileResult, type ResolvedLunoraPluginOptions, STUDIO_PATH, type Solution, type SolutionFinder, VERSION, type ViteRemotePlan, WORKER_STARTUP_HINT, augmentWorkerStartupError, buildStudioUrl, buildWorkerEntrySource, codegenPlugin, containerLogsPlugin, createCommandProbe, devVariablesPlugin, frameworkComposePlugin, isAutoComposable, isWorkerEntryEvalError, logStreamPlugin, lunora, lunoraSolutionFinder, lunoraSolutionFinders, planViteRemoteBindings, reconcileWranglerCrons, remoteBindingsCleanupPlugin, resolveOverlayOption, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
package/dist/index.d.ts CHANGED
@@ -14,6 +14,15 @@ type CloudflarePluginOptions = Record<string, unknown>;
14
14
  */
15
15
  type OverlayPluginOptions = NonNullable<Parameters<typeof errorOverlayPlugin>[0]>;
16
16
  interface LunoraPluginOptions {
17
+ /**
18
+ * Allow a client-named NON-default shard / cross-shard fan-out WITHOUT an
19
+ * `authorizeShard`/`authorizeFanOut` callback. The auto-composed class-A worker
20
+ * (`virtual:lunora/worker`) default-denies such access (403 `FORBIDDEN_SHARD`);
21
+ * set this `true` to opt into open access — only safe when every table is
22
+ * protected by per-row RLS. A production sharded app should configure
23
+ * `authorizeShard` instead (via a hand-written class-B worker). Defaults to `false`.
24
+ */
25
+ allowUnauthenticatedShardAccess?: boolean;
17
26
  /**
18
27
  * Which machine-readable API spec(s) codegen emits into `_generated/`.
19
28
  * `"openapi"` (default) writes `openapi.json` (OpenAPI 3.1; RPC + REST),
@@ -43,6 +52,7 @@ interface LunoraPluginOptions {
43
52
  }
44
53
  /** Resolved options after merging defaults. */
45
54
  interface ResolvedLunoraPluginOptions {
55
+ allowUnauthenticatedShardAccess: boolean;
46
56
  apiSpec: NonNullable<CodegenOptions["apiSpec"]>;
47
57
  cloudflare: false | CloudflarePluginOptions;
48
58
  generatedDir: string;
@@ -63,6 +73,23 @@ type LunoraPlugins = Plugin[];
63
73
  * inside the lunora schema directory.
64
74
  */
65
75
  declare const codegenPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
76
+ /**
77
+ * Dev-only plugin that tails the local dev containers' own stdout/stderr in the
78
+ * Vite terminal.
79
+ *
80
+ * `@cloudflare/vite-plugin` builds and runs each declared container locally via
81
+ * Docker (image `cloudflare-dev/&lt;class>:&lt;id>`) but only forwards the *worker's*
82
+ * console — the container process's own output is otherwise invisible. This
83
+ * plugin attaches to those Docker log streams (via `@lunora/config`'s
84
+ * `streamContainerLogs`, which lazy-loads `dockerode`) and prints each line
85
+ * through Vite's logger, branded and tagged `container:&lt;name>`.
86
+ *
87
+ * A no-op when the project declares no containers (the common case): discovery
88
+ * returns an empty list, so `dockerode` is never imported and no Docker work
89
+ * starts. Set `LUNORA_CONTAINER_LOGS=0` to opt out. A missing/stopped Docker
90
+ * engine degrades to a single warning rather than breaking dev.
91
+ */
92
+ declare const containerLogsPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
66
93
  interface ReconcileResult {
67
94
  /** `true` when `wrangler.jsonc` was rewritten. */
68
95
  changed: boolean;
@@ -87,13 +114,20 @@ interface ReconcileResult {
87
114
  */
88
115
  declare const reconcileWranglerCrons: (projectRoot: string, cronTriggers: ReadonlyArray<string>) => ReconcileResult;
89
116
  /**
90
- * Dev-only Vite plugin that offers to scaffold `.dev.vars` before the worker
91
- * boots. `@cloudflare/vite-plugin` loads `.dev.vars` into the worker's `env`,
92
- * but the file is gitignored — so a fresh clone has none and the worker throws
93
- * on the first required secret (e.g. `AUTH_SECRET is required`). When a
94
- * `.dev.vars.example` exists, we prompt to generate `.dev.vars` from it with
95
- * secrets auto-filled. Identical behaviour to `lunora dev` — both call
96
- * `ensureDevVariables` from `@lunora/config`.
117
+ * Dev-only Vite plugin that prepares `.dev.vars` before the worker boots.
118
+ * `@cloudflare/vite-plugin` loads `.dev.vars` into the worker's `env`, but the
119
+ * file is gitignored — so a fresh clone has none and the worker throws on the
120
+ * first required secret (e.g. `AUTH_SECRET is required`). Two steps, both shared
121
+ * with `lunora dev` via `@lunora/config`.
122
+ *
123
+ * First, {@link ensureDevVariables}: when a `.dev.vars.example` exists, prompt
124
+ * to generate `.dev.vars` from it with secrets auto-filled. Second,
125
+ * {@link fillDevSecrets}: fill any empty/placeholder secret already in
126
+ * `.dev.vars` (a `lunora add`-scaffolded project writes secrets blank) and
127
+ * ensure `LUNORA_ADMIN_TOKEN` is present + generated — so the worker boots with
128
+ * working secrets and the Studio authenticates without its login gate. No
129
+ * prompt: it only generates locally-derivable values and never overwrites a real
130
+ * one.
97
131
  *
98
132
  * Runs in `configResolved` (awaited by Vite) so it completes before the
99
133
  * Cloudflare plugin reads the file. Non-interactive runs decline silently.
@@ -198,7 +232,7 @@ declare const isAutoComposable: (context: LunoraPluginContext) => boolean;
198
232
  * virtual module id. Absolute paths are resolved correctly in all environments
199
233
  * (Vite 8 + rolldown 1.x confirmed).
200
234
  */
201
- declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedImportBase: string, hasContainers?: boolean) => string;
235
+ declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedImportBase: string, hasContainers?: boolean, useUmbrella?: boolean, allowUnauthenticatedShardAccess?: boolean) => string;
202
236
  /**
203
237
  * Vite plugin that auto-composes a detected class-A meta-framework's SSR
204
238
  * handler with Lunora into one Cloudflare Worker (PLAN4 §2.4 / §3 class-A row).
@@ -281,6 +315,35 @@ declare const withRemoteBindings: (options: CloudflarePluginOptions, isServe: ()
281
315
  * the build/close path). Idempotent cleanup means firing on both is safe.
282
316
  */
283
317
  declare const remoteBindingsCleanupPlugin: (cleanup: () => void) => Plugin;
318
+ /**
319
+ * A `@visulima/vite-overlay` solution finder. Derived from the overlay's own
320
+ * options type so the shape can't drift from the installed package. The overlay
321
+ * runs every finder it's given (custom finders first, then its built-ins),
322
+ * sorted by `priority` descending, and shows the first non-`undefined` result.
323
+ *
324
+ * Note: this type (and {@link Solution}) is re-exported from `@lunora/vite` and
325
+ * intentionally tracks the installed `@visulima/vite-overlay` — if a future
326
+ * overlay release changes the finder contract, that surfaces here as a compile
327
+ * error rather than a silent drift.
328
+ */
329
+ type SolutionFinder = NonNullable<OverlayPluginOptions["solutionFinders"]>[number];
330
+ /** What a finder may return: a Markdown-rendered `{ header?, body }`, or `undefined` to defer. */
331
+ type Solution = NonNullable<Awaited<ReturnType<SolutionFinder["handle"]>>>;
332
+ /**
333
+ * Lunora's solution finder for the dev error overlay. A single finder that
334
+ * delegates to `@lunora/codegen`'s shared rule table (the same table the
335
+ * standalone `lunora dev` CLI prints to the terminal) and returns the first
336
+ * match — so one `priority` slot covers every Lunora rule and the overlay's
337
+ * built-in finders still run for anything we don't recognize (we return
338
+ * `undefined`).
339
+ *
340
+ * Priority is high so a Lunora-specific hint wins over the overlay's generic
341
+ * finder for the same error; a user's own finder can still outrank it with a
342
+ * higher `priority`.
343
+ */
344
+ declare const lunoraSolutionFinder: SolutionFinder;
345
+ /** The finders Lunora injects into the overlay by default. */
346
+ declare const lunoraSolutionFinders: ReadonlyArray<SolutionFinder>;
284
347
  /** Dev-server path the studio SPA is served from. */
285
348
  declare const STUDIO_PATH = "/__lunora";
286
349
  /** Static asset routes the studio document references. */
@@ -358,6 +421,17 @@ declare const withWorkerStartupHint: (plugins: ReadonlyArray<Plugin>) => Plugin[
358
421
  */
359
422
  declare const wranglerValidatorPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
360
423
  /**
424
+ * Resolve the `overlay` toggle into the overlay plugin's options — or `false` to
425
+ * skip it. Lunora's solution finders are **prepended** so they run before the
426
+ * overlay's built-ins; a user's own finders are appended and can still win per
427
+ * error via a strictly higher `priority` (equal priority keeps Lunora first,
428
+ * since the overlay sorts stably). Lunora also forwards both `error` AND `warn`
429
+ * console calls by default (the overlay's own default is `["error"]` only) so
430
+ * Lunora's branded `warn` advisories surface in the browser too — the user can
431
+ * override `forwardedConsoleMethods`.
432
+ */
433
+ declare const resolveOverlayOption: (overlay: LunoraPluginOptions["overlay"]) => false | OverlayPluginOptions;
434
+ /**
361
435
  * Lunora Vite plugin. Returns a flat array of Vite plugins that:
362
436
  *
363
437
  * 1. Run `@lunora/codegen` on startup + on schema file changes.
@@ -371,4 +445,4 @@ declare const wranglerValidatorPlugin: (options: ResolvedLunoraPluginOptions) =>
371
445
  */
372
446
  declare const lunora: (options?: LunoraPluginOptions) => LunoraPlugins;
373
447
  declare const VERSION = "0.0.0";
374
- export { CLASS_A_WIRING, type ClassAWiring, type CloudflarePluginOptions, DEV_WORKER_ENV_VALUE, DEV_WORKER_ENV_VAR, LUNORA_WORKER_VIRTUAL_ID, type LunoraPluginOptions, type LunoraPlugins, type OverlayPluginOptions, type PlanViteRemoteOptions, type ReconcileResult, type ResolvedLunoraPluginOptions, STUDIO_PATH, VERSION, type ViteRemotePlan, WORKER_STARTUP_HINT, augmentWorkerStartupError, buildStudioUrl, buildWorkerEntrySource, codegenPlugin, createCommandProbe, devVariablesPlugin, frameworkComposePlugin, isAutoComposable, isWorkerEntryEvalError, logStreamPlugin, lunora, planViteRemoteBindings, reconcileWranglerCrons, remoteBindingsCleanupPlugin, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
448
+ export { CLASS_A_WIRING, type ClassAWiring, type CloudflarePluginOptions, DEV_WORKER_ENV_VALUE, DEV_WORKER_ENV_VAR, LUNORA_WORKER_VIRTUAL_ID, type LunoraPluginOptions, type LunoraPlugins, type OverlayPluginOptions, type PlanViteRemoteOptions, type ReconcileResult, type ResolvedLunoraPluginOptions, STUDIO_PATH, type Solution, type SolutionFinder, VERSION, type ViteRemotePlan, WORKER_STARTUP_HINT, augmentWorkerStartupError, buildStudioUrl, buildWorkerEntrySource, codegenPlugin, containerLogsPlugin, createCommandProbe, devVariablesPlugin, frameworkComposePlugin, isAutoComposable, isWorkerEntryEvalError, logStreamPlugin, lunora, lunoraSolutionFinder, lunoraSolutionFinders, planViteRemoteBindings, reconcileWranglerCrons, remoteBindingsCleanupPlugin, resolveOverlayOption, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
package/dist/index.mjs CHANGED
@@ -2,20 +2,23 @@ import { cloudflare } from '@cloudflare/vite-plugin';
2
2
  import errorOverlayPlugin from '@visulima/vite-overlay';
3
3
  import { detectAgentRules, claimAgentRulesHint, AGENT_RULES_HINT, detectFramework } from '@lunora/config';
4
4
  export { detectFramework } from '@lunora/config';
5
- import { l as lunoraLine } from './packem_shared/log-Bgkv6QhO.mjs';
6
- import codegenPlugin from './packem_shared/codegenPlugin-CHzvXqK6.mjs';
7
- import devVariablesPlugin from './packem_shared/devVariablesPlugin-DbDf7tmi.mjs';
5
+ import { l as lunoraLine } from './packem_shared/log-BjO9EWah.mjs';
6
+ import codegenPlugin from './packem_shared/codegenPlugin-B_ayaObq.mjs';
7
+ import containerLogsPlugin from './packem_shared/containerLogsPlugin-DMssU3wb.mjs';
8
+ import devVariablesPlugin from './packem_shared/devVariablesPlugin-CDNSnvOP.mjs';
8
9
  import { createCommandProbe, withDevWorkerEnv } from './packem_shared/DEV_WORKER_ENV_VALUE-Coo6bgVz.mjs';
9
10
  export { DEV_WORKER_ENV_VALUE, DEV_WORKER_ENV_VAR } from './packem_shared/DEV_WORKER_ENV_VALUE-Coo6bgVz.mjs';
10
- import { frameworkComposePlugin } from './packem_shared/CLASS_A_WIRING-Cja0SF6x.mjs';
11
- export { CLASS_A_WIRING, LUNORA_WORKER_VIRTUAL_ID, buildWorkerEntrySource, isAutoComposable } from './packem_shared/CLASS_A_WIRING-Cja0SF6x.mjs';
11
+ import { frameworkComposePlugin } from './packem_shared/CLASS_A_WIRING-DWYL4g1x.mjs';
12
+ export { CLASS_A_WIRING, LUNORA_WORKER_VIRTUAL_ID, buildWorkerEntrySource, isAutoComposable } from './packem_shared/CLASS_A_WIRING-DWYL4g1x.mjs';
12
13
  import logStreamPlugin from './packem_shared/logStreamPlugin-CqvZ17kd.mjs';
13
14
  import { planViteRemoteBindings, remoteBindingsCleanupPlugin, withRemoteBindings } from './packem_shared/planViteRemoteBindings-QN5ncUS1.mjs';
14
- import { studioPlugin } from './packem_shared/STUDIO_PATH-5ppCdBHa.mjs';
15
- export { STUDIO_PATH, buildStudioUrl } from './packem_shared/STUDIO_PATH-5ppCdBHa.mjs';
15
+ import { lunoraSolutionFinders } from './packem_shared/lunoraSolutionFinder-BKEAiUJP.mjs';
16
+ export { lunoraSolutionFinder } from './packem_shared/lunoraSolutionFinder-BKEAiUJP.mjs';
17
+ import { studioPlugin } from './packem_shared/STUDIO_PATH-yREc3Bbm.mjs';
18
+ export { STUDIO_PATH, buildStudioUrl } from './packem_shared/STUDIO_PATH-yREc3Bbm.mjs';
16
19
  import { withWorkerStartupHint } from './packem_shared/WORKER_STARTUP_HINT-DhsXUW8k.mjs';
17
20
  export { WORKER_STARTUP_HINT, augmentWorkerStartupError, isWorkerEntryEvalError } from './packem_shared/WORKER_STARTUP_HINT-DhsXUW8k.mjs';
18
- import { wranglerValidatorPlugin } from './packem_shared/wranglerValidatorPlugin-DggqUjxL.mjs';
21
+ import { wranglerValidatorPlugin } from './packem_shared/wranglerValidatorPlugin-CEoJEghS.mjs';
19
22
  export { reconcileWranglerCrons } from './packem_shared/reconcileWranglerCrons-PxGwfCp_.mjs';
20
23
 
21
24
  const agentRulesHintPlugin = (options) => {
@@ -81,6 +84,19 @@ const frameworkDetectPlugin = (options, context) => {
81
84
  };
82
85
  };
83
86
 
87
+ const resolveOverlayOption = (overlay) => {
88
+ if (overlay === false) {
89
+ return false;
90
+ }
91
+ const userOverlay = overlay === true || overlay === void 0 ? {} : overlay;
92
+ return {
93
+ ...userOverlay,
94
+ // After the spread + nullish-coalesce so an explicit `undefined` from the
95
+ // user doesn't erase Lunora's default (the spread would otherwise win).
96
+ forwardedConsoleMethods: userOverlay.forwardedConsoleMethods ?? ["error", "warn"],
97
+ solutionFinders: [...lunoraSolutionFinders, ...userOverlay.solutionFinders ?? []]
98
+ };
99
+ };
84
100
  const resolveOptions = (options) => {
85
101
  const input = options ?? {};
86
102
  const schemaDirectory = input.schemaDir ?? "lunora";
@@ -92,20 +108,13 @@ const resolveOptions = (options) => {
92
108
  } else {
93
109
  cloudflareOption = input.cloudflare;
94
110
  }
95
- let overlayOption;
96
- if (input.overlay === false) {
97
- overlayOption = false;
98
- } else if (input.overlay === true || input.overlay === void 0) {
99
- overlayOption = {};
100
- } else {
101
- overlayOption = input.overlay;
102
- }
103
111
  return {
112
+ allowUnauthenticatedShardAccess: input.allowUnauthenticatedShardAccess ?? false,
104
113
  apiSpec: input.apiSpec ?? "openapi",
105
114
  cloudflare: cloudflareOption,
106
115
  studio: input.studio ?? true,
107
116
  generatedDir: input.generatedDir ?? `${schemaDirectory}/_generated`,
108
- overlay: overlayOption,
117
+ overlay: resolveOverlayOption(input.overlay),
109
118
  projectRoot: input.projectRoot ?? process.cwd(),
110
119
  schemaDir: schemaDirectory,
111
120
  validateWrangler: input.validateWrangler ?? true
@@ -140,6 +149,7 @@ const lunora = (options) => {
140
149
  plugins.push(errorOverlayPlugin(resolved.overlay));
141
150
  }
142
151
  if (resolved.cloudflare !== false) {
152
+ plugins.push(containerLogsPlugin(resolved));
143
153
  const remotePlan = planViteRemoteBindings({ projectRoot: resolved.projectRoot });
144
154
  if (remotePlan.enabled && remotePlan.configPath !== void 0) {
145
155
  plugins.push(remoteBindingsCleanupPlugin(remotePlan.cleanup));
@@ -151,4 +161,4 @@ const lunora = (options) => {
151
161
  };
152
162
  const VERSION = "0.0.0";
153
163
 
154
- export { VERSION, codegenPlugin, createCommandProbe, devVariablesPlugin, frameworkComposePlugin, logStreamPlugin, lunora, planViteRemoteBindings, remoteBindingsCleanupPlugin, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
164
+ export { VERSION, codegenPlugin, containerLogsPlugin, createCommandProbe, devVariablesPlugin, frameworkComposePlugin, logStreamPlugin, lunora, lunoraSolutionFinders, planViteRemoteBindings, remoteBindingsCleanupPlugin, resolveOverlayOption, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
@@ -1,9 +1,12 @@
1
- import { existsSync } from 'node:fs';
1
+ import { existsSync, readFileSync } from 'node:fs';
2
2
  import { resolve, join } from 'node:path';
3
3
 
4
4
  const LUNORA_WORKER_VIRTUAL_ID = "virtual:lunora/worker";
5
5
  const RESOLVED_VIRTUAL_PREFIX = "\0";
6
6
  const RESOLVED_LUNORA_WORKER_ID = `${RESOLVED_VIRTUAL_PREFIX}${LUNORA_WORKER_VIRTUAL_ID}`;
7
+ const CLIENT_WORKER_STUB = `// @lunora/vite — the composed worker entry is worker-only and unavailable in the client environment.
8
+ export default {};
9
+ `;
7
10
  const TRAILING_SLASH = /\/$/;
8
11
  const CLASS_A_WIRING = {
9
12
  "react-router": {
@@ -36,18 +39,27 @@ const isAutoComposable = (context) => {
36
39
  return detected?.class === "A" && CLASS_A_WIRING[detected.framework] !== void 0;
37
40
  };
38
41
  const isWorkerVirtualActive = (context) => isAutoComposable(context);
39
- const buildWorkerEntrySource = (framework, generatedImportBase, hasContainers = false) => {
42
+ const projectUsesUmbrella = (projectRoot) => {
43
+ try {
44
+ const pkg = JSON.parse(readFileSync(join(projectRoot, "package.json"), "utf8"));
45
+ return ["dependencies", "devDependencies", "peerDependencies"].some((field) => pkg[field]?.["lunorash"] !== void 0);
46
+ } catch {
47
+ return false;
48
+ }
49
+ };
50
+ const buildWorkerEntrySource = (framework, generatedImportBase, hasContainers = false, useUmbrella = false, allowUnauthenticatedShardAccess = false) => {
40
51
  const wiring = CLASS_A_WIRING[framework];
41
52
  if (wiring === void 0) {
42
53
  throw new Error(`[lunora] no class-A worker wiring for framework "${framework}"`);
43
54
  }
55
+ const runtimeModule = useUmbrella ? "lunorash/runtime" : "@lunora/runtime";
44
56
  const containersReexport = hasContainers ? `
45
57
  export * from "${generatedImportBase}/containers";
46
58
  ` : "";
47
59
  return `// Generated by @lunora/vite — class-A worker composition (PLAN4 M2).
48
60
  // Do not edit: emitted from the detected framework (${framework}). Point your
49
61
  // wrangler \`main\` here (or re-export it) instead of hand-wiring createWorker.
50
- import { composeWorker } from "@lunora/runtime";
62
+ import { composeWorker } from "${runtimeModule}";
51
63
  ${wiring.imports}
52
64
  import { LUNORA_FUNCTIONS } from "${generatedImportBase}/functions";
53
65
  import { openApiSpec } from "${generatedImportBase}/openapi";
@@ -60,7 +72,7 @@ let worker;
60
72
 
61
73
  export default {
62
74
  async fetch(request, env, context) {
63
- worker ??= composeWorker({
75
+ worker ??= composeWorker({${allowUnauthenticatedShardAccess ? "\n allowUnauthenticatedShardAccess: true," : ""}
64
76
  functions: LUNORA_FUNCTIONS,
65
77
  httpRouter: ${wiring.handler},
66
78
  openApiSpec,
@@ -75,11 +87,21 @@ export default {
75
87
  };
76
88
  const frameworkComposePlugin = (options, context) => {
77
89
  const generatedImportBase = resolve(options.projectRoot, options.generatedDir.replace(TRAILING_SLASH, ""));
90
+ const useUmbrella = projectUsesUmbrella(options.projectRoot);
78
91
  return {
79
92
  load(id) {
80
93
  if (id === RESOLVED_LUNORA_WORKER_ID && isWorkerVirtualActive(context) && context.framework !== void 0) {
94
+ if (this.environment.name === "client") {
95
+ return CLIENT_WORKER_STUB;
96
+ }
81
97
  const hasContainers = existsSync(join(generatedImportBase, "containers.ts"));
82
- return buildWorkerEntrySource(context.framework.framework, generatedImportBase, hasContainers);
98
+ return buildWorkerEntrySource(
99
+ context.framework.framework,
100
+ generatedImportBase,
101
+ hasContainers,
102
+ useUmbrella,
103
+ options.allowUnauthenticatedShardAccess
104
+ );
83
105
  }
84
106
  return void 0;
85
107
  },
@@ -113,7 +113,7 @@ const createStudioHandler = (server, isNonLoopbackBind) => {
113
113
  let assetsStamp;
114
114
  let html;
115
115
  const projectRoot = server.config.root ?? process.cwd();
116
- const serveStaticAsset = (pathname, response) => {
116
+ const serveStaticAsset = (pathname, request, response) => {
117
117
  const stamp = studioAssetsStamp();
118
118
  if (assets === void 0 || stamp !== assetsStamp) {
119
119
  assets = loadStudioAssets(server.config.logger);
@@ -126,6 +126,17 @@ const createStudioHandler = (server, isNonLoopbackBind) => {
126
126
  return;
127
127
  }
128
128
  const isScript = pathname === STUDIO_SCRIPT_PATH;
129
+ const assetKind = isScript ? "js" : "css";
130
+ const etag = stamp === void 0 ? void 0 : `W/"${assetKind}-${String(stamp)}"`;
131
+ response.setHeader("Cache-Control", "no-cache");
132
+ if (etag !== void 0) {
133
+ response.setHeader("ETag", etag);
134
+ if (headerValue(request.headers?.["if-none-match"]) === etag.toLowerCase()) {
135
+ response.statusCode = 304;
136
+ response.end();
137
+ return;
138
+ }
139
+ }
129
140
  sendOk(response, isScript ? assets.script : assets.styles, isScript ? "text/javascript; charset=utf-8" : "text/css; charset=utf-8");
130
141
  };
131
142
  return (request, response, next) => {
@@ -155,7 +166,7 @@ const createStudioHandler = (server, isNonLoopbackBind) => {
155
166
  return;
156
167
  }
157
168
  if (pathname === STUDIO_SCRIPT_PATH || pathname === STUDIO_STYLE_PATH) {
158
- serveStaticAsset(pathname, response);
169
+ serveStaticAsset(pathname, request, response);
159
170
  return;
160
171
  }
161
172
  html ??= renderStudioHtml({
@@ -3,7 +3,7 @@ import { resolve, sep, join } from 'node:path';
3
3
  import { createCodegenProject, refreshCodegenProject, runCodegen, CodegenDiagnosticError } from '@lunora/codegen';
4
4
  import { inferLunoraBindings, reconcileWranglerBindings } from '@lunora/config';
5
5
  import { reconcileWranglerCrons } from './reconcileWranglerCrons-PxGwfCp_.mjs';
6
- import { L as LUNORA_TAG } from './log-Bgkv6QhO.mjs';
6
+ import { L as LUNORA_TAG, a as advisoryLine } from './log-BjO9EWah.mjs';
7
7
 
8
8
  const DEBOUNCE_MS = 100;
9
9
  const TSCONFIG_VARIANT_RE = /[/\\]tsconfig\..+\.json$/u;
@@ -56,7 +56,12 @@ const runCodegenSafely = (options, logger, overlay, project) => {
56
56
  logger.warn(`${LUNORA_TAG} cron trigger sync skipped: ${message}`);
57
57
  }
58
58
  for (const advisory of result.advisories) {
59
- logger.warn(`${LUNORA_TAG} schema advisory [${advisory.level}] ${advisory.name}: ${advisory.detail} — ${advisory.remediation}`);
59
+ const line = advisoryLine(advisory.level, advisory.name, advisory.detail, advisory.remediation);
60
+ if (advisory.level === "ERROR") {
61
+ logger.error(line);
62
+ } else {
63
+ logger.warn(line);
64
+ }
60
65
  }
61
66
  return resolve(result.outputDirectory);
62
67
  } catch (error) {
@@ -128,19 +133,8 @@ const codegenPlugin = (options) => {
128
133
  let closed = false;
129
134
  let cachedProject;
130
135
  const invalidateGenerated = () => {
131
- const environments = server.environments;
132
- const graphs = [];
133
- if (environments !== void 0) {
134
- for (const environment of Object.values(environments)) {
135
- if (environment.moduleGraph !== void 0) {
136
- graphs.push(environment.moduleGraph);
137
- }
138
- }
139
- }
140
- if (graphs.length === 0) {
141
- graphs.push(server.moduleGraph);
142
- }
143
- for (const graph of graphs) {
136
+ for (const environment of Object.values(server.environments)) {
137
+ const graph = environment.moduleGraph;
144
138
  for (const moduleEntry of graph.idToModuleMap.values()) {
145
139
  if (moduleEntry.id && isInside(moduleEntry.id, absoluteGeneratedDirectory)) {
146
140
  graph.invalidateModule(moduleEntry);
@@ -0,0 +1,50 @@
1
+ import { discoverContainerInfo, streamContainerLogs } from '@lunora/config';
2
+ import { l as lunoraLine } from './log-BjO9EWah.mjs';
3
+
4
+ const containerLogsPlugin = (options) => {
5
+ let handle;
6
+ const closeHandle = () => {
7
+ handle?.close();
8
+ handle = void 0;
9
+ };
10
+ return {
11
+ apply: "serve",
12
+ // Fires on `server.close()` for both the normal and middleware-mode dev
13
+ // servers, so the Docker poll loop is torn down even when there is no
14
+ // `httpServer` to listen on (middleware mode).
15
+ buildEnd() {
16
+ closeHandle();
17
+ },
18
+ configureServer(server) {
19
+ if (handle || process.env.LUNORA_CONTAINER_LOGS === "0") {
20
+ return;
21
+ }
22
+ const discovery = discoverContainerInfo(options.projectRoot, options.schemaDir);
23
+ const containers = discovery.containers.map((container) => {
24
+ return { className: container.className, exportName: container.exportName };
25
+ });
26
+ if (containers.length === 0) {
27
+ return;
28
+ }
29
+ const { logger } = server.config;
30
+ handle = streamContainerLogs({
31
+ containers,
32
+ onLine: (line) => {
33
+ const text = lunoraLine(`container:${line.name} ${line.text}`);
34
+ if (line.level === "error") {
35
+ logger.warn(text);
36
+ } else {
37
+ logger.info(text);
38
+ }
39
+ },
40
+ onUnavailable: (message) => {
41
+ logger.warn(lunoraLine(`container: Docker engine unreachable — container logs unavailable (${message})`));
42
+ }
43
+ });
44
+ server.httpServer?.once("close", closeHandle);
45
+ },
46
+ name: "lunora:container-logs"
47
+ };
48
+ };
49
+
50
+ export { containerLogsPlugin as default };
@@ -0,0 +1,19 @@
1
+ import { ensureDevVariables, createConfirm, fillDevSecrets } from '@lunora/config';
2
+ import { l as lunoraLine } from './log-BjO9EWah.mjs';
3
+
4
+ const devVariablesPlugin = (options) => {
5
+ return {
6
+ apply: "serve",
7
+ async configResolved() {
8
+ const info = (message) => {
9
+ console.info(lunoraLine(message));
10
+ };
11
+ await ensureDevVariables({ confirm: createConfirm("[lunora] "), cwd: options.projectRoot, info });
12
+ fillDevSecrets({ cwd: options.projectRoot, info });
13
+ },
14
+ enforce: "pre",
15
+ name: "lunora:dev-vars"
16
+ };
17
+ };
18
+
19
+ export { devVariablesPlugin as default };
@@ -0,0 +1,8 @@
1
+ import { paintBadge, BADGES } from '@lunora/config';
2
+
3
+ const LUNORA_TAG = paintBadge(BADGES.lunora);
4
+ const lunoraLine = (message) => `${LUNORA_TAG} ${message}`;
5
+ const ADVISORY_BADGE = { ERROR: BADGES.error, INFO: BADGES.info, WARN: BADGES.warn };
6
+ const advisoryLine = (level, name, detail, remediation) => `${paintBadge(ADVISORY_BADGE[level])} ${name}: ${detail} — ${remediation}`;
7
+
8
+ export { LUNORA_TAG as L, advisoryLine as a, lunoraLine as l };
@@ -0,0 +1,17 @@
1
+ import { findLunoraSolution } from '@lunora/codegen';
2
+
3
+ const lunoraSolutionFinder = {
4
+ // Synchronous body wrapped in `Promise.resolve` rather than `async`: the
5
+ // overlay's `handle` contract is promise-returning, but `require-await` is on
6
+ // for src and there's nothing to await here.
7
+ handle: (error) => {
8
+ const message = typeof error.message === "string" ? error.message : "";
9
+ const solution = findLunoraSolution(message);
10
+ return Promise.resolve(solution ? { body: solution.body, header: solution.header } : void 0);
11
+ },
12
+ name: "lunora",
13
+ priority: 100
14
+ };
15
+ const lunoraSolutionFinders = [lunoraSolutionFinder];
16
+
17
+ export { lunoraSolutionFinder, lunoraSolutionFinders };
@@ -1,6 +1,6 @@
1
1
  import { spawnSync } from 'node:child_process';
2
2
  import { validateWranglerProject, readWranglerJsonc } from '@lunora/config';
3
- import { l as lunoraLine } from './log-Bgkv6QhO.mjs';
3
+ import { l as lunoraLine } from './log-BjO9EWah.mjs';
4
4
 
5
5
  const isLocalImagePath = (image) => image.startsWith("./") || image.startsWith("../") || image.startsWith("/") || image.includes("Dockerfile");
6
6
  const probeDocker = () => {