@lunora/vite 1.0.0-alpha.7 → 1.0.0-alpha.70

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
@@ -2,7 +2,7 @@ import { CodegenOptions } from '@lunora/codegen';
2
2
  import errorOverlayPlugin from '@visulima/vite-overlay';
3
3
  import { Plugin } from 'vite';
4
4
  import { FrameworkDetection, DetectedFramework, materializeRemoteWranglerConfig, readProjectRemotePreference } from '@lunora/config';
5
- export { type DetectedFramework, type FrameworkClass, type FrameworkDetection, detectFramework } from '@lunora/config';
5
+ export { type DetectedFramework, type FrameworkClass, type FrameworkDetection, type ReconcileCronsResult as ReconcileResult, detectFramework, reconcileWranglerCrons } from '@lunora/config';
6
6
  import { AddressInfo } from 'node:net';
7
7
  /** Options forwarded to `@cloudflare/vite-plugin`'s cloudflare plugin. */
8
8
  type CloudflarePluginOptions = Record<string, unknown>;
@@ -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,37 +73,40 @@ type LunoraPlugins = Plugin[];
63
73
  * inside the lunora schema directory.
64
74
  */
65
75
  declare const codegenPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
66
- interface ReconcileResult {
67
- /** `true` when `wrangler.jsonc` was rewritten. */
68
- changed: boolean;
69
- /** Human-readable reason when reconciliation was skipped (for logging). */
70
- reason?: string;
71
- /** Resolved wrangler path, or `undefined` when none was found. */
72
- wranglerPath?: string;
73
- }
74
76
  /**
75
- * Reconcile the codegen-derived cron schedules into the project's
76
- * `wrangler.jsonc` `triggers.crons` array, preserving comments and formatting
77
- * via `jsonc-parser`'s structural edits.
77
+ * Dev-only plugin that tails the local dev containers' own stdout/stderr in the
78
+ * Vite terminal.
78
79
  *
79
- * When `triggers.crons` already matches `cronTriggers`, nothing is written (so
80
- * we don't churn the file or trip the dev server's file watcher). When the
81
- * project declares no crons, a stale non-empty array is cleared so removed
82
- * crons stop firing.
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>`.
83
86
  *
84
- * This intentionally writes the SAME `triggers.crons` shape the
85
- * `@lunora/config` validator accepts, so the wrangler validator never fights
86
- * the generated value.
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.
87
91
  */
88
- declare const reconcileWranglerCrons: (projectRoot: string, cronTriggers: ReadonlyArray<string>) => ReconcileResult;
92
+ declare const containerLogsPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
93
+ /** Vite plugin (serve-only) that writes the dev-server state record on listen and clears it on close. */
94
+ declare const devStatePlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
89
95
  /**
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`.
96
+ * Dev-only Vite plugin that prepares `.dev.vars` before the worker boots.
97
+ * `@cloudflare/vite-plugin` loads `.dev.vars` into the worker's `env`, but the
98
+ * file is gitignored — so a fresh clone has none and the worker throws on the
99
+ * first required secret (e.g. `AUTH_SECRET is required`). Two steps, both shared
100
+ * with `lunora dev` via `@lunora/config`.
101
+ *
102
+ * First, {@link ensureDevVariables}: when a `.dev.vars.example` exists, prompt
103
+ * to generate `.dev.vars` from it with secrets auto-filled. Second,
104
+ * {@link fillDevSecrets}: fill any empty/placeholder secret already in
105
+ * `.dev.vars` (a `lunora add`-scaffolded project writes secrets blank) and
106
+ * ensure `LUNORA_ADMIN_TOKEN` is present + generated — so the worker boots with
107
+ * working secrets and the Studio authenticates without its login gate. No
108
+ * prompt: it only generates locally-derivable values and never overwrites a real
109
+ * one.
97
110
  *
98
111
  * Runs in `configResolved` (awaited by Vite) so it completes before the
99
112
  * Cloudflare plugin reads the file. Non-interactive runs decline silently.
@@ -198,7 +211,7 @@ declare const isAutoComposable: (context: LunoraPluginContext) => boolean;
198
211
  * virtual module id. Absolute paths are resolved correctly in all environments
199
212
  * (Vite 8 + rolldown 1.x confirmed).
200
213
  */
201
- declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedImportBase: string, hasContainers?: boolean, useUmbrella?: boolean) => string;
214
+ declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedImportBase: string, hasContainers?: boolean, useUmbrella?: boolean, allowUnauthenticatedShardAccess?: boolean) => string;
202
215
  /**
203
216
  * Vite plugin that auto-composes a detected class-A meta-framework's SSR
204
217
  * handler with Lunora into one Cloudflare Worker (PLAN4 §2.4 / §3 class-A row).
@@ -221,9 +234,30 @@ declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedIm
221
234
  */
