@moku-labs/web 1.17.0 → 2.0.1
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/browser.d.mts +111 -108
- package/dist/browser.mjs +189 -189
- package/dist/index.cjs +198 -198
- package/dist/index.d.cts +111 -108
- package/dist/index.d.mts +111 -108
- package/dist/index.mjs +198 -198
- package/dist/{render-KdufA3_b.cjs → render-DHUcHCYs.cjs} +4 -4
- package/dist/{render-UO4nimWr.mjs → render-yXHc9BWI.mjs} +4 -4
- package/dist/testing.d.mts +59 -56
- package/dist/testing.mjs +50 -50
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -171,7 +171,7 @@ There are two entry points, and the difference is a hard guarantee, not a tree-s
|
|
|
171
171
|
| Entry | Format | For | Includes |
|
|
172
172
|
|---|---|---|---|
|
|
173
173
|
| **`@moku-labs/web`** | dual ESM + CJS | Node SSG builds | the full surface — add `content` / `build` / `deploy` / `cli` and wire `dotenv()` / `processEnv()` |
|
|
174
|
-
| **`@moku-labs/web/browser`** | ESM-only | client bundles | the same `createApp` over the same isomorphic defaults, plus `dataPlugin`, the route DSL, `
|
|
174
|
+
| **`@moku-labs/web/browser`** | ESM-only | client bundles | the same `createApp` over the same isomorphic defaults, plus `dataPlugin`, the route DSL, `createIsland`, `browserEnv`, and the SEO head primitives — **with all node-only code excluded** (`build` / `deploy` / `cli` and the `dotenv` / `processEnv` / `fileSystemContent` providers), and `browserEnv()` pre-wired as the default `env` provider |
|
|
175
175
|
|
|
176
176
|
Importing `@moku-labs/web/browser` can **never** drag node/native code into a client bundle, regardless of bundler or tree-shaking — its static import graph references zero node-only modules. This is stronger and more reliable than importing the main entry and relying on `"sideEffects": false`, where building entries together can merge node code into a shared chunk. (The browser entry keeps the `contentPlugin` *shell* so build-only loaders can `ctx.require(contentPlugin)`; the node Markdown source lives in `fileSystemContent`, which the entry does **not** export.) CI proves it:
|
|
177
177
|
|
package/dist/browser.d.mts
CHANGED
|
@@ -871,7 +871,7 @@ type Api$1 = {
|
|
|
871
871
|
composeTitle(head: HeadConfig | undefined): string;
|
|
872
872
|
};
|
|
873
873
|
declare namespace types_d_exports$4 {
|
|
874
|
-
export { AnyVNode,
|
|
874
|
+
export { AnyVNode, ExtractApi, ISLAND_HOOK_NAMES, IslandContext, IslandDef, IslandEventHandler, IslandEvents, IslandHooks, IslandInstance, IslandRender, IslandRouteSlice, IslandSpec, IslandSpecExtras, IslandStateFactory, PageData, RenderResult, ResolvedSpaConfig, SpaApi, SpaConfig, SpaContext, SpaDataReader, SpaEmitFunction, SpaEvents, SpaKernel, SpaKernelDeps, SpaRequire, SpaState };
|
|
875
875
|
}
|
|
876
876
|
/** Payload map for the events `spa` emits, used to type the kernel's `emit` closure. */
|
|
877
877
|
type SpaEvents = {
|
|
@@ -881,12 +881,12 @@ type SpaEvents = {
|
|
|
881
881
|
}; /** The swap completed and the new URL is active. */
|
|
882
882
|
"spa:navigated": {
|
|
883
883
|
url: string;
|
|
884
|
-
}; /** A
|
|
885
|
-
"spa:
|
|
884
|
+
}; /** A island instance attached to an element. */
|
|
885
|
+
"spa:island-mount": {
|
|
886
886
|
name: string;
|
|
887
887
|
el: Element;
|
|
888
|
-
}; /** A
|
|
889
|
-
"spa:
|
|
888
|
+
}; /** A island instance detached from an element. */
|
|
889
|
+
"spa:island-unmount": {
|
|
890
890
|
name: string;
|
|
891
891
|
el: Element;
|
|
892
892
|
};
|
|
@@ -955,10 +955,10 @@ type SpaConfig = {
|
|
|
955
955
|
*/
|
|
956
956
|
progressBar?: boolean;
|
|
957
957
|
/**
|
|
958
|
-
*
|
|
958
|
+
* Islands to auto-register at init (in addition to runtime `register`).
|
|
959
959
|
* Defaults to an empty array.
|
|
960
960
|
*/
|
|
961
|
-
|
|
961
|
+
islands?: IslandDef[];
|
|
962
962
|
};
|
|
963
963
|
/** Resolved SPA config after defaults are applied. */
|
|
964
964
|
interface ResolvedSpaConfig {
|
|
@@ -968,11 +968,11 @@ interface ResolvedSpaConfig {
|
|
|
968
968
|
viewTransitions: boolean;
|
|
969
969
|
/** Whether the progress bar is enabled. */
|
|
970
970
|
progressBar: boolean;
|
|
971
|
-
/** Pre-registered
|
|
972
|
-
|
|
971
|
+
/** Pre-registered islands. */
|
|
972
|
+
islands: IslandDef[];
|
|
973
973
|
}
|
|
974
974
|
/**
|
|
975
|
-
* What a
|
|
975
|
+
* What a island's `render` may return:
|
|
976
976
|
* - a Preact `VNode` — committed into the host through the lazy Preact gate (`commitVNode`);
|
|
977
977
|
* - a `Node` — replaces the host's children;
|
|
978
978
|
* - a `string` — set as the host's `innerHTML`;
|
|
@@ -984,30 +984,33 @@ type RenderResult = AnyVNode | Node | string | void;
|
|
|
984
984
|
* `VNode<SomeProps>`; the props generic is invariant under `exactOptionalPropertyTypes`,
|
|
985
985
|
* so the only supertype that accepts every concrete `VNode<P>` is `VNode<any>`.
|
|
986
986
|
*/
|
|
987
|
+
/**
|
|
988
|
+
*
|
|
989
|
+
*/
|
|
987
990
|
type AnyVNode = import("preact").VNode<any>;
|
|
988
991
|
/**
|
|
989
|
-
* Factory that builds a
|
|
992
|
+
* Factory that builds a island's typed per-instance state (mirrors a plugin's
|
|
990
993
|
* `createState`). Called ONCE at mount; the returned object is stored on the
|
|
991
|
-
* {@link
|
|
994
|
+
* {@link IslandInstance} and exposed read-only as `ctx.state`.
|
|
992
995
|
*
|
|
993
|
-
* @param ctx - The
|
|
996
|
+
* @param ctx - The island context for this instance (state is not yet set).
|
|
994
997
|
* @returns The initial per-instance state.
|
|
995
998
|
* @example
|
|
996
999
|
* state: (ctx): BoardState => ({ boardId: ctx.params.id ?? "", cards: [] })
|
|
997
1000
|
*/
|
|
998
|
-
type
|
|
1001
|
+
type IslandStateFactory<S extends object> = (ctx: IslandContext<S>) => S;
|
|
999
1002
|
/**
|
|
1000
1003
|
* Pure render of `(state, ctx)` → {@link RenderResult}. Called after mount-state-init
|
|
1001
1004
|
* and again (microtask-batched) after every `ctx.set`. Must be free of side effects
|
|
1002
1005
|
* beyond producing its result.
|
|
1003
1006
|
*
|
|
1004
1007
|
* @param state - The current per-instance state (read-only).
|
|
1005
|
-
* @param ctx - The
|
|
1008
|
+
* @param ctx - The island context for this instance.
|
|
1006
1009
|
* @returns The render result to commit into the host.
|
|
1007
1010
|
* @example
|
|
1008
1011
|
* render: (state) => h(BoardView, { snapshot: state.snapshot })
|
|
1009
1012
|
*/
|
|
1010
|
-
type
|
|
1013
|
+
type IslandRender<S extends object> = (state: Readonly<S>, ctx: IslandContext<S>) => RenderResult;
|
|
1011
1014
|
/**
|
|
1012
1015
|
* A delegated DOM event handler. `target` is the element matched by the key's selector
|
|
1013
1016
|
* (already resolved via `closest` — no `instanceof`/`closest` ceremony in the body).
|
|
@@ -1015,14 +1018,14 @@ type ComponentRender<S extends object> = (state: Readonly<S>, ctx: ComponentCont
|
|
|
1015
1018
|
* Typed `void` for ergonomics (the void-return rule accepts async handlers returning
|
|
1016
1019
|
* `Promise<void>` too); the kernel ignores any returned value.
|
|
1017
1020
|
*
|
|
1018
|
-
* @param ctx - The
|
|
1021
|
+
* @param ctx - The island context (carries the live per-instance `state`).
|
|
1019
1022
|
* @param event - The raw DOM event.
|
|
1020
1023
|
* @param target - The element matched by the selector (the host when no selector).
|
|
1021
1024
|
* @returns void (a returned promise is ignored by the kernel).
|
|
1022
1025
|
* @example
|
|
1023
1026
|
* (ctx, event, button) => { event.preventDefault(); ctx.set({ open: true }); }
|
|
1024
1027
|
*/
|
|
1025
|
-
type
|
|
1028
|
+
type IslandEventHandler<S extends object> = (ctx: IslandContext<S>, event: Event, target: Element) => void;
|
|
1026
1029
|
/**
|
|
1027
1030
|
* Declarative delegated event map. Each key is `"<type> <selector>"` (the selector is
|
|
1028
1031
|
* optional → a host-level listener). ONE real listener per event TYPE is attached to
|
|
@@ -1035,9 +1038,9 @@ type ComponentEventHandler<S extends object> = (ctx: ComponentContext<S>, event:
|
|
|
1035
1038
|
* "submit [data-add]": (ctx, e) => { e.preventDefault(); add(ctx); }
|
|
1036
1039
|
* }
|
|
1037
1040
|
*/
|
|
1038
|
-
type
|
|
1041
|
+
type IslandEvents<S extends object> = Record<string, IslandEventHandler<S>>;
|
|
1039
1042
|
/**
|
|
1040
|
-
* Context handed to every
|
|
1043
|
+
* Context handed to every island lifecycle hook, render, and event handler — the
|
|
1041
1044
|
* bound element + page data, plus the matched route's `params`/`meta`/`locale` and a
|
|
1042
1045
|
* link builder, so an island can read its route context (e.g. a `card` route's
|
|
1043
1046
|
* `ctx.meta.focus` + `ctx.params.id`) directly, without the page bridging it through
|
|
@@ -1045,11 +1048,11 @@ type ComponentEvents<S extends object> = Record<string, ComponentEventHandler<S>
|
|
|
1045
1048
|
*
|
|
1046
1049
|
* Generic over the per-instance state `S` (default `undefined` so every existing
|
|
1047
1050
|
* hooks-only island still type-checks). The additive members (`state`/`set`/`flush`/
|
|
1048
|
-
* `cleanup`/`
|
|
1051
|
+
* `cleanup`/`island`) are ALWAYS-PRESENT functions — never optional keys — so they
|
|
1049
1052
|
* never trip `exactOptionalPropertyTypes`.
|
|
1050
1053
|
*/
|
|
1051
|
-
interface
|
|
1052
|
-
/** The element the
|
|
1054
|
+
interface IslandContext<S = undefined> {
|
|
1055
|
+
/** The element the island instance is bound to. */
|
|
1053
1056
|
el: Element;
|
|
1054
1057
|
/** Page data extracted from the `script#__DATA__` payload. */
|
|
1055
1058
|
data: PageData;
|
|
@@ -1098,128 +1101,128 @@ interface ComponentContext<S = undefined> {
|
|
|
1098
1101
|
* Resolve another island's registered `api` by name. Returns `undefined` when no
|
|
1099
1102
|
* provider is registered (optional-dependency semantics, mirroring `ctx.has`).
|
|
1100
1103
|
*
|
|
1101
|
-
* @param name - The provider island's
|
|
1104
|
+
* @param name - The provider island's island name.
|
|
1102
1105
|
* @returns The provider's api, or `undefined`.
|
|
1103
1106
|
* @example
|
|
1104
|
-
* ctx.
|
|
1107
|
+
* ctx.island<LightboxApi>("lightbox")?.open(slides, index);
|
|
1105
1108
|
*/
|
|
1106
|
-
|
|
1109
|
+
island<T = unknown>(name: string): T | undefined;
|
|
1107
1110
|
}
|
|
1108
|
-
/** Lifecycle hooks a
|
|
1109
|
-
interface
|
|
1111
|
+
/** Lifecycle hooks a island may implement. Generic over the per-instance state `S`. */
|
|
1112
|
+
interface IslandHooks<S = undefined> {
|
|
1110
1113
|
/**
|
|
1111
1114
|
* Called once when the instance is created (before DOM attach).
|
|
1112
1115
|
*
|
|
1113
|
-
* @param ctx - The
|
|
1116
|
+
* @param ctx - The island context for this instance.
|
|
1114
1117
|
* @returns void
|
|
1115
1118
|
* @example
|
|
1116
1119
|
* onCreate({ el }) { el.dataset.ready = "1"; }
|
|
1117
1120
|
*/
|
|
1118
|
-
onCreate?(ctx:
|
|
1121
|
+
onCreate?(ctx: IslandContext<S>): void;
|
|
1119
1122
|
/**
|
|
1120
1123
|
* Called after the instance is attached to its element.
|
|
1121
1124
|
*
|
|
1122
|
-
* @param ctx - The
|
|
1125
|
+
* @param ctx - The island context for this instance.
|
|
1123
1126
|
* @returns void
|
|
1124
1127
|
* @example
|
|
1125
1128
|
* onMount({ el }) { el.textContent = "0"; }
|
|
1126
1129
|
* @example
|
|
1127
1130
|
* async onMount(ctx) { ctx.set({ items: await load() }); } // async is allowed; the harness awaits it via settle()
|
|
1128
1131
|
*/
|
|
1129
|
-
onMount?(ctx:
|
|
1132
|
+
onMount?(ctx: IslandContext<S>): void;
|
|
1130
1133
|
/**
|
|
1131
1134
|
* Called when a navigation begins while this instance is mounted.
|
|
1132
1135
|
*
|
|
1133
|
-
* @param ctx - The
|
|
1136
|
+
* @param ctx - The island context for this instance.
|
|
1134
1137
|
* @returns void
|
|
1135
1138
|
* @example
|
|
1136
1139
|
* onNavStart({ el }) { el.dataset.loading = ""; }
|
|
1137
1140
|
*/
|
|
1138
|
-
onNavStart?(ctx:
|
|
1141
|
+
onNavStart?(ctx: IslandContext<S>): void;
|
|
1139
1142
|
/**
|
|
1140
1143
|
* Called when a navigation completes while this instance is mounted.
|
|
1141
1144
|
*
|
|
1142
|
-
* @param ctx - The
|
|
1145
|
+
* @param ctx - The island context for this instance.
|
|
1143
1146
|
* @returns void
|
|
1144
1147
|
* @example
|
|
1145
1148
|
* onNavEnd({ el }) { delete el.dataset.loading; }
|
|
1146
1149
|
*/
|
|
1147
|
-
onNavEnd?(ctx:
|
|
1150
|
+
onNavEnd?(ctx: IslandContext<S>): void;
|
|
1148
1151
|
/**
|
|
1149
1152
|
* Called before the instance is detached from its element.
|
|
1150
1153
|
*
|
|
1151
|
-
* @param ctx - The
|
|
1154
|
+
* @param ctx - The island context for this instance.
|
|
1152
1155
|
* @returns void
|
|
1153
1156
|
* @example
|
|
1154
1157
|
* onUnMount({ el }) { el.replaceChildren(); }
|
|
1155
1158
|
*/
|
|
1156
|
-
onUnMount?(ctx:
|
|
1159
|
+
onUnMount?(ctx: IslandContext<S>): void;
|
|
1157
1160
|
/**
|
|
1158
1161
|
* Called once when the instance is destroyed (after detach).
|
|
1159
1162
|
*
|
|
1160
|
-
* @param ctx - The
|
|
1163
|
+
* @param ctx - The island context for this instance.
|
|
1161
1164
|
* @returns void
|
|
1162
1165
|
* @example
|
|
1163
1166
|
* onDestroy({ el }) { delete el.dataset.ready; }
|
|
1164
1167
|
*/
|
|
1165
|
-
onDestroy?(ctx:
|
|
1168
|
+
onDestroy?(ctx: IslandContext<S>): void;
|
|
1166
1169
|
}
|
|
1167
1170
|
/** Allowed hook names — single source of truth for fail-fast validation. */
|
|
1168
|
-
declare const
|
|
1171
|
+
declare const ISLAND_HOOK_NAMES: readonly ["onCreate", "onMount", "onNavStart", "onNavEnd", "onUnMount", "onDestroy"];
|
|
1169
1172
|
/**
|
|
1170
|
-
* The plugin-mirror authoring form for {@link
|
|
1173
|
+
* The plugin-mirror authoring form for {@link createIsland}: typed per-instance
|
|
1171
1174
|
* `state`, `render`, declarative `events`, and a cross-island `api` on top of the
|
|
1172
1175
|
* lifecycle hooks. All keys optional + additive; the presence of any spec-only key
|
|
1173
|
-
* (`state`/`render`/`events`/`api`) selects the spec overload of `
|
|
1176
|
+
* (`state`/`render`/`events`/`api`) selects the spec overload of `createIsland`.
|
|
1174
1177
|
*
|
|
1175
1178
|
* @example
|
|
1176
|
-
*
|
|
1179
|
+
* createIsland<{ boards: Board[] }>("board-list", {
|
|
1177
1180
|
* state: () => ({ boards: [] }),
|
|
1178
|
-
* async onMount(ctx) { ctx.set({ boards: await ctx.
|
|
1181
|
+
* async onMount(ctx) { ctx.set({ boards: await ctx.island<Api>("api")!.list() }); },
|
|
1179
1182
|
* render: (s) => h(BoardList, { boards: s.boards }),
|
|
1180
1183
|
* events: { "submit [data-create]": (ctx, e) => { e.preventDefault(); create(ctx); } }
|
|
1181
1184
|
* });
|
|
1182
1185
|
*/
|
|
1183
|
-
interface
|
|
1186
|
+
interface IslandSpec<S extends object = object, A = unknown> extends IslandHooks<S> {
|
|
1184
1187
|
/** Build typed per-instance state at mount (stored on the instance, not a module WeakMap). */
|
|
1185
|
-
state?:
|
|
1188
|
+
state?: IslandStateFactory<S>;
|
|
1186
1189
|
/** Pure render re-invoked (microtask-batched) on every `ctx.set`. */
|
|
1187
|
-
render?:
|
|
1190
|
+
render?: IslandRender<S>;
|
|
1188
1191
|
/** Declarative delegated DOM events with auto-teardown. */
|
|
1189
|
-
events?:
|
|
1190
|
-
/** Public api factory — registered under the
|
|
1191
|
-
api?: (ctx:
|
|
1192
|
+
events?: IslandEvents<S>;
|
|
1193
|
+
/** Public api factory — registered under the island name; reached via `app.spa.island(name)`. */
|
|
1194
|
+
api?: (ctx: IslandContext<S>) => A;
|
|
1192
1195
|
}
|
|
1193
1196
|
/**
|
|
1194
|
-
* The spec extras carried on a {@link
|
|
1195
|
-
* (authors keep full `S` inference at the `
|
|
1197
|
+
* The spec extras carried on a {@link IslandDef}, type-erased to `object` state
|
|
1198
|
+
* (authors keep full `S` inference at the `createIsland` call site; the registry
|
|
1196
1199
|
* stores the runtime-only erased form). Absent for legacy `(name, hooks)` defs.
|
|
1197
1200
|
*/
|
|
1198
|
-
interface
|
|
1201
|
+
interface IslandSpecExtras {
|
|
1199
1202
|
/** Per-instance state factory. */
|
|
1200
|
-
state?:
|
|
1203
|
+
state?: IslandStateFactory<object>;
|
|
1201
1204
|
/** Render called on mount + after every `ctx.set`. */
|
|
1202
|
-
render?:
|
|
1205
|
+
render?: IslandRender<object>;
|
|
1203
1206
|
/** Declarative delegated events. */
|
|
1204
|
-
events?:
|
|
1205
|
-
/** Public api factory registered under the
|
|
1206
|
-
api?: (ctx:
|
|
1207
|
+
events?: IslandEvents<object>;
|
|
1208
|
+
/** Public api factory registered under the island name. */
|
|
1209
|
+
api?: (ctx: IslandContext<object>) => unknown;
|
|
1207
1210
|
}
|
|
1208
|
-
/** A registered
|
|
1209
|
-
interface
|
|
1210
|
-
/** Unique
|
|
1211
|
+
/** A registered island definition (an opaque token; author inference lives on `createIsland`). */
|
|
1212
|
+
interface IslandDef {
|
|
1213
|
+
/** Unique island name (matched against `data-island`). */
|
|
1211
1214
|
name: string;
|
|
1212
1215
|
/** Lifecycle hooks (the subset shared with the legacy form). */
|
|
1213
|
-
hooks:
|
|
1216
|
+
hooks: IslandHooks<object>;
|
|
1214
1217
|
/** Plugin-mirror extras (state/render/events/api). Absent for legacy `(name, hooks)` defs. */
|
|
1215
|
-
spec?:
|
|
1218
|
+
spec?: IslandSpecExtras;
|
|
1216
1219
|
}
|
|
1217
1220
|
/** The matched-route slice carried on a live instance (params/meta/locale + link builder). */
|
|
1218
|
-
type
|
|
1219
|
-
/** A live, mounted
|
|
1220
|
-
interface
|
|
1221
|
+
type IslandRouteSlice = Pick<IslandContext, "params" | "meta" | "locale" | "url">;
|
|
1222
|
+
/** A live, mounted island instance. */
|
|
1223
|
+
interface IslandInstance {
|
|
1221
1224
|
/** The definition this instance was created from. */
|
|
1222
|
-
def:
|
|
1225
|
+
def: IslandDef;
|
|
1223
1226
|
/** The element this instance is bound to. */
|
|
1224
1227
|
el: Element;
|
|
1225
1228
|
/**
|
|
@@ -1229,13 +1232,13 @@ interface ComponentInstance {
|
|
|
1229
1232
|
*/
|
|
1230
1233
|
persistent: boolean;
|
|
1231
1234
|
/** The single per-instance context reused by every hook, event handler, and render. */
|
|
1232
|
-
ctx:
|
|
1235
|
+
ctx: IslandContext<object>;
|
|
1233
1236
|
/** Live per-instance state (the object returned by `spec.state`), or undefined for hooks-only islands. */
|
|
1234
1237
|
state: object | undefined;
|
|
1235
1238
|
/** This instance's public api (the object returned by `spec.api`), or undefined when none declared. */
|
|
1236
1239
|
api: unknown;
|
|
1237
1240
|
/** Current matched-route slice (updated on navigation; read by `ctx.params/meta/locale/url`). */
|
|
1238
|
-
route:
|
|
1241
|
+
route: IslandRouteSlice;
|
|
1239
1242
|
/** Current page data payload (updated on navigation; read by `ctx.data`). */
|
|
1240
1243
|
data: PageData;
|
|
1241
1244
|
/** Disposers from `ctx.cleanup` + the declarative `events` listeners — run LIFO on destroy. */
|
|
@@ -1275,7 +1278,7 @@ interface SpaKernelDeps {
|
|
|
1275
1278
|
/** The single shared SPA kernel — pure factory over state/config/emit/deps. */
|
|
1276
1279
|
interface SpaKernel {
|
|
1277
1280
|
/**
|
|
1278
|
-
* Validate config, register config.
|
|
1281
|
+
* Validate config, register config.islands, seed currentUrl.
|
|
1279
1282
|
*
|
|
1280
1283
|
* @returns void
|
|
1281
1284
|
* @example
|
|
@@ -1291,14 +1294,14 @@ interface SpaKernel {
|
|
|
1291
1294
|
*/
|
|
1292
1295
|
boot(): void;
|
|
1293
1296
|
/**
|
|
1294
|
-
* Register a
|
|
1297
|
+
* Register a island definition (last-registered-wins).
|
|
1295
1298
|
*
|
|
1296
|
-
* @param
|
|
1299
|
+
* @param island - The island definition to register.
|
|
1297
1300
|
* @returns void
|
|
1298
1301
|
* @example
|
|
1299
1302
|
* kernel.register(counter);
|
|
1300
1303
|
*/
|
|
1301
|
-
register(
|
|
1304
|
+
register(island: IslandDef): void;
|
|
1302
1305
|
/**
|
|
1303
1306
|
* Process a navigation to `path`: fetch then swap then head-sync then emit.
|
|
1304
1307
|
*
|
|
@@ -1309,7 +1312,7 @@ interface SpaKernel {
|
|
|
1309
1312
|
*/
|
|
1310
1313
|
processNav(path: string): void;
|
|
1311
1314
|
/**
|
|
1312
|
-
* Query the swap region and mount
|
|
1315
|
+
* Query the swap region and mount islands for matching elements.
|
|
1313
1316
|
*
|
|
1314
1317
|
* @returns void
|
|
1315
1318
|
* @example
|
|
@@ -1327,12 +1330,12 @@ interface SpaKernel {
|
|
|
1327
1330
|
}
|
|
1328
1331
|
/** Internal mutable state for the spa plugin (all kernel data lives here). */
|
|
1329
1332
|
interface SpaState {
|
|
1330
|
-
/**
|
|
1331
|
-
|
|
1332
|
-
/** Live
|
|
1333
|
-
instances: Map<Element,
|
|
1334
|
-
/** Registered island apis by
|
|
1335
|
-
|
|
1333
|
+
/** Islands registered by name (last-registered-wins). */
|
|
1334
|
+
registeredIslands: Map<string, IslandDef>;
|
|
1335
|
+
/** Live island instances keyed by their bound element. */
|
|
1336
|
+
instances: Map<Element, IslandInstance>;
|
|
1337
|
+
/** Registered island apis by island name (the cross-island `ctx.island`/`app.spa.island` seam). */
|
|
1338
|
+
islandApis: Map<string, unknown>;
|
|
1336
1339
|
/** The current resolved URL (pathname + search). */
|
|
1337
1340
|
currentUrl: string;
|
|
1338
1341
|
/** Teardown handle for the attached router listeners (null when detached). */
|
|
@@ -1345,14 +1348,14 @@ interface SpaState {
|
|
|
1345
1348
|
/** Public API of the spa plugin (registration / control surface). */
|
|
1346
1349
|
type SpaApi = {
|
|
1347
1350
|
/**
|
|
1348
|
-
* Register a
|
|
1351
|
+
* Register a island definition for client mounting.
|
|
1349
1352
|
*
|
|
1350
|
-
* @param
|
|
1353
|
+
* @param island - The island definition created via `createIsland`.
|
|
1351
1354
|
* @returns void
|
|
1352
1355
|
* @example
|
|
1353
1356
|
* app.spa.register(counter);
|
|
1354
1357
|
*/
|
|
1355
|
-
register(
|
|
1358
|
+
register(island: IslandDef): void;
|
|
1356
1359
|
/**
|
|
1357
1360
|
* Programmatically navigate to a path (client runtime; no-op without a DOM).
|
|
1358
1361
|
*
|
|
@@ -1374,12 +1377,12 @@ type SpaApi = {
|
|
|
1374
1377
|
* Resolve a registered island's api by name (the cross-island seam). Returns
|
|
1375
1378
|
* `undefined` when no provider with that name is currently registered.
|
|
1376
1379
|
*
|
|
1377
|
-
* @param name - The provider island's
|
|
1380
|
+
* @param name - The provider island's island name.
|
|
1378
1381
|
* @returns The provider's api, or `undefined`.
|
|
1379
1382
|
* @example
|
|
1380
|
-
* app.spa.
|
|
1383
|
+
* app.spa.island<LightboxApi>("lightbox")?.open(slides, 0);
|
|
1381
1384
|
*/
|
|
1382
|
-
|
|
1385
|
+
island<T = unknown>(name: string): T | undefined;
|
|
1383
1386
|
};
|
|
1384
1387
|
//#endregion
|
|
1385
1388
|
//#region src/plugins/site/index.d.ts
|
|
@@ -1597,43 +1600,43 @@ declare const headPlugin: import("@moku-labs/core").PluginInstance<"head", Confi
|
|
|
1597
1600
|
buildArticleHead: typeof buildArticleHead;
|
|
1598
1601
|
};
|
|
1599
1602
|
//#endregion
|
|
1600
|
-
//#region src/plugins/spa/
|
|
1603
|
+
//#region src/plugins/spa/islands.d.ts
|
|
1601
1604
|
/**
|
|
1602
|
-
* Create a validated
|
|
1603
|
-
* (`
|
|
1604
|
-
* (`
|
|
1605
|
+
* Create a validated island definition. Accepts either the legacy hooks-only form
|
|
1606
|
+
* (`createIsland("counter", { onMount() {} })`) or the plugin-mirror spec form
|
|
1607
|
+
* (`createIsland("board", { state, render, events, api, ...hooks })`). Spec-only
|
|
1605
1608
|
* keys (`state`/`render`/`events`/`api`) are partitioned out before hook-name
|
|
1606
1609
|
* validation, so a real typo (e.g. `onMout`) still throws immediately while the spec
|
|
1607
1610
|
* keys are accepted.
|
|
1608
1611
|
*
|
|
1609
|
-
* @param name - Unique
|
|
1612
|
+
* @param name - Unique island name.
|
|
1610
1613
|
* @param spec - Lifecycle hooks, or the `{ state, render, events, api, ...hooks }` spec.
|
|
1611
|
-
* @returns A `
|
|
1614
|
+
* @returns A `IslandDef` ready to `register`.
|
|
1612
1615
|
* @throws {Error} If `name` is empty, a hook key is unknown, or an extra/hook value has the wrong shape.
|
|
1613
1616
|
* @example
|
|
1614
|
-
* const counter =
|
|
1617
|
+
* const counter = createIsland("counter", { onMount({ el }) { el.textContent = "0"; } });
|
|
1615
1618
|
* @example
|
|
1616
|
-
* const list =
|
|
1619
|
+
* const list = createIsland<{ items: string[] }>("list", {
|
|
1617
1620
|
* state: () => ({ items: [] }),
|
|
1618
1621
|
* render: (s) => h(List, { items: s.items })
|
|
1619
1622
|
* });
|
|
1620
1623
|
*/
|
|
1621
|
-
declare function
|
|
1624
|
+
declare function createIsland<S extends object = object, A = unknown>(name: string, spec: IslandSpec<S, A>): IslandDef;
|
|
1622
1625
|
//#endregion
|
|
1623
1626
|
//#region src/plugins/spa/lazy-embed.d.ts
|
|
1624
1627
|
/**
|
|
1625
1628
|
* Lazy-embed island: facade button click → real `<iframe loading="lazy">`.
|
|
1626
1629
|
* The companion of the content pipeline's `::embed` directive.
|
|
1627
1630
|
*/
|
|
1628
|
-
declare const lazyEmbed:
|
|
1631
|
+
declare const lazyEmbed: IslandDef;
|
|
1629
1632
|
//#endregion
|
|
1630
1633
|
//#region src/plugins/spa/index.d.ts
|
|
1631
1634
|
/**
|
|
1632
1635
|
* SPA plugin — progressive client-side navigation layered over the static site:
|
|
1633
1636
|
* swaps a page region on navigation, with an optional progress bar and View
|
|
1634
|
-
* Transitions. Register interactive islands with {@link
|
|
1635
|
-
* on router and head; emits `spa:navigate`, `spa:navigated`, `spa:
|
|
1636
|
-
* and `spa:
|
|
1637
|
+
* Transitions. Register interactive islands with {@link createIsland}. Depends
|
|
1638
|
+
* on router and head; emits `spa:navigate`, `spa:navigated`, `spa:island-mount`,
|
|
1639
|
+
* and `spa:island-unmount`.
|
|
1637
1640
|
*
|
|
1638
1641
|
* @example Enable view transitions and a custom swap region
|
|
1639
1642
|
* ```ts
|
|
@@ -1656,11 +1659,11 @@ declare const spaPlugin: import("@moku-labs/core").PluginInstance<"spa", SpaConf
|
|
|
1656
1659
|
"spa:navigated": {
|
|
1657
1660
|
url: string;
|
|
1658
1661
|
};
|
|
1659
|
-
"spa:
|
|
1662
|
+
"spa:island-mount": {
|
|
1660
1663
|
name: string;
|
|
1661
1664
|
el: Element;
|
|
1662
1665
|
};
|
|
1663
|
-
"spa:
|
|
1666
|
+
"spa:island-unmount": {
|
|
1664
1667
|
name: string;
|
|
1665
1668
|
el: Element;
|
|
1666
1669
|
};
|
|
@@ -2036,7 +2039,7 @@ type GalleryProps = {
|
|
|
2036
2039
|
/**
|
|
2037
2040
|
* A consumer-supplied gallery component: a Preact function component over
|
|
2038
2041
|
* {@link GalleryProps}, rendered (at build time, to static markup) as the inner
|
|
2039
|
-
* content — inside the framework-owned `<div data-
|
|
2042
|
+
* content — inside the framework-owned `<div data-island="gallery">` that carries
|
|
2040
2043
|
* the island hook. Defaults to the built-in `GalleryTrack`.
|
|
2041
2044
|
*/
|
|
2042
2045
|
type GalleryComponent = FunctionComponent<GalleryProps>;
|
|
@@ -2359,11 +2362,11 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2359
2362
|
"spa:navigated": {
|
|
2360
2363
|
url: string;
|
|
2361
2364
|
};
|
|
2362
|
-
"spa:
|
|
2365
|
+
"spa:island-mount": {
|
|
2363
2366
|
name: string;
|
|
2364
2367
|
el: Element;
|
|
2365
2368
|
};
|
|
2366
|
-
"spa:
|
|
2369
|
+
"spa:island-unmount": {
|
|
2367
2370
|
name: string;
|
|
2368
2371
|
el: Element;
|
|
2369
2372
|
};
|
|
@@ -2388,11 +2391,11 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2388
2391
|
"spa:navigated": {
|
|
2389
2392
|
url: string;
|
|
2390
2393
|
};
|
|
2391
|
-
"spa:
|
|
2394
|
+
"spa:island-mount": {
|
|
2392
2395
|
name: string;
|
|
2393
2396
|
el: Element;
|
|
2394
2397
|
};
|
|
2395
|
-
"spa:
|
|
2398
|
+
"spa:island-unmount": {
|
|
2396
2399
|
name: string;
|
|
2397
2400
|
el: Element;
|
|
2398
2401
|
};
|
|
@@ -2414,4 +2417,4 @@ declare const createApp: <const ExtraPlugins extends readonly import("@moku-labs
|
|
|
2414
2417
|
*/
|
|
2415
2418
|
declare const createPlugin: import("@moku-labs/core").BoundCreatePluginFunction<Config$5, Events, import("@moku-labs/core").CoreApisFromTuple<[import("@moku-labs/core").CorePluginInstance<"log", import("@moku-labs/common").LogConfig, import("@moku-labs/common").LogState, import("@moku-labs/common").LogApi>, import("@moku-labs/core").CorePluginInstance<"env", import("@moku-labs/common").EnvConfig, import("@moku-labs/common").EnvState, import("@moku-labs/common").EnvApi>]>>;
|
|
2416
2419
|
//#endregion
|
|
2417
|
-
export { types_d_exports as Content, types_d_exports$1 as Data, type Env, types_d_exports$2 as Head, type Log, types_d_exports$3 as Router, types_d_exports$4 as Spa, browserEnv, buildArticleHead, canonical, contentPlugin, createApp,
|
|
2420
|
+
export { types_d_exports as Content, types_d_exports$1 as Data, type Env, types_d_exports$2 as Head, type Log, types_d_exports$3 as Router, types_d_exports$4 as Spa, browserEnv, buildArticleHead, canonical, contentPlugin, createApp, createIsland, createPlugin, createUrls, dataPlugin, defineRoutes, envPlugin, feedLink, headPlugin, hreflang, i18nPlugin, jsonLd, lazyEmbed, logPlugin, meta, og, route, routerPlugin, sitePlugin, spaPlugin, twitter };
|