@ogygia/core 0.11.4 → 0.12.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.
- package/README.md +1 -1
- package/dist/KeepHost.svelte +1 -1
- package/dist/LiveHost.svelte +1 -1
- package/dist/NestedProvider.svelte +1 -1
- package/dist/Region.svelte +28 -21
- package/dist/Region.svelte.d.ts +0 -2
- package/dist/SlotBoundary.svelte +1 -1
- package/dist/compiler/bom.js +1 -1
- package/dist/compiler/browser.d.ts +1 -1
- package/dist/compiler/browser.js +1 -1
- package/dist/compiler/ctx.d.ts +1 -1
- package/dist/compiler/ctx.js +3 -3
- package/dist/compiler/{macros/dedent.d.ts → dedent.d.ts} +12 -1
- package/dist/compiler/dedent.js +43 -0
- package/dist/compiler/driver.d.ts +9 -6
- package/dist/compiler/driver.js +69 -35
- package/dist/compiler/ids.d.ts +3 -0
- package/dist/compiler/ids.js +4 -1
- package/dist/compiler/index.d.ts +3 -3
- package/dist/compiler/index.js +3 -3
- package/dist/compiler/kit.d.ts +7 -0
- package/dist/compiler/kit.js +22 -10
- package/dist/compiler/link/caps.js +59 -9
- package/dist/compiler/link/island-deps.js +225 -22
- package/dist/compiler/link/location-map.d.ts +10 -0
- package/dist/compiler/link/location-map.js +53 -0
- package/dist/compiler/link/manifest.js +5 -1
- package/dist/compiler/link/runtime-entry.d.ts +19 -2
- package/dist/compiler/link/runtime-entry.js +34 -1
- package/dist/compiler/link/server-manifest.js +43 -7
- package/dist/compiler/link/transport.js +39 -7
- package/dist/compiler/macros/source.js +2 -2
- package/dist/compiler/program.d.ts +4 -0
- package/dist/compiler/program.js +5 -1
- package/dist/compiler/region/transform.d.ts +13 -4
- package/dist/compiler/region/transform.js +15 -6
- package/dist/context.d.ts +3 -12
- package/dist/context.js +1 -16
- package/dist/hooks.d.ts +16 -0
- package/dist/hooks.js +4 -2
- package/dist/insights/page-insights.js +2 -2
- package/dist/internal.d.ts +2 -1
- package/dist/internal.js +2 -1
- package/dist/nested.d.ts +11 -0
- package/dist/nested.js +19 -0
- package/dist/ref-sink.d.ts +15 -0
- package/dist/ref-sink.js +19 -0
- package/dist/ref.d.ts +2 -2
- package/dist/ref.js +1 -11
- package/dist/runtime/beacon.js +1 -1
- package/dist/runtime/boot-link.js +4 -1
- package/dist/runtime/core.js +10 -7
- package/dist/runtime/entry-locations.d.ts +10 -3
- package/dist/runtime/entry-locations.js +132 -21
- package/dist/runtime/full.js +5 -1
- package/dist/runtime/hydrate-core.d.ts +4 -4
- package/dist/runtime/hydrate-core.js +8 -5
- package/dist/runtime/morph.d.ts +7 -0
- package/dist/runtime/morph.js +21 -15
- package/dist/runtime/nav-handle.d.ts +3 -0
- package/dist/runtime/nav-handle.js +12 -5
- package/dist/runtime/ownership.d.ts +10 -1
- package/dist/runtime/ownership.js +19 -1
- package/dist/runtime/reconcile.d.ts +5 -2
- package/dist/runtime/reconcile.js +52 -16
- package/dist/runtime/region-endpoint-url.js +1 -0
- package/dist/runtime/region-ids.js +2 -2
- package/dist/runtime/remote-seeds.js +14 -3
- package/dist/runtime/router-nav.js +15 -0
- package/dist/runtime/router.js +3 -0
- package/dist/runtime/seeds.d.ts +10 -5
- package/dist/runtime/seeds.js +16 -12
- package/dist/runtime/slots.d.ts +24 -2
- package/dist/runtime/slots.js +2 -0
- package/dist/runtime/wire-classes.d.ts +2 -0
- package/dist/runtime/wire-classes.js +10 -1
- package/dist/runtime/wire-format.d.ts +4 -1
- package/dist/runtime/wire-format.js +6 -1
- package/dist/seed-ref-reviver.d.ts +24 -0
- package/dist/seed-ref-reviver.js +44 -0
- package/dist/seed-refs.d.ts +2 -16
- package/dist/seed-refs.js +1 -33
- package/dist/server/document-tail.d.ts +3 -2
- package/dist/server/document-tail.js +4 -3
- package/dist/server/entry-location.js +11 -8
- package/dist/server/props-wire.js +4 -3
- package/dist/server/signing-key.d.ts +10 -2
- package/dist/server/signing-key.js +49 -15
- package/dist/vite/index.js +5 -4
- package/dist/vite/kit-dirs.js +1 -1
- package/package.json +3 -3
- package/dist/compiler/link/entry-shim.d.ts +0 -4
- package/dist/compiler/link/entry-shim.js +0 -29
- package/dist/compiler/macros/dedent.js +0 -36
|
@@ -12,6 +12,11 @@ const FEATURES = {
|
|
|
12
12
|
deps: [],
|
|
13
13
|
detect: (m) => m.forms === true || m.forms !== false && m.router === true
|
|
14
14
|
},
|
|
15
|
+
"mpa-nav": {
|
|
16
|
+
module: "nav-handle.js",
|
|
17
|
+
deps: [],
|
|
18
|
+
detect: (m) => m.page !== false
|
|
19
|
+
},
|
|
15
20
|
router: {
|
|
16
21
|
module: "router.js",
|
|
17
22
|
deps: [],
|
|
@@ -39,12 +44,23 @@ const FEATURES = {
|
|
|
39
44
|
phase: "hydrate",
|
|
40
45
|
detect: (m) => m.wire === true
|
|
41
46
|
},
|
|
47
|
+
"wire-classes": {
|
|
48
|
+
module: "wire-classes.js",
|
|
49
|
+
deps: [],
|
|
50
|
+
detect: (m) => m.wire === true
|
|
51
|
+
},
|
|
42
52
|
"remote-seeds": {
|
|
43
53
|
module: "remote-seeds.js",
|
|
44
54
|
deps: [],
|
|
45
55
|
phase: "hydrate",
|
|
46
56
|
detect: (m) => m.remoteSeeds !== false
|
|
47
57
|
},
|
|
58
|
+
page: {
|
|
59
|
+
module: "seeds.js",
|
|
60
|
+
deps: [],
|
|
61
|
+
phase: "hydrate",
|
|
62
|
+
detect: (m) => m.page !== false
|
|
63
|
+
},
|
|
48
64
|
frames: {
|
|
49
65
|
module: "frames.js",
|
|
50
66
|
deps: [],
|
|
@@ -59,13 +75,16 @@ const FEATURES = {
|
|
|
59
75
|
};
|
|
60
76
|
const FEATURE_ORDER = [
|
|
61
77
|
"remote-seeds",
|
|
78
|
+
"page",
|
|
62
79
|
"wire",
|
|
80
|
+
"wire-classes",
|
|
63
81
|
"frames",
|
|
64
82
|
"lakes",
|
|
65
83
|
"morph",
|
|
66
84
|
"live",
|
|
67
85
|
"interaction",
|
|
68
86
|
"forms",
|
|
87
|
+
"mpa-nav",
|
|
69
88
|
"router",
|
|
70
89
|
"context"
|
|
71
90
|
];
|
|
@@ -129,4 +148,18 @@ function generateHydrateFeaturesSource(marks, runtimeDir, all = false) {
|
|
|
129
148
|
features
|
|
130
149
|
};
|
|
131
150
|
}
|
|
132
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Emit `virtual:ogygia/runtime-flags`: build-time constants for code that lives INSIDE the always-on
|
|
153
|
+
* region element rather than in a feature module, because it works on the element's own state. The
|
|
154
|
+
* bundler inlines an imported `const` and drops the branches it switches off, so the code a flag
|
|
155
|
+
* guards ships exactly when the feature it serves does. Same marks as the entries; `all` (dev / no
|
|
156
|
+
* marks): every flag on.
|
|
157
|
+
*
|
|
158
|
+
* - `FRAMES`: the `frames` feature is in the build — some region streams HTML (a deferred hole, a
|
|
159
|
+
* live region, a lake). Without the frame store a region's fetch has nowhere to land, so the
|
|
160
|
+
* element's fetch / apply / morph / renew path is dead code then.
|
|
161
|
+
*/
|
|
162
|
+
function generateRuntimeFlagsSource(marks, all = false) {
|
|
163
|
+
return `export const FRAMES = ${all || resolveFeatures(marks).includes("frames")};\n`;
|
|
164
|
+
}
|
|
165
|
+
export { FEATURES, FEATURE_ORDER, generateHydrateFeaturesSource, generateRuntimeEntrySource, generateRuntimeFlagsSource, phaseFeatures, resolveFeatures };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { V_SERVER_ISLANDS } from "../ids.js";
|
|
2
|
+
import { dedent } from "../dedent.js";
|
|
2
3
|
// src/compiler/link/server-manifest.ts
|
|
3
4
|
/** The SERVER-island ids the manifest carries right now (deferred / server-picked regions). */
|
|
4
5
|
function server_island_ids(program) {
|
|
@@ -7,7 +8,14 @@ function server_island_ids(program) {
|
|
|
7
8
|
return ids;
|
|
8
9
|
}
|
|
9
10
|
function server_manifest_module(ssr, program, is_dev, devUrlFor, publicUrlFor) {
|
|
10
|
-
if (!ssr) return `
|
|
11
|
+
if (!ssr) return dedent`
|
|
12
|
+
export const load_islands = () => Promise.resolve({ islands: {} });
|
|
13
|
+
export const island_url = {};
|
|
14
|
+
export const island_name = {};
|
|
15
|
+
export const island_render = {};
|
|
16
|
+
export const render_island = {};
|
|
17
|
+
export const island_reads_page_data = null;
|
|
18
|
+
`;
|
|
11
19
|
const urls = [];
|
|
12
20
|
const names = [];
|
|
13
21
|
const renders = [];
|
|
@@ -17,25 +25,53 @@ function server_manifest_module(ssr, program, is_dev, devUrlFor, publicUrlFor) {
|
|
|
17
25
|
const reg = program.registry.get(virtualPath);
|
|
18
26
|
if (!reg?.server) continue;
|
|
19
27
|
if (reg.renderId) {
|
|
20
|
-
renders.push(
|
|
28
|
+
renders.push(`${JSON.stringify(iid)}: ${JSON.stringify(reg.renderId)}`);
|
|
21
29
|
const seen = by_render.get(reg.renderId);
|
|
22
30
|
if (seen === void 0 || iid < seen) by_render.set(reg.renderId, iid);
|
|
23
31
|
}
|
|
24
32
|
const file = reg.componentPath ?? "";
|
|
25
33
|
const base = file.slice(file.lastIndexOf("/") + 1);
|
|
26
|
-
if (base.endsWith(".svelte")) names.push(
|
|
27
|
-
urls.push(
|
|
34
|
+
if (base.endsWith(".svelte")) names.push(`${JSON.stringify(iid)}: ${JSON.stringify(base.slice(0, -7))}`);
|
|
35
|
+
urls.push(`${JSON.stringify(iid)}: ${JSON.stringify(is_dev ? devUrlFor(virtualPath) : publicUrlFor(iid))}`);
|
|
28
36
|
}
|
|
29
|
-
|
|
37
|
+
const entries = [...by_render].map(([r, iid]) => `${JSON.stringify(r)}: ${JSON.stringify(iid)}`);
|
|
38
|
+
return dedent`
|
|
39
|
+
export const load_islands = () => import(${JSON.stringify(V_SERVER_ISLANDS)});
|
|
40
|
+
|
|
41
|
+
export const island_url = {
|
|
42
|
+
${urls.join(",\n ")}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const island_name = {
|
|
46
|
+
${names.join(",\n ")}
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const island_render = {
|
|
50
|
+
${renders.join(",\n ")}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export const render_island = {
|
|
54
|
+
${entries.join(",\n ")}
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// id → does the hole's SERVER tree read \`page.data\` (the page lookup a hole needs: server/
|
|
58
|
+
// render-page.ts). Known only once every module is transformed: a build patches the
|
|
59
|
+
// placeholder in renderChunk (driver \`patch_page_data_reads\`); dev leaves \`null\` (unknown → look up).
|
|
60
|
+
export const island_reads_page_data = ${is_dev ? "null" : PAGE_DATA_READS_PLACEHOLDER};
|
|
61
|
+
`;
|
|
30
62
|
}
|
|
31
63
|
/** `virtual:ogygia/server-islands`: each SERVER island's loader, `id → () => import(entry)` (empty on
|
|
32
64
|
* the client). Imported only by the manifest's `load_islands()` — never statically. */
|
|
33
65
|
function server_islands_module(ssr, program) {
|
|
34
66
|
const entries = [];
|
|
35
67
|
if (ssr) {
|
|
36
|
-
for (const [iid, virtualPath] of program.by_id) if (program.registry.get(virtualPath)?.server) entries.push(
|
|
68
|
+
for (const [iid, virtualPath] of program.by_id) if (program.registry.get(virtualPath)?.server) entries.push(`${JSON.stringify(iid)}: () => import(${JSON.stringify(virtualPath)})`);
|
|
37
69
|
}
|
|
38
|
-
return `
|
|
70
|
+
return dedent`
|
|
71
|
+
export const islands = {
|
|
72
|
+
${entries.join(",\n ")}
|
|
73
|
+
};
|
|
74
|
+
`;
|
|
39
75
|
}
|
|
40
76
|
/** The token the build replaces with its answer (driver `patch_page_data_reads`), inside a string
|
|
41
77
|
* literal the bundler cannot fold: a plain `null` placeholder was inlined into every reader as the
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { OWN_SPECIFIER_SOURCE } from "../../own-package.js";
|
|
2
|
+
import { dedent } from "../dedent.js";
|
|
2
3
|
// src/compiler/link/transport.ts
|
|
3
4
|
/**
|
|
4
5
|
* `virtual:ogygia/transport` emitter — re-exports the app's universal `transport` hook (or an empty
|
|
@@ -6,10 +7,29 @@ import { OWN_SPECIFIER_SOURCE } from "../../own-package.js";
|
|
|
6
7
|
* Pure over the resolved absolute path to the app's universal hooks module (or null).
|
|
7
8
|
*/
|
|
8
9
|
function transport_module(universal_hooks, kit_codec = null) {
|
|
9
|
-
if (!universal_hooks) return `
|
|
10
|
-
|
|
10
|
+
if (!universal_hooks) return dedent`
|
|
11
|
+
export const transport = {};
|
|
12
|
+
export const encoders = {};
|
|
13
|
+
export const decoders = {};
|
|
14
|
+
`;
|
|
15
|
+
const src = dedent`
|
|
16
|
+
import * as hooks from ${JSON.stringify(universal_hooks)};
|
|
17
|
+
|
|
18
|
+
export const transport = hooks.transport || {};
|
|
19
|
+
const codecs = Object.entries(transport);
|
|
20
|
+
export const encoders = Object.fromEntries(codecs.map(([k, v]) => [k, v.encode]));
|
|
21
|
+
export const decoders = Object.fromEntries(codecs.map(([k, v]) => [k, v.decode]));
|
|
22
|
+
`;
|
|
11
23
|
if (!kit_codec) return src;
|
|
12
|
-
return `
|
|
24
|
+
return dedent`
|
|
25
|
+
import { init_transport } from ${JSON.stringify(kit_codec)};
|
|
26
|
+
${src}
|
|
27
|
+
|
|
28
|
+
// Kit 3 (client leg): Kit's own codec module is a reader too — its remote argument encoding
|
|
29
|
+
// reads it, and only Kit's client boot fills it otherwise (never on a csr=false page). Kit's
|
|
30
|
+
// boot later fills it again with the same transport.
|
|
31
|
+
init_transport(transport);
|
|
32
|
+
`;
|
|
13
33
|
}
|
|
14
34
|
/**
|
|
15
35
|
* `virtual:ogygia/kit-transport` emitter — the value behind the barrel's `ogygia.transport` (the Kit
|
|
@@ -22,8 +42,10 @@ function transport_module(universal_hooks, kit_codec = null) {
|
|
|
22
42
|
* `transport_spec` is the resolvable path to ogygia's own `transport.js` (dist).
|
|
23
43
|
*/
|
|
24
44
|
function kit_transport_module(app_crosses_wire, transport_spec) {
|
|
25
|
-
if (!app_crosses_wire) return `export const transport = {}
|
|
26
|
-
return `
|
|
45
|
+
if (!app_crosses_wire) return `export const transport = {};`;
|
|
46
|
+
return dedent`
|
|
47
|
+
export { ogygiaTransport as transport } from ${JSON.stringify(transport_spec)};
|
|
48
|
+
`;
|
|
27
49
|
}
|
|
28
50
|
const OGYGIA_WIRE_NAMED = new RegExp(`import\\s+(?:type\\s+)?\\{([\\s\\S]*?)\\}\\s*from\\s*['"]${OWN_SPECIFIER_SOURCE}['"]`, "g");
|
|
29
51
|
const WIRE_API = /\b(?:region|Region|site)\b/;
|
|
@@ -58,10 +80,20 @@ function transportables_module(modules, ssr, rel, crosses_wire = false) {
|
|
|
58
80
|
if (ssr) {
|
|
59
81
|
const imports = [];
|
|
60
82
|
for (const abs of modules) imports.push(`import ${JSON.stringify(abs)};`);
|
|
61
|
-
return
|
|
83
|
+
return dedent`
|
|
84
|
+
${imports.join("\n")}
|
|
85
|
+
|
|
86
|
+
export const wire_loaders = {};
|
|
87
|
+
export const wire_all = false;
|
|
88
|
+
`;
|
|
62
89
|
}
|
|
63
90
|
const entries = [];
|
|
64
91
|
for (const abs of modules) entries.push(`${JSON.stringify(rel(abs))}: () => import(${JSON.stringify(abs)})`);
|
|
65
|
-
return `
|
|
92
|
+
return dedent`
|
|
93
|
+
export const wire_loaders = {
|
|
94
|
+
${entries.join(",\n ")}
|
|
95
|
+
};
|
|
96
|
+
export const wire_all = ${crosses_wire && entries.length > 0};
|
|
97
|
+
`;
|
|
66
98
|
}
|
|
67
99
|
export { kit_transport_module, source_crosses_wire, transport_module, transportables_module };
|
|
@@ -6,12 +6,12 @@ import { og_member } from "./wire.js";
|
|
|
6
6
|
* `import.meta.og.source(fn)` — declared data sources, as a compile construct (the postrender
|
|
7
7
|
* reverse index; internal/notes/freeze.md §L2):
|
|
8
8
|
*
|
|
9
|
-
* export const
|
|
9
|
+
* export const loadCmsContent = import.meta.og.source(async (event, category) => { … });
|
|
10
10
|
*
|
|
11
11
|
* The macro stamps the id from THIS definition's `file#export` (root-relative; the ogygia.files
|
|
12
12
|
* identity rail applies to package paths upstream) and rewrites to
|
|
13
13
|
* `__og_source('<id>', fn, opts?)` with the runtime-wrapper import injected. Loads stay
|
|
14
|
-
* untouched plain TS forever; `postrender.invalidate(
|
|
14
|
+
* untouched plain TS forever; `postrender.invalidate(loadCmsContent, [args])` reads the stamp
|
|
15
15
|
* off the function — no strings anywhere.
|
|
16
16
|
*
|
|
17
17
|
* STRICT BY CONSTRUCTION — legal in exactly ONE position:
|
|
@@ -120,6 +120,10 @@ export declare class Program {
|
|
|
120
120
|
* Set during the prescan walk; with `runtime_marks.wire` it decides whether `ogygia.transport`
|
|
121
121
|
* (the generated `virtual:ogygia/kit-transport`) carries the codec cluster or is an empty map. */
|
|
122
122
|
crosses_wire: boolean;
|
|
123
|
+
/** Island code can reach Kit's page world (`$app/state` and kin, or something the island-closure
|
|
124
|
+
* walk could not follow — driver.ts `mark_island_closure`). Folded into `runtime_marks.page` at
|
|
125
|
+
* the end of the prescan, with the other ways islands the walk never saw reach the runtime. */
|
|
126
|
+
island_reads_page: boolean;
|
|
123
127
|
/** Build-time capability marks for the sticky runtime entry. Incomplete → kitchen-sink. */
|
|
124
128
|
readonly runtime_marks: RuntimeMarks;
|
|
125
129
|
/** The resolved feature set folded into the runtime chunk name (busts the immutable URL when the
|
package/dist/compiler/program.js
CHANGED
|
@@ -72,6 +72,10 @@ var Program = class {
|
|
|
72
72
|
* Set during the prescan walk; with `runtime_marks.wire` it decides whether `ogygia.transport`
|
|
73
73
|
* (the generated `virtual:ogygia/kit-transport`) carries the codec cluster or is an empty map. */
|
|
74
74
|
crosses_wire = false;
|
|
75
|
+
/** Island code can reach Kit's page world (`$app/state` and kin, or something the island-closure
|
|
76
|
+
* walk could not follow — driver.ts `mark_island_closure`). Folded into `runtime_marks.page` at
|
|
77
|
+
* the end of the prescan, with the other ways islands the walk never saw reach the runtime. */
|
|
78
|
+
island_reads_page = false;
|
|
75
79
|
/** Build-time capability marks for the sticky runtime entry. Incomplete → kitchen-sink. */
|
|
76
80
|
runtime_marks;
|
|
77
81
|
/** The resolved feature set folded into the runtime chunk name (busts the immutable URL when the
|
|
@@ -84,7 +88,7 @@ var Program = class {
|
|
|
84
88
|
complete: false,
|
|
85
89
|
forms: init.forms,
|
|
86
90
|
wire: false,
|
|
87
|
-
remoteSeeds:
|
|
91
|
+
remoteSeeds: false,
|
|
88
92
|
hydrate: [],
|
|
89
93
|
defer: [],
|
|
90
94
|
persistKeys: [],
|
|
@@ -110,12 +110,21 @@ export declare function import_keys_hint(import_keys: ImportKeys): RegExp;
|
|
|
110
110
|
* Paths are always posix so SSR/client builds agree across OS path separators. */
|
|
111
111
|
export declare function islandId(relHostPath: string, index: string | number, salt?: string): string;
|
|
112
112
|
/**
|
|
113
|
-
*
|
|
114
|
-
*
|
|
115
|
-
*
|
|
113
|
+
* The directory, under the app dir, of the STABLE names (an island's identity, the runtime's): not
|
|
114
|
+
* `immutable/`. A host serves `immutable/` for a year by name (every adapter, every CDN rule written
|
|
115
|
+
* for it), and a stable name keeps its name while its content follows each build: a year-old copy
|
|
116
|
+
* would load another build's code. Here a host's ordinary rule revalidates them. The content-hashed
|
|
117
|
+
* files they point at stay in `immutable/`.
|
|
118
|
+
*/
|
|
119
|
+
export declare const STABLE_NAME_DIR = "ogygia";
|
|
120
|
+
/**
|
|
121
|
+
* Deterministic client path of a hydrate island's stable name (mirrors the runtime's). SSR bakes it
|
|
122
|
+
* into `<ogygia-region entry>` as the island's identity — a name, not a file: the content-hashed
|
|
123
|
+
* entry is found through the handoff (the server) and the location maps (the browser,
|
|
124
|
+
* link/location-map.ts), since Kit builds server before client.
|
|
116
125
|
*/
|
|
117
126
|
export declare function islandChunkFileName(iid: string, appDir?: string): string;
|
|
118
|
-
/** App-internal URL for {@link islandChunkFileName}: `/<appDir>/
|
|
127
|
+
/** App-internal URL for {@link islandChunkFileName}: `/<appDir>/ogygia/…` (leading slash, appDir,
|
|
119
128
|
* NO base). It is what SSR bakes onto `<ogygia-region entry>`; `Region.svelte` runs it through Kit's
|
|
120
129
|
* `asset()` at render time, which is the sole authority on `base` / the assets CDN / relative paths —
|
|
121
130
|
* so a base is never baked here (that would double-apply it). NOT the on-disk output filename. */
|
|
@@ -200,14 +200,23 @@ function islandId(relHostPath, index, salt = "") {
|
|
|
200
200
|
return createHash("md5").update(msg).digest("hex").slice(0, 12);
|
|
201
201
|
}
|
|
202
202
|
/**
|
|
203
|
-
*
|
|
204
|
-
*
|
|
205
|
-
*
|
|
203
|
+
* The directory, under the app dir, of the STABLE names (an island's identity, the runtime's): not
|
|
204
|
+
* `immutable/`. A host serves `immutable/` for a year by name (every adapter, every CDN rule written
|
|
205
|
+
* for it), and a stable name keeps its name while its content follows each build: a year-old copy
|
|
206
|
+
* would load another build's code. Here a host's ordinary rule revalidates them. The content-hashed
|
|
207
|
+
* files they point at stay in `immutable/`.
|
|
208
|
+
*/
|
|
209
|
+
const STABLE_NAME_DIR = "ogygia";
|
|
210
|
+
/**
|
|
211
|
+
* Deterministic client path of a hydrate island's stable name (mirrors the runtime's). SSR bakes it
|
|
212
|
+
* into `<ogygia-region entry>` as the island's identity — a name, not a file: the content-hashed
|
|
213
|
+
* entry is found through the handoff (the server) and the location maps (the browser,
|
|
214
|
+
* link/location-map.ts), since Kit builds server before client.
|
|
206
215
|
*/
|
|
207
216
|
function islandChunkFileName(iid, appDir = "_app") {
|
|
208
|
-
return `${appDir}/
|
|
217
|
+
return `${appDir}/${STABLE_NAME_DIR}/og-region.${iid}.js`;
|
|
209
218
|
}
|
|
210
|
-
/** App-internal URL for {@link islandChunkFileName}: `/<appDir>/
|
|
219
|
+
/** App-internal URL for {@link islandChunkFileName}: `/<appDir>/ogygia/…` (leading slash, appDir,
|
|
211
220
|
* NO base). It is what SSR bakes onto `<ogygia-region entry>`; `Region.svelte` runs it through Kit's
|
|
212
221
|
* `asset()` at render time, which is the sole authority on `base` / the assets CDN / relative paths —
|
|
213
222
|
* so a base is never baked here (that would double-apply it). NOT the on-disk output filename. */
|
|
@@ -1930,4 +1939,4 @@ var TsRegionCompilation = class {
|
|
|
1930
1939
|
};
|
|
1931
1940
|
}
|
|
1932
1941
|
};
|
|
1933
|
-
export { CLIENT_BINDING_STUB, DEFAULT_IMPORT_KEYS, ISLAND_DIR, asRegionLocals, foucCssVirtualId, import_keys_hint, is_island_path, islandChunkFileName, islandId, islandPublicUrl, normalize_hydrate_value, normalize_import_keys, normalize_region_value, regionBindingVirtualId, regionId, regionIdentity, strategyKey, transformHost, transformTsRegions, wrapperVirtualId };
|
|
1942
|
+
export { CLIENT_BINDING_STUB, DEFAULT_IMPORT_KEYS, ISLAND_DIR, STABLE_NAME_DIR, asRegionLocals, foucCssVirtualId, import_keys_hint, is_island_path, islandChunkFileName, islandId, islandPublicUrl, normalize_hydrate_value, normalize_import_keys, normalize_region_value, regionBindingVirtualId, regionId, regionIdentity, strategyKey, transformHost, transformTsRegions, wrapperVirtualId };
|
package/dist/context.d.ts
CHANGED
|
@@ -1,14 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Mark the current subtree's hydration state. `true` = inside a hydrated island (nested islands
|
|
3
|
-
* degrade). `false` = a LAKE resets its subtree to "dead", so an island inside the lake self-
|
|
4
|
-
* hydrates again (the nearest-boundary rule — DESIGN.md).
|
|
5
|
-
*/
|
|
6
|
-
export declare function setNested(value?: boolean): void;
|
|
7
|
-
/**
|
|
8
|
-
* True when an ancestor island wrapper already marked the subtree. `getContext` returns `undefined`
|
|
9
|
-
* when no ancestor set it (a top-level island) — that absence is exactly "not nested".
|
|
10
|
-
*/
|
|
11
|
-
export declare function isNested(): boolean;
|
|
1
|
+
import { isNested, setNested } from "./nested.js";
|
|
12
2
|
/** Mark the current subtree as opaque to Kit — a lake's inside (LakeBoundary). */
|
|
13
3
|
export declare function setInLake(): void;
|
|
14
4
|
/** True when the subtree is opaque to Kit (a lake's inside, or a held region's render). */
|
|
@@ -69,4 +59,5 @@ export declare function claim_region_css(hrefs: string[]): string[];
|
|
|
69
59
|
* injects the same script into `<head>` (presence-checked) on pages that have no island to emit it.
|
|
70
60
|
* Client / no-request → false (SSR already emitted).
|
|
71
61
|
*/
|
|
72
|
-
export declare function claimRuntimeEmit(): boolean;
|
|
62
|
+
export declare function claimRuntimeEmit(): boolean;
|
|
63
|
+
export { isNested, setNested };
|
package/dist/context.js
CHANGED
|
@@ -1,25 +1,10 @@
|
|
|
1
1
|
import { kit_hydrates_page } from "./runtime/kit-boot.js";
|
|
2
|
+
import { isNested, setNested } from "./nested.js";
|
|
2
3
|
import { getContext, setContext } from "svelte";
|
|
3
4
|
import { BROWSER } from "esm-env";
|
|
4
5
|
import { getRequestEvent } from "virtual:ogygia/request-event";
|
|
5
6
|
import { csr_true_routes, error_csr_true_routes, root_layout_csr_true } from "virtual:ogygia/route-csr";
|
|
6
7
|
// src/context.ts
|
|
7
|
-
const NESTED_KEY = Symbol.for("ogygia.nested-island");
|
|
8
|
-
/**
|
|
9
|
-
* Mark the current subtree's hydration state. `true` = inside a hydrated island (nested islands
|
|
10
|
-
* degrade). `false` = a LAKE resets its subtree to "dead", so an island inside the lake self-
|
|
11
|
-
* hydrates again (the nearest-boundary rule — DESIGN.md).
|
|
12
|
-
*/
|
|
13
|
-
function setNested(value = true) {
|
|
14
|
-
setContext(NESTED_KEY, value);
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* True when an ancestor island wrapper already marked the subtree. `getContext` returns `undefined`
|
|
18
|
-
* when no ancestor set it (a top-level island) — that absence is exactly "not nested".
|
|
19
|
-
*/
|
|
20
|
-
function isNested() {
|
|
21
|
-
return getContext(NESTED_KEY) === true;
|
|
22
|
-
}
|
|
23
8
|
const LAKE_KEY = Symbol.for("ogygia.lake-subtree");
|
|
24
9
|
/** Mark the current subtree as opaque to Kit — a lake's inside (LakeBoundary). */
|
|
25
10
|
function setInLake() {
|
package/dist/hooks.d.ts
CHANGED
|
@@ -17,6 +17,22 @@ export interface OgygiaHandleOptions {
|
|
|
17
17
|
* (`/_app/…`): Kit answers those itself, before any handle runs.
|
|
18
18
|
*/
|
|
19
19
|
endpoint?: string;
|
|
20
|
+
/**
|
|
21
|
+
* The secret that signs region capabilities (hole and live-region URLs). Takes precedence over
|
|
22
|
+
* the `OGYGIA_SECRET` environment variable, for a host whose server process never sees its
|
|
23
|
+
* runtime env. Pass a build-time private env value:
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* import { OGYGIA_SECRET } from '$env/static/private';
|
|
27
|
+
* export const handle = ogygia.handle({ secret: OGYGIA_SECRET });
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* An empty or blank value counts as not given (`OGYGIA_SECRET`, then a per-build random key, sign
|
|
31
|
+
* instead). It signs exactly as the same value in `OGYGIA_SECRET` does, so pages from either
|
|
32
|
+
* verify on the other. In production it needs ≥16 UTF-8 bytes. Every deployment that serves your
|
|
33
|
+
* pages or your islands endpoint needs the same secret.
|
|
34
|
+
*/
|
|
35
|
+
secret?: string;
|
|
20
36
|
/**
|
|
21
37
|
* Post-process the HTML ogygia sends — every document (the chunk carrying `</head>`; a streamed
|
|
22
38
|
* tail passes untouched) and every region answer (a hole, a lake remount) — e.g. a web-component
|
package/dist/hooks.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { REGION_BRAND } from "./region-brand.js";
|
|
2
|
-
import { signing_key } from "./server/signing-key.js";
|
|
2
|
+
import { configure_signing_secret, signing_key } from "./server/signing-key.js";
|
|
3
3
|
import { mpaSpeculationRules } from "./compiler/link/speculation.js";
|
|
4
4
|
import { set_page_route_config } from "./plugin/routes.js";
|
|
5
5
|
import { kit_page_facts_tail, kit_page_ready, kit_render_context, set_kit_event_reader, set_kit_page_reader, set_render_page_port } from "./server/kit-context.js";
|
|
@@ -9,7 +9,7 @@ import { absolutize_hole_html } from "./server/hole-urls.js";
|
|
|
9
9
|
import { is_keep_fallback } from "./keep-fallback.js";
|
|
10
10
|
import { set_source_recorder } from "./source.js";
|
|
11
11
|
import { kit_asset } from "./kit-paths.js";
|
|
12
|
-
import { region_mac_message, sign, verify } from "./server/hmac.js";
|
|
12
|
+
import { region_mac_message, secret_has_min_entropy, sign, verify } from "./server/hmac.js";
|
|
13
13
|
import { expiry_refusal, note_cache_control } from "./server/shared-cache.js";
|
|
14
14
|
import { app_lifetime } from "./server/region-endpoint.js";
|
|
15
15
|
import { cached_render, render_cache_key } from "./server/render-cache.js";
|
|
@@ -435,6 +435,8 @@ var OgygiaHandle = class {
|
|
|
435
435
|
this.#transform = options.transform;
|
|
436
436
|
configure_islands_endpoint(options.endpoint);
|
|
437
437
|
this.#endpoint = islands_endpoint();
|
|
438
|
+
if (options.secret?.trim() && !import.meta.env.DEV && !secret_has_min_entropy(options.secret)) throw new Error(`[ogygia] handle({ secret }) is too short for production (need ≥16 UTF-8 bytes).`);
|
|
439
|
+
configure_signing_secret(options.secret);
|
|
438
440
|
this.#speculation_rules = enabled ? "" : mpaSpeculationRules(this.#endpoint);
|
|
439
441
|
this.render_rate = new RateLimiter({
|
|
440
442
|
max: rateLimit.max,
|
|
@@ -1267,8 +1267,8 @@ function analyze_page(page, regions, failures = [], now = Infinity, cpu = null)
|
|
|
1267
1267
|
const woke = gone.filter((f) => f.recovered);
|
|
1268
1268
|
const dead = gone.filter((f) => !f.recovered);
|
|
1269
1269
|
const parts = [];
|
|
1270
|
-
if (woke.length) parts.push(`${list(woke.map(name))} woke on the current build's code
|
|
1271
|
-
if (dead.length) parts.push(`${list(dead.map(name))} stayed asleep: ${dead.length === 1 ? "
|
|
1270
|
+
if (woke.length) parts.push(`${list(woke.map(name))} woke on the current build's code, found in its \`_app/ogygia.json\`: the page's HTML and ${woke.length === 1 ? "that island" : "those islands"} now come from different builds`);
|
|
1271
|
+
if (dead.length) parts.push(`${list(dead.map(name))} stayed asleep: the current build's file failed too, or \`_app/ogygia.json\` did not name ${dead.length === 1 ? "it" : "them"}`);
|
|
1272
1272
|
findings.push({
|
|
1273
1273
|
code: "island-file-gone",
|
|
1274
1274
|
severity: dead.length ? "error" : "warn",
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { og_entry_hydrate, og_entry_unmount } from "./entry-hydrate.js";
|
|
2
|
-
import { documentIsCsrTrue
|
|
2
|
+
import { documentIsCsrTrue } from "./context.js";
|
|
3
3
|
import { __register_transportable } from "./live-transport.js";
|
|
4
|
+
import { isNested, setNested } from "./nested.js";
|
|
4
5
|
import { __og_$, __register_fn } from "./fn-transport.js";
|
|
5
6
|
import { __og_store, __register_store_factory, mark_store } from "./store-transport.js";
|
|
6
7
|
import { __og_boundary, configure_boundary } from "./boundary.js";
|
package/dist/internal.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { og_entry_hydrate, og_entry_unmount } from "./entry-hydrate.js";
|
|
2
|
-
import { documentIsCsrTrue
|
|
2
|
+
import { documentIsCsrTrue } from "./context.js";
|
|
3
3
|
import { __register_transportable } from "./live-transport.js";
|
|
4
4
|
import { __og_$, __register_fn } from "./fn-transport.js";
|
|
5
5
|
import { __og_store, __register_store_factory, mark_store } from "./store-transport.js";
|
|
6
6
|
import { __og_boundary, configure_boundary } from "./boundary.js";
|
|
7
|
+
import { isNested, setNested } from "./nested.js";
|
|
7
8
|
import { __og_source } from "./source.js";
|
|
8
9
|
import { __tag_context } from "./context-bridge.js";
|
|
9
10
|
import { og_portable } from "./region-snippet.js";
|
package/dist/nested.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mark the current subtree's hydration state. `true` = inside a hydrated island (nested islands
|
|
3
|
+
* degrade). `false` = a LAKE resets its subtree to "dead", so an island inside the lake self-
|
|
4
|
+
* hydrates again (the nearest-boundary rule — DESIGN.md).
|
|
5
|
+
*/
|
|
6
|
+
export declare function setNested(value?: boolean): void;
|
|
7
|
+
/**
|
|
8
|
+
* True when an ancestor island wrapper already marked the subtree. `getContext` returns `undefined`
|
|
9
|
+
* when no ancestor set it (a top-level island) — that absence is exactly "not nested".
|
|
10
|
+
*/
|
|
11
|
+
export declare function isNested(): boolean;
|
package/dist/nested.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getContext, setContext } from "svelte";
|
|
2
|
+
// src/nested.ts
|
|
3
|
+
const NESTED_KEY = Symbol.for("ogygia.nested-island");
|
|
4
|
+
/**
|
|
5
|
+
* Mark the current subtree's hydration state. `true` = inside a hydrated island (nested islands
|
|
6
|
+
* degrade). `false` = a LAKE resets its subtree to "dead", so an island inside the lake self-
|
|
7
|
+
* hydrates again (the nearest-boundary rule — DESIGN.md).
|
|
8
|
+
*/
|
|
9
|
+
function setNested(value = true) {
|
|
10
|
+
setContext(NESTED_KEY, value);
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* True when an ancestor island wrapper already marked the subtree. `getContext` returns `undefined`
|
|
14
|
+
* when no ancestor set it (a top-level island) — that absence is exactly "not nested".
|
|
15
|
+
*/
|
|
16
|
+
function isNested() {
|
|
17
|
+
return getContext(NESTED_KEY) === true;
|
|
18
|
+
}
|
|
19
|
+
export { isNested, setNested };
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RESOLVE SINK (reconciler R3) — a hook the runtime installs during an island's hydration to learn
|
|
3
|
+
* which page-scoped ids that island resolves, WITHOUT the hub knowing anything about the DOM.
|
|
4
|
+
* `resolve()` (ref.ts) calls the current sink with each id it freshly remembers in the page scope;
|
|
5
|
+
* the runtime's sink attributes it to the island currently hydrating (region → id-set), so a
|
|
6
|
+
* reconcile nav can dispose exactly the ids owned by REMOVED regions. Null when no island is
|
|
7
|
+
* hydrating.
|
|
8
|
+
*
|
|
9
|
+
* A leaf of its own (ref.ts re-exports the setter): the hydrate core installs the sink around every
|
|
10
|
+
* island, wired values or not, and shares modules with Kit's client graph, which uses the rest of
|
|
11
|
+
* the hub. A shared module ships whole, so reaching the sink through ref.ts sent every island page
|
|
12
|
+
* the hub's registry and minting.
|
|
13
|
+
*/
|
|
14
|
+
export declare let resolve_sink: ((id: string) => void) | null;
|
|
15
|
+
export declare function set_resolve_sink(fn: ((id: string) => void) | null): void;
|
package/dist/ref-sink.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// src/ref-sink.ts
|
|
2
|
+
/**
|
|
3
|
+
* RESOLVE SINK (reconciler R3) — a hook the runtime installs during an island's hydration to learn
|
|
4
|
+
* which page-scoped ids that island resolves, WITHOUT the hub knowing anything about the DOM.
|
|
5
|
+
* `resolve()` (ref.ts) calls the current sink with each id it freshly remembers in the page scope;
|
|
6
|
+
* the runtime's sink attributes it to the island currently hydrating (region → id-set), so a
|
|
7
|
+
* reconcile nav can dispose exactly the ids owned by REMOVED regions. Null when no island is
|
|
8
|
+
* hydrating.
|
|
9
|
+
*
|
|
10
|
+
* A leaf of its own (ref.ts re-exports the setter): the hydrate core installs the sink around every
|
|
11
|
+
* island, wired values or not, and shares modules with Kit's client graph, which uses the rest of
|
|
12
|
+
* the hub. A shared module ships whole, so reaching the sink through ref.ts sent every island page
|
|
13
|
+
* the hub's registry and minting.
|
|
14
|
+
*/
|
|
15
|
+
let resolve_sink = null;
|
|
16
|
+
function set_resolve_sink(fn) {
|
|
17
|
+
resolve_sink = fn;
|
|
18
|
+
}
|
|
19
|
+
export { resolve_sink, set_resolve_sink };
|
package/dist/ref.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { set_resolve_sink } from "./ref-sink.js";
|
|
1
2
|
import { batch, notify, watch, watcher_count } from "./ref-watch.js";
|
|
2
3
|
/**
|
|
3
4
|
* THE HUB — ogygia's identity layer. One primitive carries everything that crosses an
|
|
@@ -78,7 +79,6 @@ export declare function register_kind(kind: RefKind): void;
|
|
|
78
79
|
* silently mis-read as one). Same instance → same id, always.
|
|
79
80
|
*/
|
|
80
81
|
export declare function mint(value: unknown, only?: ReadonlySet<string>): Ref | undefined;
|
|
81
|
-
export declare function set_resolve_sink(fn: ((id: string) => void) | null): void;
|
|
82
82
|
/**
|
|
83
83
|
* DISPOSE a whole scope (hub v2, phase D) — tear down every instance in its bucket via the
|
|
84
84
|
* instance's `kind.dispose`, run any registered scope-disposers (side-effect cleanups like the
|
|
@@ -121,4 +121,4 @@ export declare const REF_WIRE_KEY = "OgygiaRef";
|
|
|
121
121
|
export declare function ref_reducer(only?: ReadonlySet<string>): (value: unknown) => Ref | undefined;
|
|
122
122
|
/** Devalue reviver for a seam. Browser passes `remember: true`; the server must pass `false`. */
|
|
123
123
|
export declare function ref_reviver(remember: boolean): (ref: Ref) => unknown;
|
|
124
|
-
export { batch, notify, watch, watcher_count };
|
|
124
|
+
export { batch, notify, set_resolve_sink, watch, watcher_count };
|
package/dist/ref.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { emit } from "./events/bus.js";
|
|
2
2
|
import { batch, notify, watch, watcher_count, watchers } from "./ref-watch.js";
|
|
3
|
+
import { resolve_sink, set_resolve_sink } from "./ref-sink.js";
|
|
3
4
|
// src/ref.ts
|
|
4
5
|
const DEVTOOLS = typeof __OGYGIA_DEVTOOLS__ !== "undefined" ? __OGYGIA_DEVTOOLS__ : false;
|
|
5
6
|
const REGISTRY_KEY = Symbol.for("ogygia.hub");
|
|
@@ -62,17 +63,6 @@ function mint(value, only) {
|
|
|
62
63
|
return ref;
|
|
63
64
|
}
|
|
64
65
|
}
|
|
65
|
-
/**
|
|
66
|
-
* RESOLVE SINK (reconciler R3) — a hook the runtime installs during an island's hydration to learn
|
|
67
|
-
* which page-scoped ids that island resolves, WITHOUT the hub knowing anything about the DOM.
|
|
68
|
-
* `resolve()` calls the current sink with each id it freshly remembers in the page scope; the
|
|
69
|
-
* runtime's sink attributes it to the island currently hydrating (region → id-set), so a reconcile
|
|
70
|
-
* nav can dispose exactly the ids owned by REMOVED regions. Null when no island is hydrating.
|
|
71
|
-
*/
|
|
72
|
-
let resolve_sink = null;
|
|
73
|
-
function set_resolve_sink(fn) {
|
|
74
|
-
resolve_sink = fn;
|
|
75
|
-
}
|
|
76
66
|
/** The live bucket for a scope (never 'request' — that scope is never stored). */
|
|
77
67
|
function bucket(reg, scope) {
|
|
78
68
|
let b = reg.instances.get(scope);
|
package/dist/runtime/beacon.js
CHANGED
|
@@ -108,7 +108,7 @@ function tag_attrs(tag) {
|
|
|
108
108
|
}
|
|
109
109
|
return out;
|
|
110
110
|
}
|
|
111
|
-
/** The tag's name, lowercase (`<
|
|
111
|
+
/** The tag's name, lowercase (`<ds-button …>` → `ds-button`). */
|
|
112
112
|
function tag_name(tag) {
|
|
113
113
|
let i = 1;
|
|
114
114
|
while (i < tag.length && tag[i] !== " " && tag[i] !== ">" && tag[i] !== "/" && tag[i] !== "\n") i++;
|
|
@@ -9,7 +9,7 @@ import { runtime_session } from "./session.js";
|
|
|
9
9
|
import { regions_in_shadow } from "./connected.js";
|
|
10
10
|
import { yield_task } from "./schedule.js";
|
|
11
11
|
import { beacon_nav } from "./beacon.js";
|
|
12
|
-
import { is_self_owned, walk_enters } from "./ownership.js";
|
|
12
|
+
import { is_self_owned, record_server_body, server_body_attributes, server_body_child, walk_enters } from "./ownership.js";
|
|
13
13
|
import { load_slot } from "./load-scheduler.js";
|
|
14
14
|
// src/runtime/boot-link.ts
|
|
15
15
|
/**
|
|
@@ -38,6 +38,9 @@ function link_boot() {
|
|
|
38
38
|
beacon_nav: BEACON ? beacon_nav : no_beacon_nav,
|
|
39
39
|
walk_enters,
|
|
40
40
|
is_self_owned,
|
|
41
|
+
record_server_body,
|
|
42
|
+
server_body_attributes,
|
|
43
|
+
server_body_child,
|
|
41
44
|
load_slot
|
|
42
45
|
};
|
|
43
46
|
}
|