222
235
  declare const frameworkComposePlugin: (options: ResolvedLunoraPluginOptions, context: LunoraPluginContext) => Plugin;
223
236
  /**
237
+ * The custom HMR event the Lunora Vite plugin sends on the client environment's
238
+ * hot channel after a successful codegen run, in place of the old blanket
239
+ * browser `full-reload`. The generated `api`/`server` modules are just
240
+ * `FunctionReference` metadata, so Vite's granular module HMR re-imports the
241
+ * changed `_generated/*` in place; this event is a non-destructive nudge that
242
+ * lets open WebSocket subscriptions, optimistic state, the offline queue, and
243
+ * form state survive a schema save.
244
+ *
245
+ * Kept in its own module (a single source of truth) so the codegen plugin —
246
+ * whose sole export is the plugin factory — can reference it without becoming a
247
+ * mixed default+named module, and so any future client-side listener can agree
248
+ * on the exact string.
249
+ *
250
+ * There is no first-party client listener yet: `@lunora/client` / `@lunora/react`
251
+ * ship as pre-bundled, side-effect-free dependencies where `import.meta.hot` is
252
+ * `undefined` at runtime (Vite's dep optimizer), so a listener there would be
253
+ * dead (and tree-shaken) code. A future app-local or codegen-emitted listener
254
+ * can import this constant to re-validate active queries over the live socket.
255
+ */
256
+ declare const LUNORA_API_UPDATED_EVENT = "lunora:api-updated";
257
+ /**
224
258
  * Vite plugin (serve-only) that formats Lunora worker logs in the terminal.
225
- * Patches `process.stdout`/`process.stderr` once the dev server is configured
226
- * and restores them when it closes.
259
+ * Patches `process.stdout`/`process.stderr` fresh for each dev-server generation
260
+ * and restores them when that server closes.
227
261
  */
228
262
  declare const logStreamPlugin: () => Plugin;
229
263
  /** The decision a {@link planViteRemoteBindings} call returns. */
@@ -264,23 +298,69 @@ declare const planViteRemoteBindings: (options: PlanViteRemoteOptions) => ViteRe
264
298
  /**
265
299
  * Wrap the cloudflare-plugin options so the dev worker loads the materialized
266
300
  * remote temp config (via `configPath`) when — and only when — remote mode is
267
- * on AND it's a `vite` serve. A `configPath` the caller already set wins (their
268
- * explicit choice), and during a production build nothing is injected, so the
269
- * deployed worker is never affected.
301
+ * on and a temp config was materialized. A `configPath` the caller already set
302
+ * wins (their explicit choice).
270
303
  *
271
- * Returns the wrapped options plus the plan, so `index.ts` can register the
272
- * cleanup on a close hook. Materialization happens lazily inside the `configPath`
273
- * resolution path: it's only meaningful during serve, but computing it eagerly
274
- * is harmless (the materializer is a no-op when disabled) and keeps the wiring
275
- * simple — the plan is computed once here.
304
+ * Pure decision only the serve-vs-build gate lives in
305
+ * {@link remoteBindingsConfigPlugin}'s `config` hook, because the resolved Vite
306
+ * `command` is unknown at plugin-factory time (when this runs). An eager serve
307
+ * check here would always read `command` as undefined and strip `configPath`.
276
308
  */
277
- declare const withRemoteBindings: (options: CloudflarePluginOptions, isServe: () => boolean, plan: ViteRemotePlan) => CloudflarePluginOptions;
309
+ declare const withRemoteBindings: (options: CloudflarePluginOptions, plan: ViteRemotePlan) => CloudflarePluginOptions;
310
+ /**
311
+ * A `enforce: "pre"` plugin whose `config` hook injects the materialized remote
312
+ * temp config into the cloudflare plugin's `configPath` — but only on a `vite`
313
+ * serve, never a production build (so the deployed worker is never affected).
314
+ *
315
+ * The deferral is the whole point: at plugin-factory time Vite has not yet told
316
+ * us `serve` vs `build`, so the check must run in a `config` hook (where
317
+ * `env.command` is known). It mutates the SAME options object handed to
318
+ * `cloudflare()` in place — the cloudflare plugin reads `pluginConfig.configPath`
319
+ * lazily inside its own `config` hook, which runs after this `enforce: "pre"`
320
+ * one, so the injection takes effect. An eager factory-time serve check (the old
321
+ * behaviour) always saw `command` undefined and silently dropped the path, so
322
+ * remote bindings never activated on `vite dev`.
323
+ *
324
+ * When remote mode was requested but nothing materialized (no eligible binding,
325
+ * no wrangler file, …), the plan's `reason` is logged so the degradation isn't
326
+ * silent.
327
+ */
328
+ declare const remoteBindingsConfigPlugin: (options: CloudflarePluginOptions, plan: ViteRemotePlan) => Plugin;
278
329
  /**
279
330
  * A tiny Vite plugin that runs the remote temp-config disposer when the dev
280
331
  * server tears down (`buildEnd` fires on close in serve; `closeBundle` covers
281
332
  * the build/close path). Idempotent cleanup means firing on both is safe.
282
333
  */
