@ogygia/core 0.12.2 → 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (227) hide show
  1. package/dist/LateIsland.svelte +3 -2
  2. package/dist/LateIslandAwait.svelte +6 -2
  3. package/dist/Region.svelte +22 -33
  4. package/dist/boundary.d.ts +0 -2
  5. package/dist/boundary.js +8 -6
  6. package/dist/compiler/debarrel/barrel.d.ts +15 -8
  7. package/dist/compiler/debarrel/barrel.js +92 -45
  8. package/dist/compiler/debarrel/plugin.js +41 -17
  9. package/dist/compiler/dev/dev-hmr.d.ts +3 -0
  10. package/dist/compiler/dev/dev-hmr.js +3 -0
  11. package/dist/compiler/driver.js +11 -4
  12. package/dist/compiler/flags.js +21 -7
  13. package/dist/compiler/fouc-css.js +5 -1
  14. package/dist/compiler/kit.js +5 -1
  15. package/dist/compiler/link/build-output.js +12 -3
  16. package/dist/compiler/macros/bake.js +18 -4
  17. package/dist/compiler/macros/dollar.js +6 -1
  18. package/dist/compiler/macros/source.js +6 -1
  19. package/dist/compiler/macros/store.js +10 -2
  20. package/dist/compiler/macros/wire.js +6 -1
  21. package/dist/compiler/parse/scan.js +10 -3
  22. package/dist/compiler/region/transform.js +188 -74
  23. package/dist/context-bridge.js +13 -5
  24. package/dist/context.d.ts +9 -0
  25. package/dist/context.js +30 -1
  26. package/dist/document.d.ts +1 -1
  27. package/dist/document.js +7 -1
  28. package/dist/early-holes-head.d.ts +5 -0
  29. package/dist/early-holes-head.js +20 -0
  30. package/dist/early-holes.d.ts +34 -0
  31. package/dist/early-holes.js +51 -0
  32. package/dist/entry-hydrate.js +9 -12
  33. package/dist/events/bus.js +7 -2
  34. package/dist/events/schema.d.ts +2 -2
  35. package/dist/fn-transport.js +4 -3
  36. package/dist/hooks.js +94 -163
  37. package/dist/insights/page-assets.js +11 -3
  38. package/dist/insights/page-insights.d.ts +0 -2
  39. package/dist/insights/page-insights.js +14 -10
  40. package/dist/insights/session-insights.js +2 -2
  41. package/dist/internal-instruments.d.ts +3 -1
  42. package/dist/internal-instruments.js +3 -1
  43. package/dist/internal-server.d.ts +3 -2
  44. package/dist/internal-server.js +3 -2
  45. package/dist/internal.d.ts +1 -2
  46. package/dist/internal.js +1 -2
  47. package/dist/keep-fallback.d.ts +0 -2
  48. package/dist/keep-fallback.js +4 -5
  49. package/dist/live-transport.js +3 -2
  50. package/dist/messages/build.d.ts +585 -0
  51. package/dist/messages/build.js +800 -0
  52. package/dist/messages/core.d.ts +464 -0
  53. package/dist/messages/core.js +843 -0
  54. package/dist/messages/define.d.ts +62 -0
  55. package/dist/messages/define.js +49 -0
  56. package/dist/messages/index.d.ts +6 -0
  57. package/dist/messages/index.js +6 -0
  58. package/dist/messages/island.d.ts +4 -0
  59. package/dist/messages/island.js +34 -0
  60. package/dist/messages/lazy.d.ts +3 -0
  61. package/dist/messages/lazy.js +22 -0
  62. package/dist/messages/prose.d.ts +15 -0
  63. package/dist/messages/prose.js +43 -0
  64. package/dist/messages/server.d.ts +142 -0
  65. package/dist/messages/server.js +246 -0
  66. package/dist/nested.d.ts +3 -0
  67. package/dist/nested.js +8 -1
  68. package/dist/plugin/css.js +14 -13
  69. package/dist/plugin/facts.d.ts +2 -0
  70. package/dist/plugin/facts.js +10 -6
  71. package/dist/plugin/hooks.d.ts +7 -17
  72. package/dist/plugin/hooks.js +32 -4
  73. package/dist/plugin/index.d.ts +9 -2
  74. package/dist/plugin/request-scope.d.ts +20 -0
  75. package/dist/plugin/request-scope.js +149 -0
  76. package/dist/plugin/server.d.ts +20 -2
  77. package/dist/plugin/server.js +98 -28
  78. package/dist/plugin/testing.d.ts +6 -0
  79. package/dist/plugin/testing.js +14 -2
  80. package/dist/policies.d.ts +21 -0
  81. package/dist/policies.js +66 -0
  82. package/dist/preference.js +6 -1
  83. package/dist/preload.js +6 -9
  84. package/dist/ref-watch.d.ts +0 -10
  85. package/dist/ref-watch.js +14 -1
  86. package/dist/ref.js +17 -5
  87. package/dist/region-snippet.js +9 -3
  88. package/dist/region.js +2 -1
  89. package/dist/report-thrown.d.ts +9 -0
  90. package/dist/report-thrown.js +15 -0
  91. package/dist/rules.d.ts +17 -0
  92. package/dist/rules.js +57 -0
  93. package/dist/runtime/beacon.d.ts +0 -4
  94. package/dist/runtime/beacon.js +58 -43
  95. package/dist/runtime/boot-link.js +19 -2
  96. package/dist/runtime/client-hooks.js +2 -1
  97. package/dist/runtime/concurrency.d.ts +16 -5
  98. package/dist/runtime/concurrency.js +123 -7
  99. package/dist/runtime/core.d.ts +3 -1
  100. package/dist/runtime/core.js +718 -384
  101. package/dist/runtime/devalue-lane.d.ts +13 -0
  102. package/dist/runtime/devalue-lane.js +49 -0
  103. package/dist/runtime/disclose-version.d.ts +1 -0
  104. package/dist/runtime/disclose-version.js +14 -0
  105. package/dist/runtime/early-holes.d.ts +4 -0
  106. package/dist/runtime/early-holes.js +40 -0
  107. package/dist/runtime/entry-locations.js +40 -49
  108. package/dist/runtime/foreign-props.d.ts +0 -7
  109. package/dist/runtime/foreign-props.js +2 -1
  110. package/dist/runtime/form-continuity.d.ts +2 -2
  111. package/dist/runtime/form-continuity.js +30 -8
  112. package/dist/runtime/frame-nav.d.ts +7 -1
  113. package/dist/runtime/frame-nav.js +84 -18
  114. package/dist/runtime/frame-store.d.ts +25 -16
  115. package/dist/runtime/frame-store.js +124 -98
  116. package/dist/runtime/frames.js +8 -4
  117. package/dist/runtime/hole-facts.js +6 -7
  118. package/dist/runtime/hole-fetch.d.ts +17 -0
  119. package/dist/runtime/hole-fetch.js +47 -0
  120. package/dist/runtime/hydrate-core.d.ts +4 -4
  121. package/dist/runtime/hydrate-core.js +80 -64
  122. package/dist/runtime/import-again.d.ts +33 -0
  123. package/dist/runtime/import-again.js +217 -0
  124. package/dist/runtime/interaction-replay.d.ts +5 -3
  125. package/dist/runtime/interaction-replay.js +6 -6
  126. package/dist/runtime/interaction.d.ts +14 -3
  127. package/dist/runtime/interaction.js +46 -23
  128. package/dist/runtime/kit-page-thread.svelte.d.ts +2 -1
  129. package/dist/runtime/kit-page-thread.svelte.js +44 -20
  130. package/dist/runtime/lakes.d.ts +1 -1
  131. package/dist/runtime/lakes.js +18 -5
  132. package/dist/runtime/live.d.ts +1 -2
  133. package/dist/runtime/live.js +6 -8
  134. package/dist/runtime/load-scheduler.d.ts +29 -18
  135. package/dist/runtime/load-scheduler.js +236 -136
  136. package/dist/runtime/nav-handle.js +25 -6
  137. package/dist/runtime/navigation.d.ts +24 -0
  138. package/dist/runtime/navigation.js +24 -0
  139. package/dist/runtime/ownership.d.ts +1 -1
  140. package/dist/runtime/page-defer.js +3 -3
  141. package/dist/runtime/parse-html.d.ts +15 -4
  142. package/dist/runtime/parse-html.js +24 -5
  143. package/dist/runtime/pushable-props.svelte.d.ts +21 -0
  144. package/dist/runtime/pushable-props.svelte.js +37 -0
  145. package/dist/runtime/region-endpoint-url.js +4 -6
  146. package/dist/runtime/region-machines.d.ts +111 -0
  147. package/dist/runtime/region-machines.js +97 -0
  148. package/dist/runtime/region-sheets.d.ts +31 -0
  149. package/dist/runtime/region-sheets.js +49 -0
  150. package/dist/runtime/restore-report.d.ts +1 -0
  151. package/dist/runtime/restore-report.js +36 -0
  152. package/dist/runtime/restore.d.ts +5 -2
  153. package/dist/runtime/restore.js +33 -14
  154. package/dist/runtime/router-nav.d.ts +32 -12
  155. package/dist/runtime/router-nav.js +320 -228
  156. package/dist/runtime/router.d.ts +83 -19
  157. package/dist/runtime/router.js +297 -129
  158. package/dist/runtime/rules.d.ts +35 -0
  159. package/dist/runtime/rules.js +137 -0
  160. package/dist/runtime/schedule.d.ts +6 -3
  161. package/dist/runtime/schedule.js +172 -57
  162. package/dist/runtime/seeds.d.ts +5 -1
  163. package/dist/runtime/seeds.js +11 -5
  164. package/dist/runtime/session.d.ts +46 -7
  165. package/dist/runtime/session.js +78 -10
  166. package/dist/runtime/slots.d.ts +53 -20
  167. package/dist/runtime/slots.js +2 -3
  168. package/dist/runtime/soft-refresh.d.ts +16 -0
  169. package/dist/runtime/soft-refresh.js +14 -0
  170. package/dist/runtime/svelte-warnings.d.ts +4 -10
  171. package/dist/runtime/svelte-warnings.js +7 -4
  172. package/dist/runtime/wire-classes.js +21 -8
  173. package/dist/runtime/wire-format.js +6 -3
  174. package/dist/script.js +8 -7
  175. package/dist/seed-ref-reviver.d.ts +0 -9
  176. package/dist/seed-ref-reviver.js +2 -1
  177. package/dist/seed-refs.d.ts +46 -36
  178. package/dist/seed-refs.js +131 -92
  179. package/dist/server/document-tail.d.ts +6 -0
  180. package/dist/server/document-tail.js +7 -1
  181. package/dist/server/early-holes-csp.d.ts +14 -0
  182. package/dist/server/early-holes-csp.js +73 -0
  183. package/dist/server/page-facts.d.ts +4 -1
  184. package/dist/server/page-facts.js +4 -1
  185. package/dist/server/page-seed.d.ts +2 -2
  186. package/dist/server/page-seed.js +104 -11
  187. package/dist/server/page-stream.d.ts +14 -1
  188. package/dist/server/page-stream.js +119 -2
  189. package/dist/server/personal-response.d.ts +0 -5
  190. package/dist/server/personal-response.js +6 -2
  191. package/dist/server/props-wire.js +5 -1
  192. package/dist/server/region-endpoint.js +10 -2
  193. package/dist/server/render-cache.d.ts +24 -20
  194. package/dist/server/render-cache.js +99 -40
  195. package/dist/server/render-page.js +46 -15
  196. package/dist/server/request-stats.d.ts +10 -8
  197. package/dist/server/request-stats.js +20 -12
  198. package/dist/server/rules.d.ts +11 -0
  199. package/dist/server/rules.js +64 -0
  200. package/dist/server/signing-key.js +31 -18
  201. package/dist/server/stream-regions.d.ts +14 -1
  202. package/dist/server/stream-regions.js +38 -2
  203. package/dist/shared-state.js +5 -1
  204. package/dist/shims/app-navigation.js +3 -2
  205. package/dist/shims/page-store.svelte.js +16 -2
  206. package/dist/source.js +7 -2
  207. package/dist/store-transport.js +12 -5
  208. package/dist/transport.js +3 -2
  209. package/dist/vite/index.js +83 -74
  210. package/dist/vite/kit-dirs.js +7 -1
  211. package/dist/vite/options.d.ts +2 -1
  212. package/dist/vite/options.js +11 -20
  213. package/dist/vite/package-files.js +13 -3
  214. package/dist/vite/paths.d.ts +2 -0
  215. package/dist/vite/paths.js +4 -4
  216. package/dist/vite/plugin-api.d.ts +6 -5
  217. package/dist/vite/plugin-api.js +42 -14
  218. package/dist/vite/style-preprocess.js +10 -3
  219. package/package.json +8 -1
  220. package/dist/KeepHost.svelte +0 -19
  221. package/dist/KeepHost.svelte.d.ts +0 -10
  222. package/dist/LiveHost.svelte +0 -19
  223. package/dist/LiveHost.svelte.d.ts +0 -10
  224. package/dist/NestedProvider.svelte +0 -13
  225. package/dist/NestedProvider.svelte.d.ts +0 -8
  226. package/dist/runtime/page-cache.d.ts +0 -23
  227. package/dist/runtime/page-cache.js +0 -74