283
334
  declare const remoteBindingsCleanupPlugin: (cleanup: () => void) => Plugin;
335
+ /**
336
+ * A `@visulima/vite-overlay` solution finder. Derived from the overlay's own
337
+ * options type so the shape can't drift from the installed package. The overlay
338
+ * runs every finder it's given (custom finders first, then its built-ins),
339
+ * sorted by `priority` descending, and shows the first non-`undefined` result.
340
+ *
341
+ * Note: this type (and {@link Solution}) is re-exported from `@lunora/vite` and
342
+ * intentionally tracks the installed `@visulima/vite-overlay` — if a future
343
+ * overlay release changes the finder contract, that surfaces here as a compile
344
+ * error rather than a silent drift.
345
+ */
346
+ type SolutionFinder = NonNullable<OverlayPluginOptions["solutionFinders"]>[number];
347
+ /** What a finder may return: a Markdown-rendered `{ header?, body }`, or `undefined` to defer. */
348
+ type Solution = NonNullable<Awaited<ReturnType<SolutionFinder["handle"]>>>;
349
+ /**
350
+ * Lunora's solution finder for the dev error overlay. A single finder that
351
+ * delegates to `@lunora/codegen`'s shared rule table (the same table the
352
+ * standalone `lunora dev` CLI prints to the terminal) and returns the first
353
+ * match — so one `priority` slot covers every Lunora rule and the overlay's
354
+ * built-in finders still run for anything we don't recognize (we return
355
+ * `undefined`).
356
+ *
357
+ * Priority is high so a Lunora-specific hint wins over the overlay's generic
358
+ * finder for the same error; a user's own finder can still outrank it with a
359
+ * higher `priority`.
360
+ */
361
+ declare const lunoraSolutionFinder: SolutionFinder;
362
+ /** The finders Lunora injects into the overlay by default. */
363
+ declare const lunoraSolutionFinders: ReadonlyArray<SolutionFinder>;
284
364
  /** Dev-server path the studio SPA is served from. */
285
365
  declare const STUDIO_PATH = "/__lunora";
286
366
  /** Static asset routes the studio document references. */
@@ -358,6 +438,17 @@ declare const withWorkerStartupHint: (plugins: ReadonlyArray<Plugin>) => Plugin[
358
438
  */
359
439
  declare const wranglerValidatorPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
360
440
  /**
441
+ * Resolve the `overlay` toggle into the overlay plugin's options — or `false` to
442
+ * skip it. Lunora's solution finders are **prepended** so they run before the
443
+ * overlay's built-ins; a user's own finders are appended and can still win per
444
+ * error via a strictly higher `priority` (equal priority keeps Lunora first,
445
+ * since the overlay sorts stably). Lunora also forwards both `error` AND `warn`
446
+ * console calls by default (the overlay's own default is `["error"]` only) so
447
+ * Lunora's branded `warn` advisories surface in the browser too — the user can
448
+ * override `forwardedConsoleMethods`.
449
+ */
450
+ declare const resolveOverlayOption: (overlay: LunoraPluginOptions["overlay"]) => false | OverlayPluginOptions;
451
+ /**
361
452
  * Lunora Vite plugin. Returns a flat array of Vite plugins that:
362
453
  *
363
454
  * 1. Run `@lunora/codegen` on startup + on schema file changes.
@@ -370,5 +461,5 @@ declare const wranglerValidatorPlugin: (options: ResolvedLunoraPluginOptions) =>
370
461
  * than relying on whether they're installed.
371
462
  */
372
463
  declare const lunora: (options?: LunoraPluginOptions) => LunoraPlugins;
373
- 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 };
464
+ declare const VERSION: string;
465
+ export { CLASS_A_WIRING, type ClassAWiring, type CloudflarePluginOptions, DEV_WORKER_ENV_VALUE, DEV_WORKER_ENV_VAR, LUNORA_API_UPDATED_EVENT, LUNORA_WORKER_VIRTUAL_ID, type LunoraPluginOptions, type LunoraPlugins, type OverlayPluginOptions, type PlanViteRemoteOptions, type ResolvedLunoraPluginOptions, STUDIO_PATH, type Solution, type SolutionFinder, VERSION, type ViteRemotePlan, WORKER_STARTUP_HINT, augmentWorkerStartupError, buildStudioUrl, buildWorkerEntrySource, codegenPlugin, containerLogsPlugin, createCommandProbe, devStatePlugin, devVariablesPlugin, frameworkComposePlugin, isAutoComposable, isWorkerEntryEvalError, logStreamPlugin, lunora, lunoraSolutionFinder, lunoraSolutionFinders, planViteRemoteBindings, remoteBindingsCleanupPlugin, remoteBindingsConfigPlugin, resolveOverlayOption, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
package/dist/index.d.ts CHANGED
@@ -2,7 +2,7 @@ import { CodegenOptions } from '@lunora/codegen';
2
2
  import errorOverlayPlugin from '@visulima/vite-overlay';
3
3
  import { Plugin } from 'vite';
4
4
  import { FrameworkDetection, DetectedFramework, materializeRemoteWranglerConfig, readProjectRemotePreference } from '@lunora/config';
5
- export { type DetectedFramework, type FrameworkClass, type FrameworkDetection, detectFramework } from '@lunora/config';
5
+ export { type DetectedFramework, type FrameworkClass, type FrameworkDetection, type ReconcileCronsResult as ReconcileResult, detectFramework, reconcileWranglerCrons } from '@lunora/config';
6
6
  import { AddressInfo } from 'node:net';
7
7
  /** Options forwarded to `@cloudflare/vite-plugin`'s cloudflare plugin. */
8
8
  type CloudflarePluginOptions = Record<string, unknown>;
@@ -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,37 +73,40 @@ type LunoraPlugins = Plugin[];
63
73
  * inside the lunora schema directory.
64
74
  */
65
75
  declare const codegenPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
66
- interface ReconcileResult {
67
- /** `true` when `wrangler.jsonc` was rewritten. */
68
- changed: boolean;
69
- /** Human-readable reason when reconciliation was skipped (for logging). */
70
- reason?: string;
71
- /** Resolved wrangler path, or `undefined` when none was found. */
72
- wranglerPath?: string;
73
- }
74
76
  /**
75
- * Reconcile the codegen-derived cron schedules into the project's
76
- * `wrangler.jsonc` `triggers.crons` array, preserving comments and formatting
77
- * via `jsonc-parser`'s structural edits.
77
+ * Dev-only plugin that tails the local dev containers' own stdout/stderr in the
78
+ * Vite terminal.
78
79
  *
79
- * When `triggers.crons` already matches `cronTriggers`, nothing is written (so
80
- * we don't churn the file or trip the dev server's file watcher). When the
81
- * project declares no crons, a stale non-empty array is cleared so removed
82
- * crons stop firing.
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>`.
83
86
  *
84
- * This intentionally writes the SAME `triggers.crons` shape the
85
- * `@lunora/config` validator accepts, so the wrangler validator never fights
86
- * the generated value.
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.
87
91
  */
88
- declare const reconcileWranglerCrons: (projectRoot: string, cronTriggers: ReadonlyArray<string>) => ReconcileResult;
92
+ declare const containerLogsPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
93
+ /** Vite plugin (serve-only) that writes the dev-server state record on listen and clears it on close. */
94
+ declare const devStatePlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
89
95
  /**
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`.
96
+ * Dev-only Vite plugin that prepares `.dev.vars` before the worker boots.
97
+ * `@cloudflare/vite-plugin` loads `.dev.vars` into the worker's `env`, but the
98
+ * file is gitignored — so a fresh clone has none and the worker throws on the
99
+ * first required secret (e.g. `AUTH_SECRET is required`). Two steps, both shared
100
+ * with `lunora dev` via `@lunora/config`.
101
+ *
102
+ * First, {@link ensureDevVariables}: when a `.dev.vars.example` exists, prompt
103
+ * to generate `.dev.vars` from it with secrets auto-filled. Second,
104
+ * {@link fillDevSecrets}: fill any empty/placeholder secret already in
105
+ * `.dev.vars` (a `lunora add`-scaffolded project writes secrets blank) and
106
+ * ensure `LUNORA_ADMIN_TOKEN` is present + generated — so the worker boots with
107
+ * working secrets and the Studio authenticates without its login gate. No
108
+ * prompt: it only generates locally-derivable values and never overwrites a real
109
+ * one.
97
110
  *
98
111
  * Runs in `configResolved` (awaited by Vite) so it completes before the
99
112
  * Cloudflare plugin reads the file. Non-interactive runs decline silently.
@@ -198,7 +211,7 @@ declare const isAutoComposable: (context: LunoraPluginContext) => boolean;
198
211
  * virtual module id. Absolute paths are resolved correctly in all environments
199
212
  * (Vite 8 + rolldown 1.x confirmed).
200
213
  */
201
- declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedImportBase: string, hasContainers?: boolean, useUmbrella?: boolean) => string;
214
+ declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedImportBase: string, hasContainers?: boolean, useUmbrella?: boolean, allowUnauthenticatedShardAccess?: boolean) => string;
202
215
  /**
203
216
  * Vite plugin that auto-composes a detected class-A meta-framework's SSR
204
217
  * handler with Lunora into one Cloudflare Worker (PLAN4 §2.4 / §3 class-A row).
@@ -221,9 +234,30 @@ declare const buildWorkerEntrySource: (framework: DetectedFramework, generatedIm
221
234
  */