@@ -27,6 +27,7 @@ import { export_names, is_registry_stub_id, registry_client_id, registry_client_
27
27
  import { island_host_loaded } from "./link/emit-gate.js";
28
28
  import { stamp_opaque } from "./ownership-stamps.js";
29
29
  import { strip_bom } from "./bom.js";
30
+ import { OgygiaError, line, warn } from "../messages/build.js";
30
31
  // src/compiler/driver.ts
31
32
  /**
32
33
  * The `Compiler` — the driver / long-lived compile session. It holds the `Program` (cross-file
@@ -116,7 +117,7 @@ const warned_undeclared = /* @__PURE__ */ new Set();
116
117
  function warn_undeclared_pkg_marks(id) {
117
118
  if (warned_undeclared.has(id)) return;
118
119
  warned_undeclared.add(id);
119
- console.warn(`[ogygia] ${id} uses ogygia marks, but its package declares no \`"ogygia": { "files": [...] }\` — the marks are IGNORED. Add the field to that package's package.json (npm-"files"-style paths/globs) to put it on ogygia's compile surface.`);
120
+ warn("package_marks_undeclared", { file: id });
120
121
  }
121
122
  /** The client-hooks boot snippet for a generated runtime entry: an import of the runtime runner and
122
123
  * a call that dynamic-imports the app's `hooks.client.*` — empty when the app has none. Runs the
@@ -460,7 +461,10 @@ var Compiler = class {
460
461
  }
461
462
  for (const { file, keys } of reads) for (const key of keys) {
462
463
  if (island_set.has(key)) continue;
463
- console.warn(`[ogygia] island code ${path.relative(root, file)} reads getContext('${key}'), but this app registers no ogygia context provider and no island code sets '${key}'. On a csr=false page a value set with Svelte's setContext in a page/layout does NOT cross the island boundary — the island reads undefined on the client, re-renders a different tree, and Svelte discards its server-rendered DOM on hydration (a silent full re-render). Fix: provide '${key}' with setContext / <Provide> / createContext imported from 'ogygia' (in a csr=false layout); the read stays getContext('${key}').`);
464
+ warn("island_context_unprovided", {
465
+ file: path.relative(root, file),
466
+ key
467
+ });
464
468
  }
465
469
  }
466
470
  if (transportable_modules.size > 0) runtime_marks.wire = true;
@@ -854,7 +858,7 @@ var Compiler = class {
854
858
  if (id === RESOLVED("virtual:ogygia/secret")) return secret_module(ssr, ctx.build_secret, ctx.id_salt !== "");
855
859
  if (id === RESOLVED("virtual:ogygia/sign")) return sign_module(ssr, ctx.hmac_module);
856
860
  if (id === RESOLVED("virtual:ogygia/request-event")) {
857
- if (!ssr) return `export function getRequestEvent() { throw new Error('[ogygia] getRequestEvent is server-only'); }`;
861
+ if (!ssr) return `export function getRequestEvent() { throw new Error(${JSON.stringify(line("request_event_server_only"))}); }`;
858
862
  return `export { getRequestEvent } from '$app/server';`;
859
863
  }
860
864
  if (id === RESOLVED("virtual:ogygia/region-endpoint")) {
@@ -979,7 +983,10 @@ var Compiler = class {
979
983
  const host = registry.get(importer_id).hostPath ?? "";
980
984
  const resolved = await resolve(source, host, { skipSelf: true });
981
985
  if (resolved?.id) island_graph.add(strip_id(resolved.id));
982
- if (!resolved && !source.startsWith(".") && !source.startsWith("\0") && !source.startsWith("virtual:") && !path.isAbsolute(source)) throw new Error(`[ogygia] cannot resolve '${source}' imported by the generated island module for ${path.relative(ctx.root, host)}. That island was marked on a package import, so the specifier must resolve from the host file: check the package is installed and its "exports" map exposes this subpath (with a "svelte" condition for .svelte components).`);
986
+ if (!resolved && !source.startsWith(".") && !source.startsWith("\0") && !source.startsWith("virtual:") && !path.isAbsolute(source)) throw new OgygiaError("island_import_unresolved", {
987
+ specifier: source,
988
+ host: path.relative(ctx.root, host)
989
+ });
983
990
  return resolved;
984
991
  }
985
992
  if (!ssr && importer_id && island_graph.has(importer_id) && !is_island_path(source)) {
@@ -24,11 +24,22 @@ const KINDS = /* @__PURE__ */ new Set(["flag"]);
24
24
  const MAYBE_RE = /\bflag\b/;
25
25
  /** All `<script …>` blocks of a `.svelte` file (instance AND module — flags can sit in either). */
26
26
  const SCRIPT_BLOCK_RE = /<script\b[^>]*>([\s\S]*?)<\/script>/g;
27
- const NL = 10;
28
- function line_at(src, index, base_line) {
29
- let line = base_line;
30
- for (let i = 0; i < index; i++) if (src.charCodeAt(i) === NL) line++;
31
- return line;
27
+ /** Where every newline of `src` is, in order: one pass, then each line is a binary search. */
28
+ function newlines_of(src) {
29
+ const at = [];
30
+ for (let i = src.indexOf("\n"); i !== -1; i = src.indexOf("\n", i + 1)) at.push(i);
31
+ return at;
32
+ }
33
+ /** The line `index` is on: `base_line` plus the newlines before it. */
34
+ function line_at(newlines, index, base_line) {
35
+ let lo = 0;
36
+ let hi = newlines.length;
37
+ while (lo < hi) {
38
+ const mid = lo + hi >>> 1;
39
+ if (newlines[mid] < index) lo = mid + 1;
40
+ else hi = mid;
41
+ }
42
+ return base_line + lo;
32
43
  }
33
44
  function string_arg(node) {
34
45
  if (!node) return null;
@@ -51,6 +62,7 @@ function collect_from_source(src, id, file, base_line, out) {
51
62
  } else if (s.type === "ImportNamespaceSpecifier") namespaces.add(s.local.name);
52
63
  }
53
64
  if (locals.size === 0 && namespaces.size === 0) return;
65
+ let newlines;
54
66
  walk(result.program, { enter(n) {
55
67
  const node = n;
56
68
  if (node.type !== "CallExpression") return;
@@ -61,11 +73,12 @@ function collect_from_source(src, id, file, base_line, out) {
61
73
  if (!kind) return;
62
74
  const name = string_arg(node.arguments?.[0]);
63
75
  if (name == null) return;
76
+ newlines ??= newlines_of(src);
64
77
  out.push({
65
78
  name,
66
79
  kind,
67
80
  file,
68
- line: line_at(src, node.start ?? 0, base_line)
81
+ line: line_at(newlines, node.start ?? 0, base_line)
69
82
  });
70
83
  } });
71
84
  }
@@ -74,10 +87,11 @@ function collect_flag_sites(code, id, file) {
74
87
  const out = [];
75
88
  if (id.endsWith(".svelte")) {
76
89
  SCRIPT_BLOCK_RE.lastIndex = 0;
90
+ const newlines = newlines_of(code);
77
91
  let m;
78
92
  while (m = SCRIPT_BLOCK_RE.exec(code)) {
79
93
  const body_start = m.index + m[0].indexOf(">") + 1;
80
- collect_from_source(m[1], id + ".ts", file, line_at(code, body_start, 1), out);
94
+ collect_from_source(m[1], id + ".ts", file, line_at(newlines, body_start, 1), out);
81
95
  }
82
96
  } else collect_from_source(code, id, file, 1, out);
83
97
  return out;
@@ -3,6 +3,7 @@ import { unscoped_marker } from "../unscoped-css.js";
3
3
  import { blank_styles } from "./blank-styles.js";
4
4
  import { BarrelIndex } from "./debarrel/barrel.js";
5
5
  import { analyze_exports, analyze_imports } from "./debarrel/parse.js";
6
+ import { warn } from "../messages/build.js";
6
7
  import { compile, parse } from "svelte/compiler";
7
8
  import { walk } from "estree-walker";
8
9
  // src/compiler/fouc-css.ts
@@ -251,7 +252,10 @@ function compileFoucScopedCss(abs, source, opts = {}) {
251
252
  err ??= e;
252
253
  }
253
254
  const why = err.message.split("\n")[0];
254
- if (opts.keepScript) console.warn(`[ogygia] scoped CSS for ${abs} could not be compiled (${why}) — shipping its style bodies unscoped`);
255
+ if (opts.keepScript) warn("scoped_css_failed", {
256
+ file: abs,
257
+ reason: why
258
+ });
255
259
  const raw = extractRawStyleBodies(source);
256
260
  return raw ? unscoped_marker(abs, why) + raw : raw;
257
261
  }
@@ -1,6 +1,7 @@
1
1
  import { dir_entries, fs, path } from "./host.js";
2
2
  import "./region/transform.js";
3
3
  import { parse_module } from "./parse/oxc.js";
4
+ import { OgygiaError } from "../messages/build.js";
4
5
  import { createRequire } from "node:module";
5
6
  // src/compiler/kit.ts
6
7
  /**
@@ -168,7 +169,10 @@ function read_route_config_uncached(file, keys) {
168
169
  const key = property.key?.type === "Identifier" ? property.key.name : property.key?.value;
169
170
  if (typeof key !== "string" || !keys.includes(key)) continue;
170
171
  const value = literal_value(property.value);
171
- if (value === NOT_LITERAL) throw new Error(`[ogygia] ${file}: \`config.${key}\` must be a literal (true, false, a number, a string or null) — ogygia reads it at build time.`);
172
+ if (value === NOT_LITERAL) throw new OgygiaError("kit_config_not_literal", {
173
+ file,
174
+ key
175
+ });
172
176
  out[key] = value;
173
177
  }
174
178
  return out;
@@ -1,5 +1,6 @@
1
1
  import { dir_entries, fs, path } from "../host.js";
2
2
  import { islandDepsHandoffPath } from "./island-deps.js";
3
+ import { line, warn } from "../../messages/build.js";
3
4
  // src/compiler/link/build-output.ts
4
5
  /**
5
6
  * Whole-program build-output side effects — the work the adapter's `writeBundle` does AFTER the
@@ -36,8 +37,11 @@ function warn_content_leaks(bundle, root, is_island_path) {
36
37
  }
37
38
  if (leaks.length) {
38
39
  const all = [...new Set(leaks.flatMap((l) => l.modules))];
39
- const sample = all.slice(0, 5).map((m) => " " + path.relative(root, m.split("?")[0])).join("\n");
40
- console.warn(`[ogygia] content leaked into the CLIENT bundle: ${all.length} corpus module(s) (.svx/.md) shipped to the browser (in chunk '${leaks[0].chunk}').\n A content() collection was imported into client-shipped code — usually an island — which drags its eager import.meta.glob (every doc) in.\n Fix: keep the collection in a server-only module (or a .remote.ts) and feed islands DATA (refs) via props or a remote, never the collection itself.\n${sample}${all.length > 5 ? "\n …" : ""}`);
40
+ warn("content_leaked_to_client", {
41
+ count: all.length,
42
+ chunk: leaks[0].chunk,
43
+ modules: all.slice(0, 5).map((m) => path.relative(root, m.split("?")[0])).join("\n")
44
+ });
41
45
  }
42
46
  } catch {}
43
47
  }
@@ -106,7 +110,12 @@ function report_seed_shaping(map, bundle, program, root, file_of = (identity) =>
106
110
  const key_count = new Set(pinned.flatMap((e) => map.page_keys[e] ?? [])).size;
107
111
  const rel = (p) => p.startsWith(root) ? p.slice(root.length).replace(/^\//, "") : p;
108
112
  const lines = [];
109
- lines.push(`[ogygia] page seed: ${readers.length} island${readers.length === 1 ? "" : "s"} read page.data — ${pinned.length} pinned to ${key_count} key${key_count === 1 ? "" : "s"}, ${unpinned.length} ship all of it`);
113
+ lines.push(line("page_seed_report", {
114
+ islands: readers.length,
115
+ pinned: pinned.length,
116
+ keys: key_count,
117
+ unpinned: unpinned.length
118
+ }));
110
119
  if (unpinned.length) {
111
120
  const closure = (entry) => {
112
121
  const seen = /* @__PURE__ */ new Set();
@@ -3,6 +3,7 @@ import { og_member } from "./wire.js";
3
3
  import { parse_module } from "../parse/oxc.js";
4
4
  import { og_js_regions } from "../parse/scan.js";
5
5
  import { BuildCache } from "../../build-cache.js";
6
+ import { OgygiaError } from "../../messages/build.js";
6
7
  import { uneval } from "devalue";
7
8
  import { pathToFileURL } from "node:url";
8
9
  // src/compiler/macros/bake.ts
@@ -76,9 +77,15 @@ function scan(src, id, markup_exts) {
76
77
  if (n.type !== "CallExpression" || og_member(n.callee) !== "bake") return;
77
78
  const abs = off + n.start;
78
79
  const args = n.arguments ?? [];
79
- if (args.length !== 1) throw new Error(`[ogygia] ${id}:${line_of(src, abs)} — import.meta.og.bake(fn) takes exactly one argument (a function).`);
80
+ if (args.length !== 1) throw new OgygiaError("macro_bake_arity", {
81
+ file: id,
82
+ line: line_of(src, abs)
83
+ });
80
84
  const a = args[0];
81
- if (a.type !== "ArrowFunctionExpression" && a.type !== "FunctionExpression") throw new Error(`[ogygia] ${id}:${line_of(src, abs)} — import.meta.og.bake(fn): the argument must be a function (\`() => …\`).`);
85
+ if (a.type !== "ArrowFunctionExpression" && a.type !== "FunctionExpression") throw new OgygiaError("macro_bake_not_function", {
86
+ file: id,
87
+ line: line_of(src, abs)
88
+ });
82
89
  calls.push({
83
90
  start: abs,
84
91
  end: off + n.end,
@@ -270,7 +277,10 @@ async function rewrite_bake(src, id, opts) {
270
277
  });
271
278
  } catch (e) {
272
279
  const msg = e instanceof Error ? e.message : String(e);
273
- throw new Error(`[ogygia] import.meta.og.bake() in ${path.basename(id)}: evaluating the function failed — ${msg}. A baked function may use this module's IMPORTS and literals only (not its other local variables), and must not touch runtime-only modules ($app/*, the browser).`);
280
+ throw new OgygiaError("macro_bake_eval_failed", {
281
+ file: path.basename(id),
282
+ reason: msg
283
+ });
274
284
  }
275
285
  const edits = [];
276
286
  found.calls.forEach((c, i) => {
@@ -279,7 +289,11 @@ async function rewrite_bake(src, id, opts) {
279
289
  literal = uneval(results[i]);
280
290
  } catch (e) {
281
291
  const msg = e instanceof Error ? e.message : String(e);
282
- throw new Error(`[ogygia] ${id}:${line_of(src, c.start)} — import.meta.og.bake(): the result is not serializable (${msg}). bake produces DATA — return plain values (JSON + Date/Map/Set/RegExp/BigInt), never a function, Promise, or class instance.`);
292
+ throw new OgygiaError("macro_bake_not_serializable", {
293
+ file: id,
294
+ line: line_of(src, c.start),
295
+ reason: msg
296
+ });
283
297
  }
284
298
  edits.push({
285
299
  start: c.start,
@@ -1,6 +1,7 @@
1
1
  import { match_close, og_js_regions } from "../parse/scan.js";
2
2
  import { parse_module } from "../parse/oxc.js";
3
3
  import { og_member } from "./wire.js";
4
+ import { OgygiaError } from "../../messages/build.js";
4
5
  // src/compiler/macros/dollar.ts
5
6
  /**
6
7
  * `import.meta.og.$()` — the boundary mark for FUNCTIONS, as a compile construct.
@@ -121,7 +122,11 @@ function line_of(src, offset) {
121
122
  return line;
122
123
  }
123
124
  function misuse(id, src, offset, what) {
124
- throw new Error(`[ogygia] ${id}:${line_of(src, offset)} — ${what}. import.meta.og.$ is legal in exactly one shape: a direct call wrapping an inline function — import.meta.og.$((args) => { … }). It hoists that function so its VALUE can cross an island boundary as a handle.`);
125
+ throw new OgygiaError("macro_dollar_misuse", {
126
+ file: id,
127
+ line: line_of(src, offset),
128
+ problem: what
129
+ });
125
130
  }
126
131
  /** Names DECLARED anywhere inside `fn` (params, vars, inner fns, catch, destructuring). */
127
132
  function declared_names(fn) {
@@ -1,6 +1,7 @@
1
1
  import { find_og_calls, og_js_regions } from "../parse/scan.js";
2
2
  import { parse_module } from "../parse/oxc.js";
3
3
  import { og_member } from "./wire.js";
4
+ import { OgygiaError } from "../../messages/build.js";
4
5
  // src/compiler/macros/source.ts
5
6
  /**
6
7
  * `import.meta.og.source(fn)` — declared data sources, as a compile construct (the postrender
@@ -29,7 +30,11 @@ function line_of(src, offset) {
29
30
  return line;
30
31
  }
31
32
  function misuse(id, src, offset, what) {
32
- throw new Error(`[ogygia] ${id}:${line_of(src, offset)} — ${what}. import.meta.og.source is legal in exactly one position: \`export const <name> = import.meta.og.source(fn)\` (optionally \`(fn, { key })\`) at module top level. The compiler stamps the source id from that export's identity.`);
33
+ throw new OgygiaError("macro_source_misuse", {
34
+ file: id,
35
+ line: line_of(src, offset),
36
+ problem: what
37
+ });
33
38
  }
34
39
  function walk(node, visit) {
35
40
  visit(node);
@@ -1,6 +1,7 @@
1
1
  import { og_js_regions } from "../parse/scan.js";
2
2
  import { parse_module } from "../parse/oxc.js";
3
3
  import { og_member } from "./wire.js";
4
+ import { OgygiaError, warn } from "../../messages/build.js";
4
5
  // src/compiler/macros/store.ts
5
6
  /**
6
7
  * `import.meta.og.store()` — the store-factory assert mark, as a compile construct.
@@ -41,7 +42,11 @@ function line_of(src, offset) {
41
42
  return line;
42
43
  }
43
44
  function misuse(id, src, offset, what) {
44
- throw new Error(`[ogygia] ${id}:${line_of(src, offset)} — ${what}. import.meta.og.store is legal in exactly one shape: a direct call wrapping a store factory — import.meta.og.store((seed) => ({ subscribe, … })). It registers the factory under a build tag so islands rebuild the store THROUGH it (custom methods survive); the factory's first parameter is the seed value.`);
45
+ throw new OgygiaError("macro_store_misuse", {
46
+ file: id,
47
+ line: line_of(src, offset),
48
+ problem: what
49
+ });
45
50
  }
46
51
  /** Unwrap oxc's preserved parens. */
47
52
  function unparen(v) {
@@ -105,7 +110,10 @@ function rewrite_store(src, id, rel_id, markup_exts) {
105
110
  claimed.add(n.callee);
106
111
  if (fn.type !== "Identifier") {
107
112
  const bad = first_non_store_return(fn);
108
- if (bad !== void 0 && typeof console !== "undefined") console.warn(`[ogygia] ${id}:${line_of(src, region.offset + bad)} — this import.meta.og.store factory has a return that is provably not a store (undefined/null/primitive). Where that branch runs, the product crosses as plain data, unbranded. If it is an environment guard, return a store there too (e.g. writable(seed)) and guard the side effects inside the factory instead.`);
113
+ if (bad !== void 0 && typeof console !== "undefined") warn("macro_store_returns_non_store", {
114
+ file: id,
115
+ line: line_of(src, region.offset + bad)
116
+ });
109
117
  }
110
118
  const fn_text = region.code.slice(fn.start, fn.end);
111
119
  const tag = `${rel_id}#store${seq++}`;
@@ -1,5 +1,6 @@
1
1
  import { find_og_calls, og_js_regions } from "../parse/scan.js";
2
2
  import { parse_module } from "../parse/oxc.js";
3
+ import { OgygiaError } from "../../messages/build.js";
3
4
  // src/compiler/macros/wire.ts
4
5
  /**
5
6
  * `import.meta.og.wire()` — the transportable mark, as a compile construct. A class opts into
@@ -66,7 +67,11 @@ function line_of(src, offset) {
66
67
  }
67
68
  /** The strict-position build error. `id` + line point at the offending spelling. */
68
69
  function misuse(id, src, offset, what) {
69
- throw new Error(`[ogygia] ${id}:${line_of(src, offset)} — ${what}. import.meta.og.wire is legal in exactly one position: \`static wire = import.meta.og.wire({ encode, decode })\` inside a class body. The macro consumes that member and mints the symbol key; the construct is not a value.`);
70
+ throw new OgygiaError("macro_wire_misuse", {
71
+ file: id,
72
+ line: line_of(src, offset),
73
+ problem: what
74
+ });
70
75
  }
71
76
  /**
72
77
  * AST pass over one JS region. The ONLY legal shape rewrites; every other appearance of the
@@ -1,3 +1,4 @@
1
+ import { OgygiaError } from "../../messages/build.js";
1
2
  // src/compiler/parse/scan.ts
2
3
  /**
3
4
  * The string-aware macro SCANNER — the cheap-bailout front-end for `import.meta.og.*`.
@@ -152,15 +153,21 @@ function split_first_string(args, who) {
152
153
  let i = 0;
153
154
  while (i < args.length && WS_RE.test(args[i])) i++;
154
155
  const q = args[i];
155
- if (q !== "'" && q !== "\"" && q !== "`") throw new Error(`[ogygia] ${who}: the first argument must be a static string literal, got '${args.slice(0, 24)}…'`);
156
+ if (q !== "'" && q !== "\"" && q !== "`") throw new OgygiaError("call_arg_not_literal", {
157
+ call: who,
158
+ got: args.slice(0, 24)
159
+ });
156
160
  let j = i + 1;
157
161
  for (; j < args.length; j++) if (args[j] === "\\") j++;
158
162
  else if (args[j] === q) break;
159
163
  const value = args.slice(i + 1, j);
160
- if (q === "`" && TEMPLATE_INTERPOLATION_RE.test(value)) throw new Error(`[ogygia] ${who}: the first argument must be a static string literal — a template literal with \${…} interpolation is a runtime value the build cannot resolve.`);
164
+ if (q === "`" && TEMPLATE_INTERPOLATION_RE.test(value)) throw new OgygiaError("call_arg_interpolated", { call: who });
161
165
  let k = j + 1;
162
166
  while (k < args.length && WS_RE.test(args[k])) k++;
163
- if (k < args.length && args[k] !== ",") throw new Error(`[ogygia] ${who}: the first argument must be a static string literal, but it is part of a larger expression (near '${args.slice(i, k + 8)}…'). Use one plain string.`);
167
+ if (k < args.length && args[k] !== ",") throw new OgygiaError("call_arg_in_expression", {
168
+ call: who,
169
+ near: args.slice(i, k + 8)
170
+ });
164
171
  if (args[k] === ",") k++;
165
172
  return {
166
173
  value,