222
235
  declare const frameworkComposePlugin: (options: ResolvedLunoraPluginOptions, context: LunoraPluginContext) => Plugin;
223
236
  /**
237
+ * The custom HMR event the Lunora Vite plugin sends on the client environment's
238
+ * hot channel after a successful codegen run, in place of the old blanket
239
+ * browser `full-reload`. The generated `api`/`server` modules are just
240
+ * `FunctionReference` metadata, so Vite's granular module HMR re-imports the
241
+ * changed `_generated/*` in place; this event is a non-destructive nudge that
242
+ * lets open WebSocket subscriptions, optimistic state, the offline queue, and
243
+ * form state survive a schema save.
244
+ *
245
+ * Kept in its own module (a single source of truth) so the codegen plugin —
246
+ * whose sole export is the plugin factory — can reference it without becoming a
247
+ * mixed default+named module, and so any future client-side listener can agree
248
+ * on the exact string.
249
+ *
250
+ * There is no first-party client listener yet: `@lunora/client` / `@lunora/react`
251
+ * ship as pre-bundled, side-effect-free dependencies where `import.meta.hot` is
252
+ * `undefined` at runtime (Vite's dep optimizer), so a listener there would be
253
+ * dead (and tree-shaken) code. A future app-local or codegen-emitted listener
254
+ * can import this constant to re-validate active queries over the live socket.
255
+ */
256
+ declare const LUNORA_API_UPDATED_EVENT = "lunora:api-updated";
257
+ /**
224
258
  * Vite plugin (serve-only) that formats Lunora worker logs in the terminal.
225
- * Patches `process.stdout`/`process.stderr` once the dev server is configured
226
- * and restores them when it closes.
259
+ * Patches `process.stdout`/`process.stderr` fresh for each dev-server generation
260
+ * and restores them when that server closes.
227
261
  */
228
262
  declare const logStreamPlugin: () => Plugin;
229
263
  /** The decision a {@link planViteRemoteBindings} call returns. */
@@ -264,23 +298,69 @@ declare const planViteRemoteBindings: (options: PlanViteRemoteOptions) => ViteRe
264
298
  /**
265
299
  * Wrap the cloudflare-plugin options so the dev worker loads the materialized
266
300
  * remote temp config (via `configPath`) when — and only when — remote mode is
267
- * on AND it's a `vite` serve. A `configPath` the caller already set wins (their
268
- * explicit choice), and during a production build nothing is injected, so the
269
- * deployed worker is never affected.
301
+ * on and a temp config was materialized. A `configPath` the caller already set
302
+ * wins (their explicit choice).
270
303
  *
271
- * Returns the wrapped options plus the plan, so `index.ts` can register the
272
- * cleanup on a close hook. Materialization happens lazily inside the `configPath`
273
- * resolution path: it's only meaningful during serve, but computing it eagerly
274
- * is harmless (the materializer is a no-op when disabled) and keeps the wiring
275
- * simple — the plan is computed once here.
304
+ * Pure decision only the serve-vs-build gate lives in
305
+ * {@link remoteBindingsConfigPlugin}'s `config` hook, because the resolved Vite
306
+ * `command` is unknown at plugin-factory time (when this runs). An eager serve
307
+ * check here would always read `command` as undefined and strip `configPath`.
276
308
  */
277
- declare const withRemoteBindings: (options: CloudflarePluginOptions, isServe: () => boolean, plan: ViteRemotePlan) => CloudflarePluginOptions;
309
+ declare const withRemoteBindings: (options: CloudflarePluginOptions, plan: ViteRemotePlan) => CloudflarePluginOptions;
310
+ /**
311
+ * A `enforce: "pre"` plugin whose `config` hook injects the materialized remote
312
+ * temp config into the cloudflare plugin's `configPath` — but only on a `vite`
313
+ * serve, never a production build (so the deployed worker is never affected).
314
+ *
315
+ * The deferral is the whole point: at plugin-factory time Vite has not yet told
316
+ * us `serve` vs `build`, so the check must run in a `config` hook (where
317
+ * `env.command` is known). It mutates the SAME options object handed to
318
+ * `cloudflare()` in place — the cloudflare plugin reads `pluginConfig.configPath`
319
+ * lazily inside its own `config` hook, which runs after this `enforce: "pre"`
320
+ * one, so the injection takes effect. An eager factory-time serve check (the old
321
+ * behaviour) always saw `command` undefined and silently dropped the path, so
322
+ * remote bindings never activated on `vite dev`.
323
+ *
324
+ * When remote mode was requested but nothing materialized (no eligible binding,
325
+ * no wrangler file, …), the plan's `reason` is logged so the degradation isn't
326
+ * silent.
327
+ */
328
+ declare const remoteBindingsConfigPlugin: (options: CloudflarePluginOptions, plan: ViteRemotePlan) => Plugin;
278
329
  /**
279
330
  * A tiny Vite plugin that runs the remote temp-config disposer when the dev
280
331
  * server tears down (`buildEnd` fires on close in serve; `closeBundle` covers
281
332
  * the build/close path). Idempotent cleanup means firing on both is safe.
282
333
  */
283
334
  declare const remoteBindingsCleanupPlugin: (cleanup: () => void) => Plugin;
335
+ /**
336
+ * A `@visulima/vite-overlay` solution finder. Derived from the overlay's own
337
+ * options type so the shape can't drift from the installed package. The overlay
338
+ * runs every finder it's given (custom finders first, then its built-ins),
339
+ * sorted by `priority` descending, and shows the first non-`undefined` result.
340
+ *
341
+ * Note: this type (and {@link Solution}) is re-exported from `@lunora/vite` and
342
+ * intentionally tracks the installed `@visulima/vite-overlay` — if a future
343
+ * overlay release changes the finder contract, that surfaces here as a compile
344
+ * error rather than a silent drift.
345
+ */
346
+ type SolutionFinder = NonNullable<OverlayPluginOptions["solutionFinders"]>[number];
347
+ /** What a finder may return: a Markdown-rendered `{ header?, body }`, or `undefined` to defer. */
348
+ type Solution = NonNullable<Awaited<ReturnType<SolutionFinder["handle"]>>>;
349
+ /**
350
+ * Lunora's solution finder for the dev error overlay. A single finder that
351
+ * delegates to `@lunora/codegen`'s shared rule table (the same table the
352
+ * standalone `lunora dev` CLI prints to the terminal) and returns the first
353
+ * match — so one `priority` slot covers every Lunora rule and the overlay's
354
+ * built-in finders still run for anything we don't recognize (we return
355
+ * `undefined`).
356
+ *
357
+ * Priority is high so a Lunora-specific hint wins over the overlay's generic
358
+ * finder for the same error; a user's own finder can still outrank it with a
359
+ * higher `priority`.
360
+ */
361
+ declare const lunoraSolutionFinder: SolutionFinder;
362
+ /** The finders Lunora injects into the overlay by default. */
363
+ declare const lunoraSolutionFinders: ReadonlyArray<SolutionFinder>;
284
364
  /** Dev-server path the studio SPA is served from. */
285
365
  declare const STUDIO_PATH = "/__lunora";
286
366
  /** Static asset routes the studio document references. */
@@ -358,6 +438,17 @@ declare const withWorkerStartupHint: (plugins: ReadonlyArray<Plugin>) => Plugin[
358
438
  */
359
439
  declare const wranglerValidatorPlugin: (options: ResolvedLunoraPluginOptions) => Plugin;
360
440
  /**
441
+ * Resolve the `overlay` toggle into the overlay plugin's options — or `false` to
442
+ * skip it. Lunora's solution finders are **prepended** so they run before the
443
+ * overlay's built-ins; a user's own finders are appended and can still win per
444
+ * error via a strictly higher `priority` (equal priority keeps Lunora first,
445
+ * since the overlay sorts stably). Lunora also forwards both `error` AND `warn`
446
+ * console calls by default (the overlay's own default is `["error"]` only) so
447
+ * Lunora's branded `warn` advisories surface in the browser too — the user can
448
+ * override `forwardedConsoleMethods`.
449
+ */
450
+ declare const resolveOverlayOption: (overlay: LunoraPluginOptions["overlay"]) => false | OverlayPluginOptions;
451
+ /**
361
452
  * Lunora Vite plugin. Returns a flat array of Vite plugins that:
362
453
  *
363
454
  * 1. Run `@lunora/codegen` on startup + on schema file changes.
@@ -370,5 +461,5 @@ declare const wranglerValidatorPlugin: (options: ResolvedLunoraPluginOptions) =>
370
461
  * than relying on whether they're installed.
371
462
  */
372
463
  declare const lunora: (options?: LunoraPluginOptions) => LunoraPlugins;
373
- 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 };
464
+ declare const VERSION: string;
465
+ export { CLASS_A_WIRING, type ClassAWiring, type CloudflarePluginOptions, DEV_WORKER_ENV_VALUE, DEV_WORKER_ENV_VAR, LUNORA_API_UPDATED_EVENT, LUNORA_WORKER_VIRTUAL_ID, type LunoraPluginOptions, type LunoraPlugins, type OverlayPluginOptions, type PlanViteRemoteOptions, type ResolvedLunoraPluginOptions, STUDIO_PATH, type Solution, type SolutionFinder, VERSION, type ViteRemotePlan, WORKER_STARTUP_HINT, augmentWorkerStartupError, buildStudioUrl, buildWorkerEntrySource, codegenPlugin, containerLogsPlugin, createCommandProbe, devStatePlugin, devVariablesPlugin, frameworkComposePlugin, isAutoComposable, isWorkerEntryEvalError, logStreamPlugin, lunora, lunoraSolutionFinder, lunoraSolutionFinders, planViteRemoteBindings, remoteBindingsCleanupPlugin, remoteBindingsConfigPlugin, resolveOverlayOption, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
package/dist/index.mjs CHANGED
@@ -1,22 +1,28 @@
1
+ import { createRequire } from 'node:module';
1
2
  import { cloudflare } from '@cloudflare/vite-plugin';
2
3
  import errorOverlayPlugin from '@visulima/vite-overlay';
3
4
  import { detectAgentRules, claimAgentRulesHint, AGENT_RULES_HINT, detectFramework } from '@lunora/config';
4
- export { detectFramework } from '@lunora/config';
5
+ export { detectFramework, reconcileWranglerCrons } from '@lunora/config';
5
6
  import { l as lunoraLine } from './packem_shared/log-BjO9EWah.mjs';
6
- import codegenPlugin from './packem_shared/codegenPlugin-MuvbqAP8.mjs';
7
- import devVariablesPlugin from './packem_shared/devVariablesPlugin-CVjkQay7.mjs';
7
+ import codegenPlugin from './packem_shared/codegenPlugin-B8c38rky.mjs';
8
+ import containerLogsPlugin from './packem_shared/containerLogsPlugin-DMssU3wb.mjs';
9
+ import devStatePlugin from './packem_shared/devStatePlugin-DdjfMrhx.mjs';
10
+ import devVariablesPlugin from './packem_shared/devVariablesPlugin-CDNSnvOP.mjs';
8
11
  import { createCommandProbe, withDevWorkerEnv } from './packem_shared/DEV_WORKER_ENV_VALUE-Coo6bgVz.mjs';
9
12
  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-CZVcjgKo.mjs';
11
- export { CLASS_A_WIRING, LUNORA_WORKER_VIRTUAL_ID, buildWorkerEntrySource, isAutoComposable } from './packem_shared/CLASS_A_WIRING-CZVcjgKo.mjs';
12
- import logStreamPlugin from './packem_shared/logStreamPlugin-CqvZ17kd.mjs';
13
- 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';
13
+ import { frameworkComposePlugin } from './packem_shared/CLASS_A_WIRING-T1RohRZQ.mjs';
14
+ export { CLASS_A_WIRING, LUNORA_WORKER_VIRTUAL_ID, buildWorkerEntrySource, isAutoComposable } from './packem_shared/CLASS_A_WIRING-T1RohRZQ.mjs';
15
+ import logStreamPlugin from './packem_shared/logStreamPlugin-DeFzd_pC.mjs';
16
+ import { planViteRemoteBindings, remoteBindingsCleanupPlugin, remoteBindingsConfigPlugin } from './packem_shared/planViteRemoteBindings-CRU90Bip.mjs';
17
+ export { withRemoteBindings } from './packem_shared/planViteRemoteBindings-CRU90Bip.mjs';
18
+ import { lunoraSolutionFinders } from './packem_shared/lunoraSolutionFinder-BKEAiUJP.mjs';
19
+ export { lunoraSolutionFinder } from './packem_shared/lunoraSolutionFinder-BKEAiUJP.mjs';
20
+ import { studioPlugin } from './packem_shared/STUDIO_PATH-U0eIaW1t.mjs';
21
+ export { STUDIO_PATH, buildStudioUrl } from './packem_shared/STUDIO_PATH-U0eIaW1t.mjs';
16
22
  import { withWorkerStartupHint } from './packem_shared/WORKER_STARTUP_HINT-DhsXUW8k.mjs';
17
23
  export { WORKER_STARTUP_HINT, augmentWorkerStartupError, isWorkerEntryEvalError } from './packem_shared/WORKER_STARTUP_HINT-DhsXUW8k.mjs';
18
- import { wranglerValidatorPlugin } from './packem_shared/wranglerValidatorPlugin-CEoJEghS.mjs';
19
- export { reconcileWranglerCrons } from './packem_shared/reconcileWranglerCrons-PxGwfCp_.mjs';
24
+ import { wranglerValidatorPlugin } from './packem_shared/wranglerValidatorPlugin-C5VWOD7N.mjs';
25
+ export { default as LUNORA_API_UPDATED_EVENT } from './packem_shared/LUNORA_API_UPDATED_EVENT-BrkWk3rr.mjs';
20
26
 
21
27
  const agentRulesHintPlugin = (options) => {
22
28
  return {
@@ -81,6 +87,19 @@ const frameworkDetectPlugin = (options, context) => {
81
87
  };
82
88
  };
83
89
 
90
+ const resolveOverlayOption = (overlay) => {
91
+ if (overlay === false) {
92
+ return false;
93
+ }
94
+ const userOverlay = overlay === true || overlay === void 0 ? {} : overlay;
95
+ return {
96
+ ...userOverlay,
97
+ // After the spread + nullish-coalesce so an explicit `undefined` from the
98
+ // user doesn't erase Lunora's default (the spread would otherwise win).
99
+ forwardedConsoleMethods: userOverlay.forwardedConsoleMethods ?? ["error", "warn"],
100
+ solutionFinders: [...lunoraSolutionFinders, ...userOverlay.solutionFinders ?? []]
101
+ };
102
+ };
84
103
  const resolveOptions = (options) => {
85
104
  const input = options ?? {};
86
105
  const schemaDirectory = input.schemaDir ?? "lunora";
@@ -92,20 +111,13 @@ const resolveOptions = (options) => {
92
111
  } else {
93
112
  cloudflareOption = input.cloudflare;
94
113
  }
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
114
  return {
115
+ allowUnauthenticatedShardAccess: input.allowUnauthenticatedShardAccess ?? false,
104
116
  apiSpec: input.apiSpec ?? "openapi",
105
117
  cloudflare: cloudflareOption,
106
118
  studio: input.studio ?? true,
107
119
  generatedDir: input.generatedDir ?? `${schemaDirectory}/_generated`,
108
- overlay: overlayOption,
120
+ overlay: resolveOverlayOption(input.overlay),
109
121
  projectRoot: input.projectRoot ?? process.cwd(),
110
122
  schemaDir: schemaDirectory,
111
123
  validateWrangler: input.validateWrangler ?? true
@@ -128,6 +140,9 @@ const lunora = (options) => {
128
140
  devVariablesPlugin(resolved),
129
141
  codegenPlugin(resolved),
130
142
  logStreamPlugin(),
143
+ // Registers the running dev server in `.lunora/dev.json` so
144
+ // `lunora dev --background|stop|status|logs` manage Vite projects too.
145
+ devStatePlugin(resolved),
131
146
  agentRulesHintPlugin(resolved)
132
147
  ];
133
148
  if (resolved.studio) {
@@ -140,15 +155,19 @@ const lunora = (options) => {
140
155
  plugins.push(errorOverlayPlugin(resolved.overlay));
141
156
  }
142
157
  if (resolved.cloudflare !== false) {
158
+ plugins.push(containerLogsPlugin(resolved));
143
159
  const remotePlan = planViteRemoteBindings({ projectRoot: resolved.projectRoot });
144
- if (remotePlan.enabled && remotePlan.configPath !== void 0) {
145
- plugins.push(remoteBindingsCleanupPlugin(remotePlan.cleanup));
160
+ const cloudflareOptions = withDevWorkerEnv(resolved.cloudflare, isServe);
161
+ if (remotePlan.enabled) {
162
+ if (remotePlan.configPath !== void 0) {
163
+ plugins.push(remoteBindingsCleanupPlugin(remotePlan.cleanup));
164
+ }
165
+ plugins.push(remoteBindingsConfigPlugin(cloudflareOptions, remotePlan));
146
166
  }
147
- const cloudflareOptions = withRemoteBindings(withDevWorkerEnv(resolved.cloudflare, isServe), isServe, remotePlan);
148
167
  plugins.push(...withWorkerStartupHint(cloudflare(cloudflareOptions)));
149
168
  }
150
169
  return plugins;
151
170
  };
152
- const VERSION = "0.0.0";
171
+ const VERSION = createRequire(import.meta.url)("../package.json").version;
153
172
 
154
- export { VERSION, codegenPlugin, createCommandProbe, devVariablesPlugin, frameworkComposePlugin, logStreamPlugin, lunora, planViteRemoteBindings, remoteBindingsCleanupPlugin, studioPlugin, withDevWorkerEnv, withRemoteBindings, withWorkerStartupHint, wranglerValidatorPlugin };
173
+ export { VERSION, codegenPlugin, containerLogsPlugin, createCommandProbe, devStatePlugin, devVariablesPlugin, frameworkComposePlugin, logStreamPlugin, lunora, lunoraSolutionFinders, planViteRemoteBindings, remoteBindingsCleanupPlugin, remoteBindingsConfigPlugin, resolveOverlayOption, studioPlugin, withDevWorkerEnv, withWorkerStartupHint, wranglerValidatorPlugin };