@askrjs/askr 0.0.35 → 0.0.36
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/bench/components/benchmark-row.js.map +1 -1
- package/dist/bench/components/benchmark-table.js.map +1 -1
- package/dist/benchmark.js +2 -2
- package/dist/benchmark.js.map +1 -1
- package/dist/bin/askr-ssg.js.map +1 -1
- package/dist/boot/index.d.ts.map +1 -1
- package/dist/boot/index.js +9 -29
- package/dist/boot/index.js.map +1 -1
- package/dist/common/control.js.map +1 -1
- package/dist/common/env.js.map +1 -1
- package/dist/common/ssr-errors.js.map +1 -1
- package/dist/common/vnode.js.map +1 -1
- package/dist/components/error-boundary.js.map +1 -1
- package/dist/components/link.js.map +1 -1
- package/dist/control/case.js.map +1 -1
- package/dist/control/for.js.map +1 -1
- package/dist/control/shared.js.map +1 -1
- package/dist/control/show.js.map +1 -1
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +165 -23
- package/dist/data/index.js.map +1 -1
- package/dist/dev/invariant.js.map +1 -1
- package/dist/dev/logger.js.map +1 -1
- package/dist/foundations/icon/icon.js.map +1 -1
- package/dist/foundations/interactions/dismissable.js.map +1 -1
- package/dist/foundations/interactions/focusable.js.map +1 -1
- package/dist/foundations/interactions/hoverable.js.map +1 -1
- package/dist/foundations/interactions/interaction-policy.js.map +1 -1
- package/dist/foundations/interactions/pressable.js.map +1 -1
- package/dist/foundations/interactions/roving-focus.js.map +1 -1
- package/dist/foundations/state/controllable.js.map +1 -1
- package/dist/foundations/structures/collection.js.map +1 -1
- package/dist/foundations/structures/layer.js.map +1 -1
- package/dist/foundations/structures/layout.js.map +1 -1
- package/dist/foundations/structures/portal.js.map +1 -1
- package/dist/foundations/structures/presence.js.map +1 -1
- package/dist/foundations/structures/slot.js.map +1 -1
- package/dist/foundations/utilities/aria.js.map +1 -1
- package/dist/foundations/utilities/compose-handlers.js.map +1 -1
- package/dist/foundations/utilities/compose-ref.js.map +1 -1
- package/dist/foundations/utilities/merge-props.js.map +1 -1
- package/dist/foundations/utilities/use-id.js.map +1 -1
- package/dist/fx/fx.js.map +1 -1
- package/dist/fx/timing.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/jsx/utils.js.map +1 -1
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.js.map +1 -1
- package/dist/renderer/children.js.map +1 -1
- package/dist/renderer/cleanup.js.map +1 -1
- package/dist/renderer/dom.js +128 -53
- package/dist/renderer/dom.js.map +1 -1
- package/dist/renderer/evaluate.js +7 -0
- package/dist/renderer/evaluate.js.map +1 -1
- package/dist/renderer/fastpath.js.map +1 -1
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/keyed.js +1 -1
- package/dist/renderer/keyed.js.map +1 -1
- package/dist/renderer/reconcile.js.map +1 -1
- package/dist/renderer/utils.js.map +1 -1
- package/dist/router/index.d.ts +2 -2
- package/dist/router/index.js +2 -2
- package/dist/router/match.js.map +1 -1
- package/dist/router/navigate.d.ts.map +1 -1
- package/dist/router/navigate.js +4 -2
- package/dist/router/navigate.js.map +1 -1
- package/dist/router/policy.js.map +1 -1
- package/dist/router/route-context.js.map +1 -1
- package/dist/router/route.d.ts +1 -1
- package/dist/router/route.d.ts.map +1 -1
- package/dist/router/route.js +3 -34
- package/dist/router/route.js.map +1 -1
- package/dist/runtime/child-scope.d.ts +1 -0
- package/dist/runtime/child-scope.d.ts.map +1 -1
- package/dist/runtime/child-scope.js +2 -0
- package/dist/runtime/child-scope.js.map +1 -1
- package/dist/runtime/component.js +1 -1
- package/dist/runtime/component.js.map +1 -1
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/control.js.map +1 -1
- package/dist/runtime/derive.d.ts +2 -1
- package/dist/runtime/derive.d.ts.map +1 -1
- package/dist/runtime/derive.js +15 -10
- package/dist/runtime/derive.js.map +1 -1
- package/dist/runtime/dev-namespace.js.map +1 -1
- package/dist/runtime/effect.d.ts.map +1 -1
- package/dist/runtime/effect.js +34 -9
- package/dist/runtime/effect.js.map +1 -1
- package/dist/runtime/events.js.map +1 -1
- package/dist/runtime/execution-model.js.map +1 -1
- package/dist/runtime/fastlane.js.map +1 -1
- package/dist/runtime/for-bench.d.ts +1 -1
- package/dist/runtime/for-bench.d.ts.map +1 -1
- package/dist/runtime/for-bench.js +15 -14
- package/dist/runtime/for-bench.js.map +1 -1
- package/dist/runtime/for.d.ts +1 -0
- package/dist/runtime/for.d.ts.map +1 -1
- package/dist/runtime/for.js +63 -16
- package/dist/runtime/for.js.map +1 -1
- package/dist/runtime/operations.d.ts.map +1 -1
- package/dist/runtime/operations.js +9 -7
- package/dist/runtime/operations.js.map +1 -1
- package/dist/runtime/perf-metrics.js +8 -2
- package/dist/runtime/perf-metrics.js.map +1 -1
- package/dist/runtime/readable.js.map +1 -1
- package/dist/runtime/resource-cell.js +3 -2
- package/dist/runtime/resource-cell.js.map +1 -1
- package/dist/runtime/scheduler.d.ts +0 -1
- package/dist/runtime/scheduler.d.ts.map +1 -1
- package/dist/runtime/scheduler.js +6 -19
- package/dist/runtime/scheduler.js.map +1 -1
- package/dist/runtime/selector.d.ts.map +1 -1
- package/dist/runtime/selector.js +6 -4
- package/dist/runtime/selector.js.map +1 -1
- package/dist/runtime/snapshot-source.d.ts +10 -0
- package/dist/runtime/snapshot-source.d.ts.map +1 -0
- package/dist/runtime/snapshot-source.js +17 -0
- package/dist/runtime/snapshot-source.js.map +1 -0
- package/dist/runtime/ssr-bridge.js.map +1 -1
- package/dist/runtime/state.d.ts.map +1 -1
- package/dist/runtime/state.js +1 -5
- package/dist/runtime/state.js.map +1 -1
- package/dist/ssg/batch-render.js.map +1 -1
- package/dist/ssg/create-static-gen.js.map +1 -1
- package/dist/ssg/generate-metadata.js.map +1 -1
- package/dist/ssg/incremental-manifest.js.map +1 -1
- package/dist/ssg/resolve-ssg-data.js.map +1 -1
- package/dist/ssg/route-utils.js.map +1 -1
- package/dist/ssg/write-static-files.js.map +1 -1
- package/dist/ssr/attrs.js.map +1 -1
- package/dist/ssr/context.d.ts +1 -0
- package/dist/ssr/context.d.ts.map +1 -1
- package/dist/ssr/context.js +3 -0
- package/dist/ssr/context.js.map +1 -1
- package/dist/ssr/escape.js +56 -16
- package/dist/ssr/escape.js.map +1 -1
- package/dist/ssr/index.d.ts.map +1 -1
- package/dist/ssr/index.js +15 -3
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/render-keys.js.map +1 -1
- package/dist/ssr/sink.js.map +1 -1
- package/dist/ssr/stream-render.js +14 -2
- package/dist/ssr/stream-render.js.map +1 -1
- package/package.json +8 -6
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark-row.js","names":[],"sources":["../../../src/bench/components/benchmark-row.tsx"],"sourcesContent":["export interface BenchmarkRowData {\n id: number;\n label: string;\n}\n\ninterface BenchmarkRowProps {\n item: BenchmarkRowData;\n isSelected: (id: number) => boolean;\n onSelect: (id: number) => void;\n onRemove: (id: number) => void;\n}\n\nexport function BenchmarkRow({\n item,\n isSelected,\n onSelect,\n onRemove,\n}: BenchmarkRowProps) {\n return (\n <tr class={() => (isSelected(item.id) ? 'danger' : '')}>\n <td key=\"id\" class=\"col-md-1\">\n {() => item.id}\n </td>\n <td key=\"label\" class=\"col-md-4\">\n <a\n onClick={(event: MouseEvent) => {\n event.preventDefault();\n onSelect(item.id);\n }}\n >\n {() => item.label}\n </a>\n </td>\n <td key=\"remove\" class=\"col-md-1\">\n <a\n onClick={(event: MouseEvent) => {\n event.preventDefault();\n onRemove(item.id);\n }}\n >\n <span class=\"glyphicon glyphicon-remove\" aria-hidden=\"true\" />\n </a>\n </td>\n <td key=\"spacer\" class=\"col-md-6\" />\n </tr>\n );\n}\n"],"mappings":";;;AAYA,SAAgB,aAAa,EAC3B,MACA,YACA,UACA,YACoB;
|
|
1
|
+
{"version":3,"file":"benchmark-row.js","names":[],"sources":["../../../src/bench/components/benchmark-row.tsx"],"sourcesContent":["export interface BenchmarkRowData {\n id: number;\n label: string;\n}\n\ninterface BenchmarkRowProps {\n item: BenchmarkRowData;\n isSelected: (id: number) => boolean;\n onSelect: (id: number) => void;\n onRemove: (id: number) => void;\n}\n\nexport function BenchmarkRow({\n item,\n isSelected,\n onSelect,\n onRemove,\n}: BenchmarkRowProps) {\n return (\n <tr class={() => (isSelected(item.id) ? 'danger' : '')}>\n <td key=\"id\" class=\"col-md-1\">\n {() => item.id}\n </td>\n <td key=\"label\" class=\"col-md-4\">\n <a\n onClick={(event: MouseEvent) => {\n event.preventDefault();\n onSelect(item.id);\n }}\n >\n {() => item.label}\n </a>\n </td>\n <td key=\"remove\" class=\"col-md-1\">\n <a\n onClick={(event: MouseEvent) => {\n event.preventDefault();\n onRemove(item.id);\n }}\n >\n <span class=\"glyphicon glyphicon-remove\" aria-hidden=\"true\" />\n </a>\n </td>\n <td key=\"spacer\" class=\"col-md-6\" />\n </tr>\n );\n}\n"],"mappings":";;;AAYA,SAAgB,aAAa,EAC3B,MACA,YACA,UACA,YACoB;CACpB,OACE,qBAAC,MAAD;EAAI,aAAc,WAAW,KAAK,GAAG,GAAG,WAAW;YAAnD;GACE,oBAAC,MAAD;IAAa,OAAM;oBACV,KAAK;IACT,EAFG,KAEH;GACL,oBAAC,MAAD;IAAgB,OAAM;cACpB,oBAAC,KAAD;KACE,UAAU,UAAsB;MAC9B,MAAM,gBAAgB;MACtB,SAAS,KAAK,GAAG;;qBAGZ,KAAK;KACV,CAAA;IACD,EATG,QASH;GACL,oBAAC,MAAD;IAAiB,OAAM;cACrB,oBAAC,KAAD;KACE,UAAU,UAAsB;MAC9B,MAAM,gBAAgB;MACtB,SAAS,KAAK,GAAG;;eAGnB,oBAAC,QAAD;MAAM,OAAM;MAA6B,eAAY;MAAS,CAAA;KAC5D,CAAA;IACD,EATG,SASH;GACL,oBAAC,MAAD,EAAiB,OAAM,YAAa,EAA5B,SAA4B;GACjC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark-table.js","names":[],"sources":["../../../src/bench/components/benchmark-table.tsx"],"sourcesContent":["import { For, type State } from '../../index';\nimport { BenchmarkRow, type BenchmarkRowData } from './benchmark-row';\n\ninterface BenchmarkTableProps {\n rows: State<BenchmarkRowData[]>;\n isSelected: (id: number) => boolean;\n onSelect: (id: number) => void;\n onRemove: (id: number) => void;\n}\n\nexport function BenchmarkTable({\n rows,\n isSelected,\n onSelect,\n onRemove,\n}: BenchmarkTableProps) {\n return (\n <table class=\"table table-hover table-striped test-data\">\n <tbody>\n {\n <For each={() => rows()} by={(item) => item.id}>\n {(item) => (\n <BenchmarkRow\n item={item}\n isSelected={isSelected}\n onSelect={onSelect}\n onRemove={onRemove}\n />\n )}\n </For>\n }\n </tbody>\n </table>\n );\n}\n"],"mappings":";;;;;;AAUA,SAAgB,eAAe,EAC7B,MACA,YACA,UACA,YACsB;
|
|
1
|
+
{"version":3,"file":"benchmark-table.js","names":[],"sources":["../../../src/bench/components/benchmark-table.tsx"],"sourcesContent":["import { For, type State } from '../../index';\nimport { BenchmarkRow, type BenchmarkRowData } from './benchmark-row';\n\ninterface BenchmarkTableProps {\n rows: State<BenchmarkRowData[]>;\n isSelected: (id: number) => boolean;\n onSelect: (id: number) => void;\n onRemove: (id: number) => void;\n}\n\nexport function BenchmarkTable({\n rows,\n isSelected,\n onSelect,\n onRemove,\n}: BenchmarkTableProps) {\n return (\n <table class=\"table table-hover table-striped test-data\">\n <tbody>\n {\n <For each={() => rows()} by={(item) => item.id}>\n {(item) => (\n <BenchmarkRow\n item={item}\n isSelected={isSelected}\n onSelect={onSelect}\n onRemove={onRemove}\n />\n )}\n </For>\n }\n </tbody>\n </table>\n );\n}\n"],"mappings":";;;;;;AAUA,SAAgB,eAAe,EAC7B,MACA,YACA,UACA,YACsB;CACtB,OACE,oBAAC,SAAD;EAAO,OAAM;YACX,oBAAC,SAAD,EAAA,UAEI,oBAAC,KAAD;GAAK,YAAY,MAAM;GAAE,KAAK,SAAS,KAAK;cACxC,SACA,oBAAC,cAAD;IACQ;IACM;IACF;IACA;IACV,CAAA;GAEA,CAAA,EAEF,CAAA;EACF,CAAA"}
|
package/dist/benchmark.js
CHANGED
|
@@ -10,8 +10,8 @@ import { BenchmarkTable } from "./bench/components/benchmark-table.js";
|
|
|
10
10
|
installRendererBridge();
|
|
11
11
|
const benchmarkMetadata = {
|
|
12
12
|
packageName: "@askrjs/askr",
|
|
13
|
-
packageVersion: "0.0.
|
|
14
|
-
buildLabel: "0.0.
|
|
13
|
+
packageVersion: "0.0.36",
|
|
14
|
+
buildLabel: "0.0.36-local"
|
|
15
15
|
};
|
|
16
16
|
function getBenchmarkMetadata() {
|
|
17
17
|
return { ...benchmarkMetadata };
|
package/dist/benchmark.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"benchmark.js","names":[],"sources":["../src/bench/benchmark-entry.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { createIsland } from '../boot';\nimport { installRendererBridge } from '../renderer';\nimport { selector } from '../runtime/selector';\nimport { state, State } from '../runtime/state';\nimport { globalScheduler } from '../runtime/scheduler';\nimport { BenchmarkTable } from './components/benchmark-table';\nimport type { BenchmarkRowData } from './components/benchmark-row';\n\ninstallRendererBridge();\n\ntype RowData = BenchmarkRowData;\n\nexport interface BenchmarkMetadata {\n packageName: string;\n packageVersion: string;\n buildLabel: string;\n}\n\nconst packageName =\n process.env.ASKR_PACKAGE_NAME ||\n process.env.npm_package_name ||\n '@askrjs/askr';\nconst packageVersion =\n process.env.ASKR_PACKAGE_VERSION ||\n process.env.npm_package_version ||\n '0.0.0';\n\nexport const benchmarkMetadata: BenchmarkMetadata = {\n packageName,\n packageVersion,\n buildLabel:\n process.env.ASKR_BENCHMARK_BUILD_LABEL || `${packageVersion}-local`,\n};\n\nexport function getBenchmarkMetadata(): BenchmarkMetadata {\n return { ...benchmarkMetadata };\n}\n\nexport function mountBenchmark(root: Element, initialRows?: RowData[]) {\n const initialRowsTyped: RowData[] = Array.isArray(initialRows)\n ? initialRows\n : [];\n let dataState!: State<RowData[]>;\n let selectedState!: State<number | null>;\n\n const App = () => {\n dataState = state<RowData[]>(initialRowsTyped);\n selectedState = state<number | null>(null);\n dataState._hasBeenRead = true;\n const isSelected = selector(selectedState);\n\n const select = (id: number) => selectedState.set(id);\n const remove = (id: number) => {\n dataState.set((rows) => rows.filter((item) => item.id !== id));\n selectedState.set((selected) => (selected === id ? null : selected));\n };\n\n return (\n <div class=\"container\">\n <BenchmarkTable\n rows={dataState}\n isSelected={isSelected}\n onSelect={select}\n onRemove={remove}\n />\n </div>\n );\n };\n\n createIsland({ root, component: App as any });\n globalScheduler.flush();\n\n return {\n setRows(rows: RowData[]) {\n dataState.set(rows);\n // Ensure work is flushed so external harnesses (like JFB) that check the DOM\n // immediately after calling `setRows` observe the committed DOM.\n globalScheduler.flush();\n },\n setSelected(id: number | null) {\n selectedState.set(id);\n globalScheduler.flush();\n },\n cleanup() {\n // nothing to do; outer test may clean the container\n },\n };\n}\n\nexport default { mountBenchmark };\n"],"mappings":";;;;;;;;;AASA,uBAAuB;AAmBvB,MAAa,oBAAuC;CAClD;CACA;CACA,YAAA;CAED;AAED,SAAgB,uBAA0C;
|
|
1
|
+
{"version":3,"file":"benchmark.js","names":[],"sources":["../src/bench/benchmark-entry.tsx"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-explicit-any */\nimport { createIsland } from '../boot';\nimport { installRendererBridge } from '../renderer';\nimport { selector } from '../runtime/selector';\nimport { state, State } from '../runtime/state';\nimport { globalScheduler } from '../runtime/scheduler';\nimport { BenchmarkTable } from './components/benchmark-table';\nimport type { BenchmarkRowData } from './components/benchmark-row';\n\ninstallRendererBridge();\n\ntype RowData = BenchmarkRowData;\n\nexport interface BenchmarkMetadata {\n packageName: string;\n packageVersion: string;\n buildLabel: string;\n}\n\nconst packageName =\n process.env.ASKR_PACKAGE_NAME ||\n process.env.npm_package_name ||\n '@askrjs/askr';\nconst packageVersion =\n process.env.ASKR_PACKAGE_VERSION ||\n process.env.npm_package_version ||\n '0.0.0';\n\nexport const benchmarkMetadata: BenchmarkMetadata = {\n packageName,\n packageVersion,\n buildLabel:\n process.env.ASKR_BENCHMARK_BUILD_LABEL || `${packageVersion}-local`,\n};\n\nexport function getBenchmarkMetadata(): BenchmarkMetadata {\n return { ...benchmarkMetadata };\n}\n\nexport function mountBenchmark(root: Element, initialRows?: RowData[]) {\n const initialRowsTyped: RowData[] = Array.isArray(initialRows)\n ? initialRows\n : [];\n let dataState!: State<RowData[]>;\n let selectedState!: State<number | null>;\n\n const App = () => {\n dataState = state<RowData[]>(initialRowsTyped);\n selectedState = state<number | null>(null);\n dataState._hasBeenRead = true;\n const isSelected = selector(selectedState);\n\n const select = (id: number) => selectedState.set(id);\n const remove = (id: number) => {\n dataState.set((rows) => rows.filter((item) => item.id !== id));\n selectedState.set((selected) => (selected === id ? null : selected));\n };\n\n return (\n <div class=\"container\">\n <BenchmarkTable\n rows={dataState}\n isSelected={isSelected}\n onSelect={select}\n onRemove={remove}\n />\n </div>\n );\n };\n\n createIsland({ root, component: App as any });\n globalScheduler.flush();\n\n return {\n setRows(rows: RowData[]) {\n dataState.set(rows);\n // Ensure work is flushed so external harnesses (like JFB) that check the DOM\n // immediately after calling `setRows` observe the committed DOM.\n globalScheduler.flush();\n },\n setSelected(id: number | null) {\n selectedState.set(id);\n globalScheduler.flush();\n },\n cleanup() {\n // nothing to do; outer test may clean the container\n },\n };\n}\n\nexport default { mountBenchmark };\n"],"mappings":";;;;;;;;;AASA,uBAAuB;AAmBvB,MAAa,oBAAuC;CAClD;CACA;CACA,YAAA;CAED;AAED,SAAgB,uBAA0C;CACxD,OAAO,EAAE,GAAG,mBAAmB;;AAGjC,SAAgB,eAAe,MAAe,aAAyB;CACrE,MAAM,mBAA8B,MAAM,QAAQ,YAAY,GAC1D,cACA,EAAE;CACN,IAAI;CACJ,IAAI;CAEJ,MAAM,YAAY;EAChB,YAAY,MAAiB,iBAAiB;EAC9C,gBAAgB,MAAqB,KAAK;EAC1C,UAAU,eAAe;EACzB,MAAM,aAAa,SAAS,cAAc;EAE1C,MAAM,UAAU,OAAe,cAAc,IAAI,GAAG;EACpD,MAAM,UAAU,OAAe;GAC7B,UAAU,KAAK,SAAS,KAAK,QAAQ,SAAS,KAAK,OAAO,GAAG,CAAC;GAC9D,cAAc,KAAK,aAAc,aAAa,KAAK,OAAO,SAAU;;EAGtE,OACE,oBAAC,OAAD;GAAK,OAAM;aACT,oBAAC,gBAAD;IACE,MAAM;IACM;IACZ,UAAU;IACV,UAAU;IACV,CAAA;GACE,CAAA;;CAIV,aAAa;EAAE;EAAM,WAAW;EAAY,CAAC;CAC7C,gBAAgB,OAAO;CAEvB,OAAO;EACL,QAAQ,MAAiB;GACvB,UAAU,IAAI,KAAK;GAGnB,gBAAgB,OAAO;;EAEzB,YAAY,IAAmB;GAC7B,cAAc,IAAI,GAAG;GACrB,gBAAgB,OAAO;;EAEzB,UAAU;EAGX;;AAGH,IAAA,0BAAe,EAAE,gBAAgB"}
|
package/dist/bin/askr-ssg.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"askr-ssg.js","names":[],"sources":["../../src/bin/askr-ssg.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * askr-ssg CLI\n *\n * Usage: askr-ssg --config path/to/config.ts --output dist/static\n */\n\nimport * as pathModule from 'path';\nimport * as fsModule from 'fs';\nimport { createStaticGen } from '../ssg/index';\nimport type { RouteConfig } from '../ssg/types';\nimport type { SSGGenerateOptions, SSGResult } from '../ssg/types';\n\nconst { resolve } = pathModule;\nconst { existsSync } = fsModule;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = resolve(filePath).replace(/\\\\/g, '/');\n const leadingSlash = normalized.startsWith('/') ? '' : '/';\n return `file://${leadingSlash}${encodeURI(normalized)}`;\n}\n\nexport interface ParsedCliArgs {\n configPath: string;\n outputDir: string;\n workers: number | 'auto';\n incremental: boolean;\n changedKeys: string[];\n changedRoutes: string[];\n forceFull: boolean;\n help: boolean;\n}\n\ninterface CliIo {\n log: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n}\n\ninterface CliDeps {\n cwd: () => string;\n now: () => number;\n existsSync: (path: string) => boolean;\n importConfig: (path: string) => Promise<Record<string, unknown>>;\n createStaticGen: typeof createStaticGen;\n}\n\ninterface CliConfig {\n routes: RouteConfig[];\n seed?: number;\n dataOverrides?: Record<string, unknown>;\n concurrency?: number;\n}\n\nconst defaultCliDeps: CliDeps = {\n cwd: () => process.cwd(),\n now: () => performance.now(),\n existsSync,\n importConfig: async (path: string) =>\n (await import(toFileUrl(path))) as Record<string, unknown>,\n createStaticGen,\n};\n\nconst helpText = `\naskr-ssg - Static Site Generation for Askr\n\nUsage:\n askr-ssg --config <path> --output <dir> [--incremental]\n\nOptions:\n --config <path> Path to SSG config file (TypeScript module)\n --output <dir> Output directory for generated HTML\n --workers <n|auto> Preferred render worker count for SSG throughput\n --incremental Use incremental generation if a manifest exists\n --changed-key <key> Mark an invalidation key as changed (repeatable)\n --changed-route <path> Mark a concrete route path as changed (repeatable)\n --force-full Force a full rebuild even with incremental flags\n --help Show this help message\n\nExample:\n askr-ssg --config ./ssg.config.ts --output ./dist/static --incremental --changed-key blog/post-123\n\nTypeScript config execution:\n Use tsx to run the CLI with TS config loading:\n tsx node_modules/@askrjs/askr/dist/bin/askr-ssg.js --config ./ssg.config.ts --output ./dist/static\n`;\n\nexport function parseCliArgs(args: string[]): ParsedCliArgs {\n const parsed: ParsedCliArgs = {\n configPath: '',\n outputDir: '',\n workers: 1,\n incremental: false,\n changedKeys: [],\n changedRoutes: [],\n forceFull: false,\n help: false,\n };\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--config' && i + 1 < args.length) {\n parsed.configPath = args[i + 1];\n i++;\n } else if (args[i] === '--output' && i + 1 < args.length) {\n parsed.outputDir = args[i + 1];\n i++;\n } else if (args[i] === '--workers' && i + 1 < args.length) {\n parsed.workers = args[i + 1] === 'auto' ? 'auto' : Number(args[i + 1]);\n i++;\n } else if (args[i] === '--changed-key' && i + 1 < args.length) {\n parsed.changedKeys.push(args[i + 1]);\n i++;\n } else if (args[i] === '--changed-route' && i + 1 < args.length) {\n parsed.changedRoutes.push(args[i + 1]);\n i++;\n } else if (args[i] === '--incremental') {\n parsed.incremental = true;\n } else if (args[i] === '--force-full') {\n parsed.forceFull = true;\n } else if (args[i] === '--help' || args[i] === '-h') {\n parsed.help = true;\n }\n }\n\n return parsed;\n}\n\nfunction toGenerateOptions(args: ParsedCliArgs): SSGGenerateOptions {\n return {\n mode: args.incremental ? 'incremental' : 'full',\n changedKeys: args.changedKeys,\n changedRoutes: args.changedRoutes,\n forceFull: args.forceFull,\n };\n}\n\nfunction printSummary(\n io: CliIo,\n outputDir: string,\n durationSeconds: string,\n result: SSGResult\n) {\n io.log('');\n io.log(`Generation complete in ${durationSeconds}s`);\n io.log(` Mode: ${result.mode}`);\n io.log(` Generated: ${result.successful}/${result.totalRoutes} routes`);\n io.log(` Failed: ${result.failed} routes`);\n io.log(` Rebuilt: ${result.rebuilt} routes`);\n io.log(` Skipped: ${result.skipped} routes`);\n io.log(` Removed: ${result.removed} routes`);\n io.log(` CacheHit: ${result.cacheHits} routes`);\n io.log(` Output: ${outputDir}`);\n io.log(` Metadata: ${outputDir}/metadata.json`);\n io.log('');\n}\n\nexport async function runCli(\n args: string[],\n deps: Partial<CliDeps> = {},\n io: CliIo = console\n): Promise<number> {\n const parsed = parseCliArgs(args);\n if (parsed.help) {\n io.log(helpText);\n return 0;\n }\n\n if (!parsed.configPath) {\n io.error('Error: --config argument is required');\n return 1;\n }\n\n if (!parsed.configPath.endsWith('.ts')) {\n io.error('Error: --config must point to a TypeScript file (.ts)');\n return 1;\n }\n\n if (!parsed.outputDir) {\n io.error('Error: --output argument is required');\n return 1;\n }\n\n const resolvedDeps = { ...defaultCliDeps, ...deps };\n const resolvedConfigPath = resolve(resolvedDeps.cwd(), parsed.configPath);\n const resolvedOutputDir = resolve(resolvedDeps.cwd(), parsed.outputDir);\n\n if (!resolvedDeps.existsSync(resolvedConfigPath)) {\n io.error(`Error: Config file not found: ${resolvedConfigPath}`);\n return 1;\n }\n\n try {\n io.log(`Loading config: ${resolvedConfigPath}`);\n\n const configModule = await resolvedDeps.importConfig(resolvedConfigPath);\n const config = (configModule.default || configModule) as Partial<CliConfig>;\n\n if (!Array.isArray(config.routes)) {\n io.error('Error: Config must export routes array');\n return 1;\n }\n\n io.log(`Generating ${config.routes.length} routes...`);\n\n const ssg = resolvedDeps.createStaticGen({\n routes: config.routes,\n outputDir: resolvedOutputDir,\n seed: config.seed,\n dataOverrides: config.dataOverrides,\n concurrency: config.concurrency,\n parallelism: parsed.workers,\n });\n\n const startTime = resolvedDeps.now();\n const result = await ssg.generate(toGenerateOptions(parsed));\n const duration = ((resolvedDeps.now() - startTime) / 1000).toFixed(2);\n\n printSummary(io, resolvedOutputDir, duration, result);\n\n if (result.failed > 0) {\n io.log('Errors encountered:');\n for (const route of result.routes) {\n if (route.status === 'error') {\n io.log(` ${route.path}: ${route.error}`);\n }\n }\n io.log('');\n return 1;\n }\n\n return 0;\n } catch (error) {\n io.error('Generation failed:');\n io.error(error instanceof Error ? error.message : String(error));\n if (error instanceof Error && error.stack) {\n io.error(error.stack);\n }\n return 1;\n }\n}\n\nasync function main() {\n const exitCode = await runCli(process.argv.slice(2));\n process.exit(exitCode);\n}\n\nif (process.argv[1] && import.meta.url === toFileUrl(process.argv[1])) {\n void main();\n}\n"],"mappings":";;;;;;;;;;;AAcA,MAAM,EAAE,YAAY;AACpB,MAAM,EAAE,eAAe;AAEvB,SAAS,UAAU,UAA0B;CAC3C,MAAM,aAAa,QAAQ,SAAS,CAAC,QAAQ,OAAO,IAAI;AAExD,QAAO,UADc,WAAW,WAAW,IAAI,GAAG,KAAK,MACvB,UAAU,WAAW;;AAkCvD,MAAM,iBAA0B;CAC9B,WAAW,QAAQ,KAAK;CACxB,WAAW,YAAY,KAAK;CAC5B;CACA,cAAc,OAAO,SAClB,MAAM,OAAO,UAAU,KAAK;CAC/B;CACD;AAED,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;AAwBjB,SAAgB,aAAa,MAA+B;CAC1D,MAAM,SAAwB;EAC5B,YAAY;EACZ,WAAW;EACX,SAAS;EACT,aAAa;EACb,aAAa,EAAE;EACf,eAAe,EAAE;EACjB,WAAW;EACX,MAAM;EACP;AAED,MAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,IAC/B,KAAI,KAAK,OAAO,cAAc,IAAI,IAAI,KAAK,QAAQ;AACjD,SAAO,aAAa,KAAK,IAAI;AAC7B;YACS,KAAK,OAAO,cAAc,IAAI,IAAI,KAAK,QAAQ;AACxD,SAAO,YAAY,KAAK,IAAI;AAC5B;YACS,KAAK,OAAO,eAAe,IAAI,IAAI,KAAK,QAAQ;AACzD,SAAO,UAAU,KAAK,IAAI,OAAO,SAAS,SAAS,OAAO,KAAK,IAAI,GAAG;AACtE;YACS,KAAK,OAAO,mBAAmB,IAAI,IAAI,KAAK,QAAQ;AAC7D,SAAO,YAAY,KAAK,KAAK,IAAI,GAAG;AACpC;YACS,KAAK,OAAO,qBAAqB,IAAI,IAAI,KAAK,QAAQ;AAC/D,SAAO,cAAc,KAAK,KAAK,IAAI,GAAG;AACtC;YACS,KAAK,OAAO,gBACrB,QAAO,cAAc;UACZ,KAAK,OAAO,eACrB,QAAO,YAAY;UACV,KAAK,OAAO,YAAY,KAAK,OAAO,KAC7C,QAAO,OAAO;AAIlB,QAAO;;AAGT,SAAS,kBAAkB,MAAyC;AAClE,QAAO;EACL,MAAM,KAAK,cAAc,gBAAgB;EACzC,aAAa,KAAK;EAClB,eAAe,KAAK;EACpB,WAAW,KAAK;EACjB;;AAGH,SAAS,aACP,IACA,WACA,iBACA,QACA;AACA,IAAG,IAAI,GAAG;AACV,IAAG,IAAI,0BAA0B,gBAAgB,GAAG;AACpD,IAAG,IAAI,iBAAiB,OAAO,OAAO;AACtC,IAAG,IAAI,iBAAiB,OAAO,WAAW,GAAG,OAAO,YAAY,SAAS;AACzE,IAAG,IAAI,iBAAiB,OAAO,OAAO,SAAS;AAC/C,IAAG,IAAI,iBAAiB,OAAO,QAAQ,SAAS;AAChD,IAAG,IAAI,iBAAiB,OAAO,QAAQ,SAAS;AAChD,IAAG,IAAI,iBAAiB,OAAO,QAAQ,SAAS;AAChD,IAAG,IAAI,iBAAiB,OAAO,UAAU,SAAS;AAClD,IAAG,IAAI,iBAAiB,YAAY;AACpC,IAAG,IAAI,iBAAiB,UAAU,gBAAgB;AAClD,IAAG,IAAI,GAAG;;AAGZ,eAAsB,OACpB,MACA,OAAyB,EAAE,EAC3B,KAAY,SACK;CACjB,MAAM,SAAS,aAAa,KAAK;AACjC,KAAI,OAAO,MAAM;AACf,KAAG,IAAI,SAAS;AAChB,SAAO;;AAGT,KAAI,CAAC,OAAO,YAAY;AACtB,KAAG,MAAM,uCAAuC;AAChD,SAAO;;AAGT,KAAI,CAAC,OAAO,WAAW,SAAS,MAAM,EAAE;AACtC,KAAG,MAAM,wDAAwD;AACjE,SAAO;;AAGT,KAAI,CAAC,OAAO,WAAW;AACrB,KAAG,MAAM,uCAAuC;AAChD,SAAO;;CAGT,MAAM,eAAe;EAAE,GAAG;EAAgB,GAAG;EAAM;CACnD,MAAM,qBAAqB,QAAQ,aAAa,KAAK,EAAE,OAAO,WAAW;CACzE,MAAM,oBAAoB,QAAQ,aAAa,KAAK,EAAE,OAAO,UAAU;AAEvE,KAAI,CAAC,aAAa,WAAW,mBAAmB,EAAE;AAChD,KAAG,MAAM,iCAAiC,qBAAqB;AAC/D,SAAO;;AAGT,KAAI;AACF,KAAG,IAAI,mBAAmB,qBAAqB;EAE/C,MAAM,eAAe,MAAM,aAAa,aAAa,mBAAmB;EACxE,MAAM,SAAU,aAAa,WAAW;AAExC,MAAI,CAAC,MAAM,QAAQ,OAAO,OAAO,EAAE;AACjC,MAAG,MAAM,yCAAyC;AAClD,UAAO;;AAGT,KAAG,IAAI,cAAc,OAAO,OAAO,OAAO,YAAY;EAEtD,MAAM,MAAM,aAAa,gBAAgB;GACvC,QAAQ,OAAO;GACf,WAAW;GACX,MAAM,OAAO;GACb,eAAe,OAAO;GACtB,aAAa,OAAO;GACpB,aAAa,OAAO;GACrB,CAAC;EAEF,MAAM,YAAY,aAAa,KAAK;EACpC,MAAM,SAAS,MAAM,IAAI,SAAS,kBAAkB,OAAO,CAAC;AAG5D,eAAa,IAAI,qBAFE,aAAa,KAAK,GAAG,aAAa,KAAM,QAAQ,EAE/B,EAAU,OAAO;AAErD,MAAI,OAAO,SAAS,GAAG;AACrB,MAAG,IAAI,sBAAsB;AAC7B,QAAK,MAAM,SAAS,OAAO,OACzB,KAAI,MAAM,WAAW,QACnB,IAAG,IAAI,MAAM,MAAM,KAAK,IAAI,MAAM,QAAQ;AAG9C,MAAG,IAAI,GAAG;AACV,UAAO;;AAGT,SAAO;UACA,OAAO;AACd,KAAG,MAAM,qBAAqB;AAC9B,KAAG,MAAM,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM,CAAC;AAChE,MAAI,iBAAiB,SAAS,MAAM,MAClC,IAAG,MAAM,MAAM,MAAM;AAEvB,SAAO;;;AAIX,eAAe,OAAO;CACpB,MAAM,WAAW,MAAM,OAAO,QAAQ,KAAK,MAAM,EAAE,CAAC;AACpD,SAAQ,KAAK,SAAS;;AAGxB,IAAI,QAAQ,KAAK,MAAM,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,GAAG,CACnE,CAAK,MAAM"}
|
|
1
|
+
{"version":3,"file":"askr-ssg.js","names":[],"sources":["../../src/bin/askr-ssg.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * askr-ssg CLI\n *\n * Usage: askr-ssg --config path/to/config.ts --output dist/static\n */\n\nimport * as pathModule from 'path';\nimport * as fsModule from 'fs';\nimport { createStaticGen } from '../ssg/index';\nimport type { RouteConfig } from '../ssg/types';\nimport type { SSGGenerateOptions, SSGResult } from '../ssg/types';\n\nconst { resolve } = pathModule;\nconst { existsSync } = fsModule;\n\nfunction toFileUrl(filePath: string): string {\n const normalized = resolve(filePath).replace(/\\\\/g, '/');\n const leadingSlash = normalized.startsWith('/') ? '' : '/';\n return `file://${leadingSlash}${encodeURI(normalized)}`;\n}\n\nexport interface ParsedCliArgs {\n configPath: string;\n outputDir: string;\n workers: number | 'auto';\n incremental: boolean;\n changedKeys: string[];\n changedRoutes: string[];\n forceFull: boolean;\n help: boolean;\n}\n\ninterface CliIo {\n log: (...args: unknown[]) => void;\n error: (...args: unknown[]) => void;\n}\n\ninterface CliDeps {\n cwd: () => string;\n now: () => number;\n existsSync: (path: string) => boolean;\n importConfig: (path: string) => Promise<Record<string, unknown>>;\n createStaticGen: typeof createStaticGen;\n}\n\ninterface CliConfig {\n routes: RouteConfig[];\n seed?: number;\n dataOverrides?: Record<string, unknown>;\n concurrency?: number;\n}\n\nconst defaultCliDeps: CliDeps = {\n cwd: () => process.cwd(),\n now: () => performance.now(),\n existsSync,\n importConfig: async (path: string) =>\n (await import(toFileUrl(path))) as Record<string, unknown>,\n createStaticGen,\n};\n\nconst helpText = `\naskr-ssg - Static Site Generation for Askr\n\nUsage:\n askr-ssg --config <path> --output <dir> [--incremental]\n\nOptions:\n --config <path> Path to SSG config file (TypeScript module)\n --output <dir> Output directory for generated HTML\n --workers <n|auto> Preferred render worker count for SSG throughput\n --incremental Use incremental generation if a manifest exists\n --changed-key <key> Mark an invalidation key as changed (repeatable)\n --changed-route <path> Mark a concrete route path as changed (repeatable)\n --force-full Force a full rebuild even with incremental flags\n --help Show this help message\n\nExample:\n askr-ssg --config ./ssg.config.ts --output ./dist/static --incremental --changed-key blog/post-123\n\nTypeScript config execution:\n Use tsx to run the CLI with TS config loading:\n tsx node_modules/@askrjs/askr/dist/bin/askr-ssg.js --config ./ssg.config.ts --output ./dist/static\n`;\n\nexport function parseCliArgs(args: string[]): ParsedCliArgs {\n const parsed: ParsedCliArgs = {\n configPath: '',\n outputDir: '',\n workers: 1,\n incremental: false,\n changedKeys: [],\n changedRoutes: [],\n forceFull: false,\n help: false,\n };\n\n for (let i = 0; i < args.length; i++) {\n if (args[i] === '--config' && i + 1 < args.length) {\n parsed.configPath = args[i + 1];\n i++;\n } else if (args[i] === '--output' && i + 1 < args.length) {\n parsed.outputDir = args[i + 1];\n i++;\n } else if (args[i] === '--workers' && i + 1 < args.length) {\n parsed.workers = args[i + 1] === 'auto' ? 'auto' : Number(args[i + 1]);\n i++;\n } else if (args[i] === '--changed-key' && i + 1 < args.length) {\n parsed.changedKeys.push(args[i + 1]);\n i++;\n } else if (args[i] === '--changed-route' && i + 1 < args.length) {\n parsed.changedRoutes.push(args[i + 1]);\n i++;\n } else if (args[i] === '--incremental') {\n parsed.incremental = true;\n } else if (args[i] === '--force-full') {\n parsed.forceFull = true;\n } else if (args[i] === '--help' || args[i] === '-h') {\n parsed.help = true;\n }\n }\n\n return parsed;\n}\n\nfunction toGenerateOptions(args: ParsedCliArgs): SSGGenerateOptions {\n return {\n mode: args.incremental ? 'incremental' : 'full',\n changedKeys: args.changedKeys,\n changedRoutes: args.changedRoutes,\n forceFull: args.forceFull,\n };\n}\n\nfunction printSummary(\n io: CliIo,\n outputDir: string,\n durationSeconds: string,\n result: SSGResult\n) {\n io.log('');\n io.log(`Generation complete in ${durationSeconds}s`);\n io.log(` Mode: ${result.mode}`);\n io.log(` Generated: ${result.successful}/${result.totalRoutes} routes`);\n io.log(` Failed: ${result.failed} routes`);\n io.log(` Rebuilt: ${result.rebuilt} routes`);\n io.log(` Skipped: ${result.skipped} routes`);\n io.log(` Removed: ${result.removed} routes`);\n io.log(` CacheHit: ${result.cacheHits} routes`);\n io.log(` Output: ${outputDir}`);\n io.log(` Metadata: ${outputDir}/metadata.json`);\n io.log('');\n}\n\nexport async function runCli(\n args: string[],\n deps: Partial<CliDeps> = {},\n io: CliIo = console\n): Promise<number> {\n const parsed = parseCliArgs(args);\n if (parsed.help) {\n io.log(helpText);\n return 0;\n }\n\n if (!parsed.configPath) {\n io.error('Error: --config argument is required');\n return 1;\n }\n\n if (!parsed.configPath.endsWith('.ts')) {\n io.error('Error: --config must point to a TypeScript file (.ts)');\n return 1;\n }\n\n if (!parsed.outputDir) {\n io.error('Error: --output argument is required');\n return 1;\n }\n\n const resolvedDeps = { ...defaultCliDeps, ...deps };\n const resolvedConfigPath = resolve(resolvedDeps.cwd(), parsed.configPath);\n const resolvedOutputDir = resolve(resolvedDeps.cwd(), parsed.outputDir);\n\n if (!resolvedDeps.existsSync(resolvedConfigPath)) {\n io.error(`Error: Config file not found: ${resolvedConfigPath}`);\n return 1;\n }\n\n try {\n io.log(`Loading config: ${resolvedConfigPath}`);\n\n const configModule = await resolvedDeps.importConfig(resolvedConfigPath);\n const config = (configModule.default || configModule) as Partial<CliConfig>;\n\n if (!Array.isArray(config.routes)) {\n io.error('Error: Config must export routes array');\n return 1;\n }\n\n io.log(`Generating ${config.routes.length} routes...`);\n\n const ssg = resolvedDeps.createStaticGen({\n routes: config.routes,\n outputDir: resolvedOutputDir,\n seed: config.seed,\n dataOverrides: config.dataOverrides,\n concurrency: config.concurrency,\n parallelism: parsed.workers,\n });\n\n const startTime = resolvedDeps.now();\n const result = await ssg.generate(toGenerateOptions(parsed));\n const duration = ((resolvedDeps.now() - startTime) / 1000).toFixed(2);\n\n printSummary(io, resolvedOutputDir, duration, result);\n\n if (result.failed > 0) {\n io.log('Errors encountered:');\n for (const route of result.routes) {\n if (route.status === 'error') {\n io.log(` ${route.path}: ${route.error}`);\n }\n }\n io.log('');\n return 1;\n }\n\n return 0;\n } catch (error) {\n io.error('Generation failed:');\n io.error(error instanceof Error ? error.message : String(error));\n if (error instanceof Error && error.stack) {\n io.error(error.stack);\n }\n return 1;\n }\n}\n\nasync function main() {\n const exitCode = await runCli(process.argv.slice(2));\n process.exit(exitCode);\n}\n\nif (process.argv[1] && import.meta.url === toFileUrl(process.argv[1])) {\n void main();\n}\n"],"mappings":"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"}
|
package/dist/boot/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/boot/index.ts"],"mappings":";;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/boot/index.ts"],"mappings":";;;;;;AAsIA;;;;iBAAgB,WAAA,CAAY,KAAA,EAAO,OAAA;AAAA,KA4GvB,YAAA;EACV,IAAA,EAAM,OAAA;EACN,SAAA,EAAW,iBAAA;EAEX,aAAA;EAEA,MAAA;AAAA;AAAA,KAGU,aAAA;EACV,OAAA,EAAS,YAAA;AAAA;AAAA,KAGC,SAAA;EACV,IAAA,EAAM,OAAA;EALI;;;;;AAIZ;;EASE,QAAA,GAAW,aAAA,EARL;EAUN,MAAA,GAAS,KAAA;EACT,IAAA,GAAO,gBAAA;EACP,iBAAA,aAA8B,wBAAA;EAC9B,aAAA;EACA,SAAA;AAAA;AAAA,KAGU,gBAAA;EACV,IAAA,EAAM,OAAA,WAVN;EAYA,QAAA,GAAW,aAAA,EAVX;EAYA,MAAA,GAAS,KAAA;EACT,IAAA,GAAO,gBAAA;EACP,iBAAA,aAA8B,wBAAA;EAC9B,aAAA;EACA,OAAA;IACE,YAAA;IACA,cAAA;IACA,cAAA;IACA,aAAA;IACA,aAAA;EAAA;AAAA;;;;iBAOY,YAAA,CAAa,MAAA,EAAQ,YAAA;;;;;iBA6CrB,aAAA,CAAc,MAAA,EAAQ,aAAA;;;;;;;;;;;;;;;iBA2BhB,SAAA,CAAU,MAAA,EAAQ,SAAA,GAAY,OAAA;;;AAxEpD;;iBAqXsB,UAAA,CAAW,MAAA,EAAQ,gBAAA,GAAmB,OAAA;;;AAxU5D;;iBAsbgB,UAAA,CAAW,IAAA,EAAM,OAAA;;;AA3ZjC;iBA4agB,MAAA,CAAO,IAAA,EAAM,OAAA"}
|
package/dist/boot/index.js
CHANGED
|
@@ -17,7 +17,7 @@ import { configureScrollRestoration, initializeNavigation, registerAppInstance }
|
|
|
17
17
|
const HAS_ROUTES_KEY = Symbol.for("__ASKR_HAS_ROUTES__");
|
|
18
18
|
let componentIdCounter = 0;
|
|
19
19
|
const instancesByRoot = /* @__PURE__ */ new WeakMap();
|
|
20
|
-
const CLEANUP_SYMBOL = Symbol.for("
|
|
20
|
+
const CLEANUP_SYMBOL = Symbol.for("__askrCleanup__");
|
|
21
21
|
function attachCleanupForRoot(rootElement, instance) {
|
|
22
22
|
rootElement[CLEANUP_SYMBOL] = () => {
|
|
23
23
|
const errors = [];
|
|
@@ -26,28 +26,6 @@ function attachCleanupForRoot(rootElement, instance) {
|
|
|
26
26
|
} catch (e) {
|
|
27
27
|
errors.push(e);
|
|
28
28
|
}
|
|
29
|
-
try {
|
|
30
|
-
const descendants = rootElement.querySelectorAll("*");
|
|
31
|
-
for (const d of Array.from(descendants)) try {
|
|
32
|
-
const inst = d.__ASKR_INSTANCE;
|
|
33
|
-
if (inst) {
|
|
34
|
-
try {
|
|
35
|
-
cleanupComponent(inst);
|
|
36
|
-
} catch (err) {
|
|
37
|
-
errors.push(err);
|
|
38
|
-
}
|
|
39
|
-
try {
|
|
40
|
-
delete d.__ASKR_INSTANCE;
|
|
41
|
-
} catch (err) {
|
|
42
|
-
errors.push(err);
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
} catch (err) {
|
|
46
|
-
errors.push(err);
|
|
47
|
-
}
|
|
48
|
-
} catch (e) {
|
|
49
|
-
errors.push(e);
|
|
50
|
-
}
|
|
51
29
|
try {
|
|
52
30
|
cleanupComponent(instance);
|
|
53
31
|
} catch (e) {
|
|
@@ -240,7 +218,9 @@ async function createSPA(config) {
|
|
|
240
218
|
* Mark elements that should be skipped during hydration
|
|
241
219
|
*/
|
|
242
220
|
function markSkippedElements(root, skipSelectors) {
|
|
243
|
-
|
|
221
|
+
if (skipSelectors.length === 0) return;
|
|
222
|
+
const selectorList = Array.from(new Set(skipSelectors)).join(", ");
|
|
223
|
+
root.querySelectorAll(selectorList).forEach((el) => el.setAttribute("data-skip-hydrate", "true"));
|
|
244
224
|
}
|
|
245
225
|
function collectDeferredBelowFoldBoundaries(root, foldY) {
|
|
246
226
|
const boundaries = [];
|
|
@@ -364,12 +344,12 @@ async function hydrateSPA(config) {
|
|
|
364
344
|
if (isProductionEnvironment()) lockRouteRegistration();
|
|
365
345
|
const resolved = resolveRoute(path);
|
|
366
346
|
if (!resolved) throw new Error(`hydrateSPA: no route found for current path (${path}).`);
|
|
367
|
-
const legacyRouteTable = hasManifest ? config.manifest.records.map((r) => ({
|
|
368
|
-
path: r.path,
|
|
369
|
-
handler: r.handler,
|
|
370
|
-
namespace: r.options.namespace
|
|
371
|
-
})) : config.routes;
|
|
372
347
|
if (shouldVerifyHydrationMarkup(config)) {
|
|
348
|
+
const legacyRouteTable = hasManifest ? config.manifest.records.map((r) => ({
|
|
349
|
+
path: r.path,
|
|
350
|
+
handler: r.handler,
|
|
351
|
+
namespace: r.options.namespace
|
|
352
|
+
})) : config.routes;
|
|
373
353
|
const { verifyHydrationSyncForUrl } = await import("../ssr/index.js");
|
|
374
354
|
if (!verifyHydrationSyncForUrl({
|
|
375
355
|
root: rootElement,
|
package/dist/boot/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../src/boot/index.ts"],"sourcesContent":["/**\n * App bootstrap and mount\n */\n\nimport {\n createComponentInstance,\n mountComponent,\n cleanupComponent,\n type ComponentFunction,\n type ComponentInstance,\n} from '../runtime/component';\nimport {\n isDevelopmentEnvironment,\n isProductionEnvironment,\n} from '../common/env';\nimport { logger } from '../dev/logger';\nimport { initializeNavigation, registerAppInstance } from '../router/navigate';\nimport {\n configureScrollRestoration,\n type ScrollRestorationOptions,\n} from '../router/navigate';\nimport {\n _applyManifest,\n _drainLazy,\n _setActiveRouteAuthOptions,\n _snapshotLazy,\n clearRoutes,\n lockRouteRegistration,\n resolveRoute,\n resolveRouteRequest,\n route as registerRoute,\n setServerLocation,\n} from '../router/route';\nimport { globalScheduler } from '../runtime/scheduler';\nimport { assertExecutionModel } from '../runtime/execution-model';\n\nconst HAS_ROUTES_KEY = Symbol.for('__ASKR_HAS_ROUTES__');\n\nlet componentIdCounter = 0;\n\n// Track instances by root element to support multiple createIsland calls on same root\nconst instancesByRoot = new WeakMap<Element, ComponentInstance>();\n\n// Symbol for storing cleanup on elements\nconst CLEANUP_SYMBOL = Symbol.for('__tempoCleanup__');\n\n// Type for elements that have cleanup functions attached\ninterface ElementWithCleanup extends Element {\n [CLEANUP_SYMBOL]?: () => void;\n}\n\nfunction attachCleanupForRoot(\n rootElement: Element,\n instance: ComponentInstance\n) {\n (rootElement as ElementWithCleanup)[CLEANUP_SYMBOL] = () => {\n // Attempt to remove listeners and cleanup instances under the root.\n // In non-strict mode we preserve previous behavior by swallowing errors\n // (but logging in dev); in strict mode we aggregate and re-throw.\n const errors: unknown[] = [];\n try {\n removeAllListeners(rootElement);\n } catch (e) {\n errors.push(e);\n }\n\n // Manually traverse descendants and attempt to cleanup their instances.\n // Avoids import cycles by using local traversal and existing cleanupComponent.\n try {\n const descendants = rootElement.querySelectorAll('*');\n for (const d of Array.from(descendants)) {\n try {\n const inst = (d as Element & { __ASKR_INSTANCE?: ComponentInstance })\n .__ASKR_INSTANCE;\n if (inst) {\n try {\n cleanupComponent(inst);\n } catch (err) {\n errors.push(err);\n }\n try {\n delete (d as Element & { __ASKR_INSTANCE?: ComponentInstance })\n .__ASKR_INSTANCE;\n } catch (err) {\n errors.push(err);\n }\n }\n } catch (err) {\n errors.push(err);\n }\n }\n } catch (e) {\n errors.push(e);\n }\n\n try {\n cleanupComponent(instance as ComponentInstance);\n } catch (e) {\n errors.push(e);\n }\n\n if (errors.length > 0) {\n if (instance.cleanupStrict) {\n throw new AggregateError(errors, `cleanup failed for app root`);\n } else if (isDevelopmentEnvironment()) {\n for (const err of errors) logger.warn('[Askr] cleanup error:', err);\n }\n }\n };\n\n try {\n const descriptor =\n Object.getOwnPropertyDescriptor(rootElement, 'innerHTML') ||\n Object.getOwnPropertyDescriptor(\n Object.getPrototypeOf(rootElement),\n 'innerHTML'\n ) ||\n Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');\n\n if (descriptor && (descriptor.get || descriptor.set)) {\n Object.defineProperty(rootElement, 'innerHTML', {\n get: descriptor.get\n ? function (this: Element) {\n return descriptor.get!.call(this);\n }\n : undefined,\n set: function (this: Element, value: string) {\n if (value === '' && instancesByRoot.get(this) === instance) {\n try {\n removeAllListeners(rootElement);\n } catch (e) {\n if (instance.cleanupStrict) throw e;\n if (isDevelopmentEnvironment()) {\n logger.warn('[Askr] cleanup error:', e);\n }\n }\n\n try {\n cleanupComponent(instance as ComponentInstance);\n } catch (e) {\n if (instance.cleanupStrict) throw e;\n if (isDevelopmentEnvironment()) {\n logger.warn('[Askr] cleanup error:', e);\n }\n }\n }\n if (descriptor.set) {\n return descriptor.set.call(this, value);\n }\n },\n configurable: true,\n });\n }\n } catch {\n // If Object.defineProperty fails, ignore\n }\n}\n\n/**\n * Explicitly teardown an app mounted on `root` if present. This is the\n * recommended API for deterministic cleanup rather than relying on overriding\n * `innerHTML` setter behavior.\n */\nexport function teardownApp(_root: Element | string) {\n throw new Error(\n 'The `teardownApp` alias has been removed. Use `cleanupApp(root)` instead.'\n );\n}\n\nimport { Fragment, ELEMENT_TYPE } from '../jsx';\n\nimport { DefaultPortal } from '../foundations/structures/portal';\n\nfunction mountOrUpdate(\n rootElement: Element,\n componentFn: ComponentFunction,\n options?: { cleanupStrict?: boolean }\n) {\n // Ensure root component always includes a DefaultPortal host by wrapping it.\n const wrappedFn: ComponentFunction = (props, ctx) => {\n const out = componentFn(props, ctx);\n const portalVNode = {\n $$typeof: ELEMENT_TYPE,\n type: DefaultPortal,\n props: {},\n key: '__default_portal',\n } as unknown;\n return {\n $$typeof: ELEMENT_TYPE,\n type: Fragment,\n props: {\n children:\n out === undefined || out === null\n ? [portalVNode]\n : [out, portalVNode],\n },\n } as unknown as ReturnType<ComponentFunction>;\n };\n // Preserve the original component name for debugging/dev warnings\n Object.defineProperty(wrappedFn, 'name', {\n value: componentFn.name || 'Component',\n });\n\n // Clean up existing cleanup function before mounting new one\n const existingCleanup = (rootElement as ElementWithCleanup)[CLEANUP_SYMBOL];\n if (existingCleanup) existingCleanup();\n\n let instance = instancesByRoot.get(rootElement);\n\n if (instance) {\n removeAllListeners(rootElement);\n try {\n cleanupComponent(instance);\n } catch (e) {\n // If previous cleanup threw in strict mode, log but continue mounting new instance\n if (isDevelopmentEnvironment()) {\n logger.warn('[Askr] prior cleanup threw:', e);\n }\n }\n\n instance.fn = wrappedFn;\n instance.evaluationGeneration++;\n instance.mounted = false;\n instance.expectedStateIndices = [];\n instance.firstRenderComplete = false;\n instance.isRoot = true;\n // Update strict flag if provided\n if (options && typeof options.cleanupStrict === 'boolean') {\n instance.cleanupStrict = options.cleanupStrict;\n }\n } else {\n const componentId = String(++componentIdCounter);\n instance = createComponentInstance(componentId, wrappedFn, {}, rootElement);\n instancesByRoot.set(rootElement, instance);\n instance.isRoot = true;\n // Initialize strict flag from options\n if (options && typeof options.cleanupStrict === 'boolean') {\n instance.cleanupStrict = options.cleanupStrict;\n }\n }\n\n attachCleanupForRoot(rootElement, instance);\n mountComponent(instance);\n globalScheduler.flush();\n}\n\nfunction bindResolvedRouteHandler(resolved: ResolvedRoute): ComponentFunction {\n return () =>\n resolved.handler(resolved.params) as ReturnType<ComponentFunction>;\n}\n\nfunction bindDeniedStatus(status: number): ComponentFunction {\n return () => ({\n type: 'div',\n props: {\n 'data-route-denied': String(status),\n },\n children: [String(status)],\n });\n}\n\n// New strongly-typed init functions\nimport type {\n Route,\n RouteManifest,\n ResolvedRoute,\n RouteAuthOptions,\n} from '../common/router';\nimport { installRendererBridge, removeAllListeners } from '../renderer';\ninstallRendererBridge();\n\nexport type IslandConfig = {\n root: Element | string;\n component: ComponentFunction;\n // Optional: surface cleanup errors during teardown for this island\n cleanupStrict?: boolean;\n // Explicitly disallow routes on islands at type level\n routes?: never;\n};\n\nexport type IslandsConfig = {\n islands: IslandConfig[];\n};\n\nexport type SPAConfig = {\n root: Element | string;\n /**\n * Preferred: pass the route manifest built via `registerRoutes(() => { ... })`.\n * ```ts\n * import { getManifest } from '@askrjs/askr/router';\n * await createSPA({ root: '#app', manifest: getManifest() });\n * ```\n */\n manifest?: RouteManifest;\n /** Legacy: flat route array — kept for backward compatibility and test fixtures. */\n routes?: Route[];\n auth?: RouteAuthOptions;\n scrollRestoration?: boolean | ScrollRestorationOptions;\n cleanupStrict?: boolean;\n component?: never;\n};\n\nexport type HydrateSPAConfig = {\n root: Element | string;\n /** Preferred manifest input — see `SPAConfig.manifest`. */\n manifest?: RouteManifest;\n /** Legacy flat route array. */\n routes?: Route[];\n auth?: RouteAuthOptions;\n scrollRestoration?: boolean | ScrollRestorationOptions;\n cleanupStrict?: boolean;\n hydrate?: {\n verifyMarkup?: boolean;\n deferUntilIdle?: boolean;\n deferBelowFold?: boolean;\n foldThreshold?: number;\n skipSelectors?: string[];\n };\n};\n\n/**\n * createIsland: Enhances existing DOM (no router, mounts once)\n */\nexport function createIsland(config: IslandConfig): void {\n assertExecutionModel('islands');\n if (!config || typeof config !== 'object') {\n throw new Error('createIsland requires a config object');\n }\n if (typeof config.component !== 'function') {\n throw new Error('createIsland: component must be a function');\n }\n\n const rootElement =\n typeof config.root === 'string'\n ? document.getElementById(config.root)\n : config.root;\n if (!rootElement) throw new Error(`Root element not found: ${config.root}`);\n\n // Islands must not initialize router or routes\n if ('routes' in config) {\n throw new Error(\n 'createIsland does not accept routes; use createSPA for routed apps'\n );\n }\n\n // Routes are never supported with islands.\n // If routes were registered (even at module load time), fail fast to avoid\n // surprising partial router behavior.\n try {\n const g = globalThis as unknown as Record<string | symbol, unknown>;\n if (g[HAS_ROUTES_KEY]) {\n throw new Error(\n 'Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead.'\n );\n }\n } catch {\n // ignore\n }\n\n mountOrUpdate(rootElement, config.component, {\n cleanupStrict: config.cleanupStrict,\n });\n}\n\n/**\n * createIslands: Enhances one or more existing DOM roots (no router).\n * The only public islands constructor.\n */\nexport function createIslands(config: IslandsConfig): void {\n assertExecutionModel('islands');\n if (!config || typeof config !== 'object') {\n throw new Error('createIslands requires a config object');\n }\n if (!Array.isArray(config.islands) || config.islands.length === 0) {\n throw new Error('createIslands requires a non-empty islands array');\n }\n for (const island of config.islands) {\n createIsland(island);\n }\n}\n\n/**\n * createSPA: Initializes router and mounts the app with the provided route manifest or route table.\n *\n * Preferred usage with manifest:\n * ```ts\n * import { getManifest } from '@askrjs/askr/router';\n * await createSPA({ root: '#app', manifest: getManifest() });\n * ```\n *\n * Legacy usage with plain routes array (still supported):\n * ```ts\n * await createSPA({ root: '#app', routes: getRoutes() });\n * ```\n */\nexport async function createSPA(config: SPAConfig): Promise<void> {\n assertExecutionModel('spa');\n if (!config || typeof config !== 'object') {\n throw new Error('createSPA requires a config object');\n }\n\n const hasManifest =\n config.manifest != null && config.manifest.records.length > 0;\n const hasRoutes = Array.isArray(config.routes) && config.routes.length > 0;\n\n if (!hasManifest && !hasRoutes) {\n throw new Error(\n 'createSPA requires a route manifest or route table. ' +\n 'Pass `manifest: getManifest()` or `routes: getRoutes()`. ' +\n 'If you are enhancing existing HTML, use createIsland instead.'\n );\n }\n\n const rootElement =\n typeof config.root === 'string'\n ? document.getElementById(config.root)\n : config.root;\n if (!rootElement) throw new Error(`Root element not found: ${config.root}`);\n\n const pendingLazyAtBoot = _snapshotLazy();\n\n configureScrollRestoration(config.scrollRestoration);\n\n clearRoutes();\n\n if (hasManifest) {\n // Preferred path: apply pre-built manifest records directly\n _applyManifest(config.manifest!);\n } else {\n // Legacy path: register plain Route objects (no layout metadata)\n for (const r of config.routes!) {\n registerRoute(r.path, r.handler as Parameters<typeof registerRoute>[1]);\n }\n }\n\n const routeAuth = config.auth ?? config.manifest?.auth;\n _setActiveRouteAuthOptions(routeAuth);\n\n // Drain any lazy() imports so all split chunks are ready before mounting\n await _drainLazy(pendingLazyAtBoot);\n\n // Lock registration in production to prevent late registration surprises\n if (isProductionEnvironment()) lockRouteRegistration();\n\n // Mount the currently-resolved route handler (if any)\n let path = typeof window !== 'undefined' ? window.location.pathname : '/';\n let href =\n typeof window !== 'undefined'\n ? `${window.location.pathname}${window.location.search}${window.location.hash}`\n : path;\n let resolved = await resolveRouteRequest(href, { auth: routeAuth });\n\n while (\n typeof window !== 'undefined' &&\n resolved &&\n resolved.kind === 'redirect'\n ) {\n const redirectTarget = new URL(resolved.to, window.location.href);\n const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;\n window.history.replaceState({ path: redirectHref }, '', redirectHref);\n path = redirectTarget.pathname;\n href = redirectHref;\n resolved = await resolveRouteRequest(href, { auth: routeAuth });\n }\n\n if (!resolved) {\n mountOrUpdate(rootElement, () => ({ type: 'div', children: [] }), {\n cleanupStrict: config.cleanupStrict,\n });\n\n await registerAppNavigation(rootElement, path);\n return;\n }\n\n if (resolved.kind === 'redirect') {\n mountOrUpdate(rootElement, () => ({ type: 'div', children: [] }), {\n cleanupStrict: config.cleanupStrict,\n });\n\n await registerAppNavigation(rootElement, path);\n return;\n }\n\n mountOrUpdate(\n rootElement,\n resolved.kind === 'deny'\n ? bindDeniedStatus(resolved.status)\n : bindResolvedRouteHandler({\n handler: resolved.handler,\n params: resolved.params,\n }),\n {\n cleanupStrict: config.cleanupStrict,\n }\n );\n\n await registerAppNavigation(rootElement, path);\n}\n\n/**\n * Mark elements that should be skipped during hydration\n */\nfunction markSkippedElements(root: Element, skipSelectors: string[]): void {\n for (const selector of skipSelectors) {\n const elements = root.querySelectorAll(selector);\n elements.forEach((el) => el.setAttribute('data-skip-hydrate', 'true'));\n }\n}\n\nfunction collectDeferredBelowFoldBoundaries(\n root: Element,\n foldY: number\n): Element[] {\n const boundaries: Element[] = [];\n const stack: Element[] = [];\n\n for (let index = root.children.length - 1; index >= 0; index -= 1) {\n stack.push(root.children[index]);\n }\n\n while (stack.length > 0) {\n const element = stack.pop()!;\n\n if (element.hasAttribute('data-skip-hydrate')) {\n continue;\n }\n\n const rect = element.getBoundingClientRect();\n if (rect.top >= foldY) {\n element.setAttribute('data-skip-hydrate', 'true');\n boundaries.push(element);\n continue;\n }\n\n for (let index = element.children.length - 1; index >= 0; index -= 1) {\n stack.push(element.children[index]);\n }\n }\n\n return boundaries;\n}\n\nfunction activateVisibleDeferredBoundaries(\n boundaries: Element[],\n foldY: number\n): { activated: boolean; remaining: number } {\n let activated = false;\n let remaining = 0;\n\n for (const element of boundaries) {\n if (!element.hasAttribute('data-skip-hydrate')) {\n continue;\n }\n\n const rect = element.getBoundingClientRect();\n if (rect.top < foldY) {\n element.removeAttribute('data-skip-hydrate');\n activated = true;\n } else {\n remaining += 1;\n }\n }\n\n return { activated, remaining };\n}\n\nfunction queueIdleWork(work: () => void): Promise<void> {\n return new Promise((resolve) => {\n if (typeof requestIdleCallback !== 'undefined') {\n requestIdleCallback(\n () => {\n work();\n resolve();\n },\n { timeout: 2000 }\n );\n return;\n }\n\n setTimeout(() => {\n work();\n resolve();\n }, 0);\n });\n}\n\nfunction flushHydrationActivation(rootElement: Element): void {\n const instance = instancesByRoot.get(rootElement);\n if (!instance) return;\n instance._enqueueRun?.();\n globalScheduler.flush();\n}\n\nfunction shouldVerifyHydrationMarkup(config: HydrateSPAConfig): boolean {\n const explicit = config.hydrate?.verifyMarkup;\n if (typeof explicit === 'boolean') {\n return explicit;\n }\n\n return !isProductionEnvironment();\n}\n\nasync function registerAppNavigation(rootElement: Element, path: string) {\n const instance = instancesByRoot.get(rootElement);\n if (!instance) throw new Error('Internal error: app instance missing');\n registerAppInstance(instance as ComponentInstance, path);\n initializeNavigation();\n}\n\n/**\n * Apply selective hydration with deferral options\n */\nasync function applySelectiveHydration(\n rootElement: Element,\n resolved: { handler: ComponentFunction; params: Record<string, unknown> },\n path: string,\n cleanupStrict: boolean | undefined,\n hydrateOptions: NonNullable<HydrateSPAConfig['hydrate']>\n): Promise<void> {\n const hasPermanentSkips = (hydrateOptions.skipSelectors?.length ?? 0) > 0;\n const hasBelowFoldDeferral = !!hydrateOptions.deferBelowFold;\n const hasSelectiveBoundaries = hasPermanentSkips || hasBelowFoldDeferral;\n\n if (hydrateOptions.skipSelectors?.length) {\n markSkippedElements(rootElement, hydrateOptions.skipSelectors);\n }\n\n let deferredBoundaries: Element[] = [];\n if (hydrateOptions.deferBelowFold) {\n const foldY = hydrateOptions.foldThreshold ?? window.innerHeight;\n deferredBoundaries = collectDeferredBelowFoldBoundaries(rootElement, foldY);\n\n const handleScroll = () => {\n const { activated, remaining } = activateVisibleDeferredBoundaries(\n deferredBoundaries,\n foldY\n );\n\n if (!activated) {\n return;\n }\n\n flushHydrationActivation(rootElement);\n\n if (remaining === 0) {\n window.removeEventListener('scroll', handleScroll);\n }\n };\n\n window.addEventListener('scroll', handleScroll, { passive: true });\n }\n\n if (hydrateOptions.deferUntilIdle && !hasSelectiveBoundaries) {\n await queueIdleWork(() => {\n mountOrUpdate(\n rootElement,\n (() => resolved.handler(resolved.params)) as ComponentFunction,\n {\n cleanupStrict,\n }\n );\n });\n await registerAppNavigation(rootElement, path);\n return;\n }\n\n mountOrUpdate(\n rootElement,\n (() => resolved.handler(resolved.params)) as ComponentFunction,\n {\n cleanupStrict,\n }\n );\n await registerAppNavigation(rootElement, path);\n\n if (hydrateOptions.deferUntilIdle && deferredBoundaries.length > 0) {\n await queueIdleWork(() => {\n const { activated } = activateVisibleDeferredBoundaries(\n deferredBoundaries,\n Number.POSITIVE_INFINITY\n );\n if (activated) {\n flushHydrationActivation(rootElement);\n }\n });\n }\n}\n\n/**\n * hydrateSPA: Hydrate server-rendered HTML.\n * Accepts either a `manifest` (preferred) or a legacy `routes` array.\n */\nexport async function hydrateSPA(config: HydrateSPAConfig): Promise<void> {\n assertExecutionModel('spa');\n if (!config || typeof config !== 'object') {\n throw new Error('hydrateSPA requires a config object');\n }\n\n const hasManifest =\n config.manifest != null && config.manifest.records.length > 0;\n const hasRoutes = Array.isArray(config.routes) && config.routes.length > 0;\n\n if (!hasManifest && !hasRoutes) {\n throw new Error(\n 'hydrateSPA requires a route manifest or route table. ' +\n 'Pass `manifest: getManifest()` or `routes: getRoutes()`. ' +\n 'If you are enhancing existing HTML, use createIsland instead.'\n );\n }\n\n const rootElement =\n typeof config.root === 'string'\n ? document.getElementById(config.root)\n : config.root;\n if (!rootElement) throw new Error(`Root element not found: ${config.root}`);\n\n const pendingLazyAtHydrationBoot = _snapshotLazy();\n\n configureScrollRestoration(config.scrollRestoration);\n\n clearRoutes();\n\n if (hasManifest) {\n _applyManifest(config.manifest!);\n } else {\n for (const r of config.routes!) {\n registerRoute(r.path, r.handler as Parameters<typeof registerRoute>[1]);\n }\n }\n\n _setActiveRouteAuthOptions(config.auth ?? config.manifest?.auth);\n\n // Drain any lazy() imports so all split chunks are ready before mounting\n await _drainLazy(pendingLazyAtHydrationBoot);\n\n const path = typeof window !== 'undefined' ? window.location.pathname : '/';\n const currentUrl =\n typeof window !== 'undefined'\n ? `${window.location.pathname}${window.location.search}${window.location.hash}`\n : path;\n setServerLocation(currentUrl);\n if (isProductionEnvironment()) lockRouteRegistration();\n\n const resolved = resolveRoute(path);\n if (!resolved) {\n throw new Error(`hydrateSPA: no route found for current path (${path}).`);\n }\n\n // Build a legacy-compatible routes array for the hydration verify call\n const legacyRouteTable = hasManifest\n ? config.manifest!.records.map((r) => ({\n path: r.path,\n handler: r.handler,\n namespace: r.options.namespace,\n }))\n : config.routes!;\n\n if (shouldVerifyHydrationMarkup(config)) {\n const { verifyHydrationSyncForUrl } = await import('../ssr');\n if (\n !verifyHydrationSyncForUrl({\n root: rootElement,\n url: currentUrl,\n routes: legacyRouteTable,\n })\n ) {\n throw new Error(\n '[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output.'\n );\n }\n }\n\n const hydrateOptions = config.hydrate;\n if (hydrateOptions) {\n if (hydrateOptions.deferUntilIdle || hydrateOptions.deferBelowFold) {\n await applySelectiveHydration(\n rootElement,\n {\n handler: resolved.handler as ComponentFunction,\n params: resolved.params as Record<string, unknown>,\n },\n path,\n config.cleanupStrict,\n hydrateOptions\n );\n return;\n }\n\n if (hydrateOptions.skipSelectors?.length) {\n markSkippedElements(rootElement, hydrateOptions.skipSelectors);\n }\n }\n\n mountOrUpdate(rootElement, bindResolvedRouteHandler(resolved), {\n cleanupStrict: config.cleanupStrict,\n });\n await registerAppNavigation(rootElement, path);\n}\n\n/**\n * Cleanup an app mounted on a root element (element or id).\n * Safe to call multiple times — no-op when nothing is mounted.\n */\nexport function cleanupApp(root: Element | string): void {\n const rootElement =\n typeof root === 'string' ? document.getElementById(root) : root;\n\n if (!rootElement) return;\n\n const cleanupFn = (rootElement as ElementWithCleanup)[CLEANUP_SYMBOL];\n if (typeof cleanupFn === 'function') {\n cleanupFn();\n }\n\n instancesByRoot.delete(rootElement);\n}\n\n/**\n * Check whether an app is mounted on the given root\n */\nexport function hasApp(root: Element | string): boolean {\n const rootElement =\n typeof root === 'string' ? document.getElementById(root) : root;\n\n if (!rootElement) return false;\n return instancesByRoot.has(rootElement);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../src/boot/index.ts"],"sourcesContent":["/**\n * App bootstrap and mount\n */\n\nimport {\n createComponentInstance,\n mountComponent,\n cleanupComponent,\n type ComponentFunction,\n type ComponentInstance,\n} from '../runtime/component';\nimport {\n isDevelopmentEnvironment,\n isProductionEnvironment,\n} from '../common/env';\nimport { logger } from '../dev/logger';\nimport { initializeNavigation, registerAppInstance } from '../router/navigate';\nimport {\n configureScrollRestoration,\n type ScrollRestorationOptions,\n} from '../router/navigate';\nimport {\n _applyManifest,\n _drainLazy,\n _setActiveRouteAuthOptions,\n _snapshotLazy,\n clearRoutes,\n lockRouteRegistration,\n resolveRoute,\n resolveRouteRequest,\n route as registerRoute,\n setServerLocation,\n} from '../router/route';\nimport { globalScheduler } from '../runtime/scheduler';\nimport { assertExecutionModel } from '../runtime/execution-model';\n\nconst HAS_ROUTES_KEY = Symbol.for('__ASKR_HAS_ROUTES__');\n\nlet componentIdCounter = 0;\n\n// Track instances by root element to support multiple createIsland calls on same root\nconst instancesByRoot = new WeakMap<Element, ComponentInstance>();\n\n// Symbol for storing cleanup on elements\nconst CLEANUP_SYMBOL = Symbol.for('__askrCleanup__');\n\n// Type for elements that have cleanup functions attached\ninterface ElementWithCleanup extends Element {\n [CLEANUP_SYMBOL]?: () => void;\n}\n\nfunction attachCleanupForRoot(\n rootElement: Element,\n instance: ComponentInstance\n) {\n (rootElement as ElementWithCleanup)[CLEANUP_SYMBOL] = () => {\n // Attempt to remove listeners and cleanup instances under the root.\n // In non-strict mode we preserve previous behavior by swallowing errors\n // (but logging in dev); in strict mode we aggregate and re-throw.\n const errors: unknown[] = [];\n try {\n removeAllListeners(rootElement);\n } catch (e) {\n errors.push(e);\n }\n\n try {\n cleanupComponent(instance as ComponentInstance);\n } catch (e) {\n errors.push(e);\n }\n\n if (errors.length > 0) {\n if (instance.cleanupStrict) {\n throw new AggregateError(errors, `cleanup failed for app root`);\n } else if (isDevelopmentEnvironment()) {\n for (const err of errors) logger.warn('[Askr] cleanup error:', err);\n }\n }\n };\n\n try {\n const descriptor =\n Object.getOwnPropertyDescriptor(rootElement, 'innerHTML') ||\n Object.getOwnPropertyDescriptor(\n Object.getPrototypeOf(rootElement),\n 'innerHTML'\n ) ||\n Object.getOwnPropertyDescriptor(Element.prototype, 'innerHTML');\n\n if (descriptor && (descriptor.get || descriptor.set)) {\n Object.defineProperty(rootElement, 'innerHTML', {\n get: descriptor.get\n ? function (this: Element) {\n return descriptor.get!.call(this);\n }\n : undefined,\n set: function (this: Element, value: string) {\n if (value === '' && instancesByRoot.get(this) === instance) {\n try {\n removeAllListeners(rootElement);\n } catch (e) {\n if (instance.cleanupStrict) throw e;\n if (isDevelopmentEnvironment()) {\n logger.warn('[Askr] cleanup error:', e);\n }\n }\n\n try {\n cleanupComponent(instance as ComponentInstance);\n } catch (e) {\n if (instance.cleanupStrict) throw e;\n if (isDevelopmentEnvironment()) {\n logger.warn('[Askr] cleanup error:', e);\n }\n }\n }\n if (descriptor.set) {\n return descriptor.set.call(this, value);\n }\n },\n configurable: true,\n });\n }\n } catch {\n // If Object.defineProperty fails, ignore\n }\n}\n\n/**\n * Explicitly teardown an app mounted on `root` if present. This is the\n * recommended API for deterministic cleanup rather than relying on overriding\n * `innerHTML` setter behavior.\n */\nexport function teardownApp(_root: Element | string) {\n throw new Error(\n 'The `teardownApp` alias has been removed. Use `cleanupApp(root)` instead.'\n );\n}\n\nimport { Fragment, ELEMENT_TYPE } from '../jsx';\n\nimport { DefaultPortal } from '../foundations/structures/portal';\n\nfunction mountOrUpdate(\n rootElement: Element,\n componentFn: ComponentFunction,\n options?: { cleanupStrict?: boolean }\n) {\n // Ensure root component always includes a DefaultPortal host by wrapping it.\n const wrappedFn: ComponentFunction = (props, ctx) => {\n const out = componentFn(props, ctx);\n const portalVNode = {\n $$typeof: ELEMENT_TYPE,\n type: DefaultPortal,\n props: {},\n key: '__default_portal',\n } as unknown;\n return {\n $$typeof: ELEMENT_TYPE,\n type: Fragment,\n props: {\n children:\n out === undefined || out === null\n ? [portalVNode]\n : [out, portalVNode],\n },\n } as unknown as ReturnType<ComponentFunction>;\n };\n // Preserve the original component name for debugging/dev warnings\n Object.defineProperty(wrappedFn, 'name', {\n value: componentFn.name || 'Component',\n });\n\n // Clean up existing cleanup function before mounting new one\n const existingCleanup = (rootElement as ElementWithCleanup)[CLEANUP_SYMBOL];\n if (existingCleanup) existingCleanup();\n\n let instance = instancesByRoot.get(rootElement);\n\n if (instance) {\n removeAllListeners(rootElement);\n try {\n cleanupComponent(instance);\n } catch (e) {\n // If previous cleanup threw in strict mode, log but continue mounting new instance\n if (isDevelopmentEnvironment()) {\n logger.warn('[Askr] prior cleanup threw:', e);\n }\n }\n\n instance.fn = wrappedFn;\n instance.evaluationGeneration++;\n instance.mounted = false;\n instance.expectedStateIndices = [];\n instance.firstRenderComplete = false;\n instance.isRoot = true;\n // Update strict flag if provided\n if (options && typeof options.cleanupStrict === 'boolean') {\n instance.cleanupStrict = options.cleanupStrict;\n }\n } else {\n const componentId = String(++componentIdCounter);\n instance = createComponentInstance(componentId, wrappedFn, {}, rootElement);\n instancesByRoot.set(rootElement, instance);\n instance.isRoot = true;\n // Initialize strict flag from options\n if (options && typeof options.cleanupStrict === 'boolean') {\n instance.cleanupStrict = options.cleanupStrict;\n }\n }\n\n attachCleanupForRoot(rootElement, instance);\n mountComponent(instance);\n globalScheduler.flush();\n}\n\nfunction bindResolvedRouteHandler(resolved: ResolvedRoute): ComponentFunction {\n return () =>\n resolved.handler(resolved.params) as ReturnType<ComponentFunction>;\n}\n\nfunction bindDeniedStatus(status: number): ComponentFunction {\n return () => ({\n type: 'div',\n props: {\n 'data-route-denied': String(status),\n },\n children: [String(status)],\n });\n}\n\n// New strongly-typed init functions\nimport type {\n Route,\n RouteManifest,\n ResolvedRoute,\n RouteAuthOptions,\n} from '../common/router';\nimport { installRendererBridge, removeAllListeners } from '../renderer';\ninstallRendererBridge();\n\nexport type IslandConfig = {\n root: Element | string;\n component: ComponentFunction;\n // Optional: surface cleanup errors during teardown for this island\n cleanupStrict?: boolean;\n // Explicitly disallow routes on islands at type level\n routes?: never;\n};\n\nexport type IslandsConfig = {\n islands: IslandConfig[];\n};\n\nexport type SPAConfig = {\n root: Element | string;\n /**\n * Preferred: pass the route manifest built via `registerRoutes(() => { ... })`.\n * ```ts\n * import { getManifest } from '@askrjs/askr/router';\n * await createSPA({ root: '#app', manifest: getManifest() });\n * ```\n */\n manifest?: RouteManifest;\n /** Legacy: flat route array — kept for backward compatibility and test fixtures. */\n routes?: Route[];\n auth?: RouteAuthOptions;\n scrollRestoration?: boolean | ScrollRestorationOptions;\n cleanupStrict?: boolean;\n component?: never;\n};\n\nexport type HydrateSPAConfig = {\n root: Element | string;\n /** Preferred manifest input — see `SPAConfig.manifest`. */\n manifest?: RouteManifest;\n /** Legacy flat route array. */\n routes?: Route[];\n auth?: RouteAuthOptions;\n scrollRestoration?: boolean | ScrollRestorationOptions;\n cleanupStrict?: boolean;\n hydrate?: {\n verifyMarkup?: boolean;\n deferUntilIdle?: boolean;\n deferBelowFold?: boolean;\n foldThreshold?: number;\n skipSelectors?: string[];\n };\n};\n\n/**\n * createIsland: Enhances existing DOM (no router, mounts once)\n */\nexport function createIsland(config: IslandConfig): void {\n assertExecutionModel('islands');\n if (!config || typeof config !== 'object') {\n throw new Error('createIsland requires a config object');\n }\n if (typeof config.component !== 'function') {\n throw new Error('createIsland: component must be a function');\n }\n\n const rootElement =\n typeof config.root === 'string'\n ? document.getElementById(config.root)\n : config.root;\n if (!rootElement) throw new Error(`Root element not found: ${config.root}`);\n\n // Islands must not initialize router or routes\n if ('routes' in config) {\n throw new Error(\n 'createIsland does not accept routes; use createSPA for routed apps'\n );\n }\n\n // Routes are never supported with islands.\n // If routes were registered (even at module load time), fail fast to avoid\n // surprising partial router behavior.\n try {\n const g = globalThis as unknown as Record<string | symbol, unknown>;\n if (g[HAS_ROUTES_KEY]) {\n throw new Error(\n 'Routes are not supported with islands. Use createSPA (client) or createSSR (server) instead.'\n );\n }\n } catch {\n // ignore\n }\n\n mountOrUpdate(rootElement, config.component, {\n cleanupStrict: config.cleanupStrict,\n });\n}\n\n/**\n * createIslands: Enhances one or more existing DOM roots (no router).\n * The only public islands constructor.\n */\nexport function createIslands(config: IslandsConfig): void {\n assertExecutionModel('islands');\n if (!config || typeof config !== 'object') {\n throw new Error('createIslands requires a config object');\n }\n if (!Array.isArray(config.islands) || config.islands.length === 0) {\n throw new Error('createIslands requires a non-empty islands array');\n }\n for (const island of config.islands) {\n createIsland(island);\n }\n}\n\n/**\n * createSPA: Initializes router and mounts the app with the provided route manifest or route table.\n *\n * Preferred usage with manifest:\n * ```ts\n * import { getManifest } from '@askrjs/askr/router';\n * await createSPA({ root: '#app', manifest: getManifest() });\n * ```\n *\n * Legacy usage with plain routes array (still supported):\n * ```ts\n * await createSPA({ root: '#app', routes: getRoutes() });\n * ```\n */\nexport async function createSPA(config: SPAConfig): Promise<void> {\n assertExecutionModel('spa');\n if (!config || typeof config !== 'object') {\n throw new Error('createSPA requires a config object');\n }\n\n const hasManifest =\n config.manifest != null && config.manifest.records.length > 0;\n const hasRoutes = Array.isArray(config.routes) && config.routes.length > 0;\n\n if (!hasManifest && !hasRoutes) {\n throw new Error(\n 'createSPA requires a route manifest or route table. ' +\n 'Pass `manifest: getManifest()` or `routes: getRoutes()`. ' +\n 'If you are enhancing existing HTML, use createIsland instead.'\n );\n }\n\n const rootElement =\n typeof config.root === 'string'\n ? document.getElementById(config.root)\n : config.root;\n if (!rootElement) throw new Error(`Root element not found: ${config.root}`);\n\n const pendingLazyAtBoot = _snapshotLazy();\n\n configureScrollRestoration(config.scrollRestoration);\n\n clearRoutes();\n\n if (hasManifest) {\n // Preferred path: apply pre-built manifest records directly\n _applyManifest(config.manifest!);\n } else {\n // Legacy path: register plain Route objects (no layout metadata)\n for (const r of config.routes!) {\n registerRoute(r.path, r.handler as Parameters<typeof registerRoute>[1]);\n }\n }\n\n const routeAuth = config.auth ?? config.manifest?.auth;\n _setActiveRouteAuthOptions(routeAuth);\n\n // Drain any lazy() imports so all split chunks are ready before mounting\n await _drainLazy(pendingLazyAtBoot);\n\n // Lock registration in production to prevent late registration surprises\n if (isProductionEnvironment()) lockRouteRegistration();\n\n // Mount the currently-resolved route handler (if any)\n let path = typeof window !== 'undefined' ? window.location.pathname : '/';\n let href =\n typeof window !== 'undefined'\n ? `${window.location.pathname}${window.location.search}${window.location.hash}`\n : path;\n let resolved = await resolveRouteRequest(href, { auth: routeAuth });\n\n while (\n typeof window !== 'undefined' &&\n resolved &&\n resolved.kind === 'redirect'\n ) {\n const redirectTarget = new URL(resolved.to, window.location.href);\n const redirectHref = `${redirectTarget.pathname}${redirectTarget.search}${redirectTarget.hash}`;\n window.history.replaceState({ path: redirectHref }, '', redirectHref);\n path = redirectTarget.pathname;\n href = redirectHref;\n resolved = await resolveRouteRequest(href, { auth: routeAuth });\n }\n\n if (!resolved) {\n mountOrUpdate(rootElement, () => ({ type: 'div', children: [] }), {\n cleanupStrict: config.cleanupStrict,\n });\n\n await registerAppNavigation(rootElement, path);\n return;\n }\n\n if (resolved.kind === 'redirect') {\n mountOrUpdate(rootElement, () => ({ type: 'div', children: [] }), {\n cleanupStrict: config.cleanupStrict,\n });\n\n await registerAppNavigation(rootElement, path);\n return;\n }\n\n mountOrUpdate(\n rootElement,\n resolved.kind === 'deny'\n ? bindDeniedStatus(resolved.status)\n : bindResolvedRouteHandler({\n handler: resolved.handler,\n params: resolved.params,\n }),\n {\n cleanupStrict: config.cleanupStrict,\n }\n );\n\n await registerAppNavigation(rootElement, path);\n}\n\n/**\n * Mark elements that should be skipped during hydration\n */\nfunction markSkippedElements(root: Element, skipSelectors: string[]): void {\n if (skipSelectors.length === 0) {\n return;\n }\n\n const uniqueSelectors = Array.from(new Set(skipSelectors));\n const selectorList = uniqueSelectors.join(', ');\n const elements = root.querySelectorAll(selectorList);\n elements.forEach((el) => el.setAttribute('data-skip-hydrate', 'true'));\n}\n\nfunction collectDeferredBelowFoldBoundaries(\n root: Element,\n foldY: number\n): Element[] {\n const boundaries: Element[] = [];\n const stack: Element[] = [];\n\n for (let index = root.children.length - 1; index >= 0; index -= 1) {\n stack.push(root.children[index]);\n }\n\n while (stack.length > 0) {\n const element = stack.pop()!;\n\n if (element.hasAttribute('data-skip-hydrate')) {\n continue;\n }\n\n const rect = element.getBoundingClientRect();\n if (rect.top >= foldY) {\n element.setAttribute('data-skip-hydrate', 'true');\n boundaries.push(element);\n continue;\n }\n\n for (let index = element.children.length - 1; index >= 0; index -= 1) {\n stack.push(element.children[index]);\n }\n }\n\n return boundaries;\n}\n\nfunction activateVisibleDeferredBoundaries(\n boundaries: Element[],\n foldY: number\n): { activated: boolean; remaining: number } {\n let activated = false;\n let remaining = 0;\n\n for (const element of boundaries) {\n if (!element.hasAttribute('data-skip-hydrate')) {\n continue;\n }\n\n const rect = element.getBoundingClientRect();\n if (rect.top < foldY) {\n element.removeAttribute('data-skip-hydrate');\n activated = true;\n } else {\n remaining += 1;\n }\n }\n\n return { activated, remaining };\n}\n\nfunction queueIdleWork(work: () => void): Promise<void> {\n return new Promise((resolve) => {\n if (typeof requestIdleCallback !== 'undefined') {\n requestIdleCallback(\n () => {\n work();\n resolve();\n },\n { timeout: 2000 }\n );\n return;\n }\n\n setTimeout(() => {\n work();\n resolve();\n }, 0);\n });\n}\n\nfunction flushHydrationActivation(rootElement: Element): void {\n const instance = instancesByRoot.get(rootElement);\n if (!instance) return;\n instance._enqueueRun?.();\n globalScheduler.flush();\n}\n\nfunction shouldVerifyHydrationMarkup(config: HydrateSPAConfig): boolean {\n const explicit = config.hydrate?.verifyMarkup;\n if (typeof explicit === 'boolean') {\n return explicit;\n }\n\n return !isProductionEnvironment();\n}\n\nasync function registerAppNavigation(rootElement: Element, path: string) {\n const instance = instancesByRoot.get(rootElement);\n if (!instance) throw new Error('Internal error: app instance missing');\n registerAppInstance(instance as ComponentInstance, path);\n initializeNavigation();\n}\n\n/**\n * Apply selective hydration with deferral options\n */\nasync function applySelectiveHydration(\n rootElement: Element,\n resolved: { handler: ComponentFunction; params: Record<string, unknown> },\n path: string,\n cleanupStrict: boolean | undefined,\n hydrateOptions: NonNullable<HydrateSPAConfig['hydrate']>\n): Promise<void> {\n const hasPermanentSkips = (hydrateOptions.skipSelectors?.length ?? 0) > 0;\n const hasBelowFoldDeferral = !!hydrateOptions.deferBelowFold;\n const hasSelectiveBoundaries = hasPermanentSkips || hasBelowFoldDeferral;\n\n if (hydrateOptions.skipSelectors?.length) {\n markSkippedElements(rootElement, hydrateOptions.skipSelectors);\n }\n\n let deferredBoundaries: Element[] = [];\n if (hydrateOptions.deferBelowFold) {\n const foldY = hydrateOptions.foldThreshold ?? window.innerHeight;\n deferredBoundaries = collectDeferredBelowFoldBoundaries(rootElement, foldY);\n\n const handleScroll = () => {\n const { activated, remaining } = activateVisibleDeferredBoundaries(\n deferredBoundaries,\n foldY\n );\n\n if (!activated) {\n return;\n }\n\n flushHydrationActivation(rootElement);\n\n if (remaining === 0) {\n window.removeEventListener('scroll', handleScroll);\n }\n };\n\n window.addEventListener('scroll', handleScroll, { passive: true });\n }\n\n if (hydrateOptions.deferUntilIdle && !hasSelectiveBoundaries) {\n await queueIdleWork(() => {\n mountOrUpdate(\n rootElement,\n (() => resolved.handler(resolved.params)) as ComponentFunction,\n {\n cleanupStrict,\n }\n );\n });\n await registerAppNavigation(rootElement, path);\n return;\n }\n\n mountOrUpdate(\n rootElement,\n (() => resolved.handler(resolved.params)) as ComponentFunction,\n {\n cleanupStrict,\n }\n );\n await registerAppNavigation(rootElement, path);\n\n if (hydrateOptions.deferUntilIdle && deferredBoundaries.length > 0) {\n await queueIdleWork(() => {\n const { activated } = activateVisibleDeferredBoundaries(\n deferredBoundaries,\n Number.POSITIVE_INFINITY\n );\n if (activated) {\n flushHydrationActivation(rootElement);\n }\n });\n }\n}\n\n/**\n * hydrateSPA: Hydrate server-rendered HTML.\n * Accepts either a `manifest` (preferred) or a legacy `routes` array.\n */\nexport async function hydrateSPA(config: HydrateSPAConfig): Promise<void> {\n assertExecutionModel('spa');\n if (!config || typeof config !== 'object') {\n throw new Error('hydrateSPA requires a config object');\n }\n\n const hasManifest =\n config.manifest != null && config.manifest.records.length > 0;\n const hasRoutes = Array.isArray(config.routes) && config.routes.length > 0;\n\n if (!hasManifest && !hasRoutes) {\n throw new Error(\n 'hydrateSPA requires a route manifest or route table. ' +\n 'Pass `manifest: getManifest()` or `routes: getRoutes()`. ' +\n 'If you are enhancing existing HTML, use createIsland instead.'\n );\n }\n\n const rootElement =\n typeof config.root === 'string'\n ? document.getElementById(config.root)\n : config.root;\n if (!rootElement) throw new Error(`Root element not found: ${config.root}`);\n\n const pendingLazyAtHydrationBoot = _snapshotLazy();\n\n configureScrollRestoration(config.scrollRestoration);\n\n clearRoutes();\n\n if (hasManifest) {\n _applyManifest(config.manifest!);\n } else {\n for (const r of config.routes!) {\n registerRoute(r.path, r.handler as Parameters<typeof registerRoute>[1]);\n }\n }\n\n _setActiveRouteAuthOptions(config.auth ?? config.manifest?.auth);\n\n // Drain any lazy() imports so all split chunks are ready before mounting\n await _drainLazy(pendingLazyAtHydrationBoot);\n\n const path = typeof window !== 'undefined' ? window.location.pathname : '/';\n const currentUrl =\n typeof window !== 'undefined'\n ? `${window.location.pathname}${window.location.search}${window.location.hash}`\n : path;\n setServerLocation(currentUrl);\n if (isProductionEnvironment()) lockRouteRegistration();\n\n const resolved = resolveRoute(path);\n if (!resolved) {\n throw new Error(`hydrateSPA: no route found for current path (${path}).`);\n }\n\n if (shouldVerifyHydrationMarkup(config)) {\n const legacyRouteTable = hasManifest\n ? config.manifest!.records.map((r) => ({\n path: r.path,\n handler: r.handler,\n namespace: r.options.namespace,\n }))\n : config.routes!;\n\n const { verifyHydrationSyncForUrl } = await import('../ssr');\n if (\n !verifyHydrationSyncForUrl({\n root: rootElement,\n url: currentUrl,\n routes: legacyRouteTable,\n })\n ) {\n throw new Error(\n '[Askr] Hydration mismatch detected. Server HTML does not match expected server-render output.'\n );\n }\n }\n\n const hydrateOptions = config.hydrate;\n if (hydrateOptions) {\n if (hydrateOptions.deferUntilIdle || hydrateOptions.deferBelowFold) {\n await applySelectiveHydration(\n rootElement,\n {\n handler: resolved.handler as ComponentFunction,\n params: resolved.params as Record<string, unknown>,\n },\n path,\n config.cleanupStrict,\n hydrateOptions\n );\n return;\n }\n\n if (hydrateOptions.skipSelectors?.length) {\n markSkippedElements(rootElement, hydrateOptions.skipSelectors);\n }\n }\n\n mountOrUpdate(rootElement, bindResolvedRouteHandler(resolved), {\n cleanupStrict: config.cleanupStrict,\n });\n await registerAppNavigation(rootElement, path);\n}\n\n/**\n * Cleanup an app mounted on a root element (element or id).\n * Safe to call multiple times — no-op when nothing is mounted.\n */\nexport function cleanupApp(root: Element | string): void {\n const rootElement =\n typeof root === 'string' ? document.getElementById(root) : root;\n\n if (!rootElement) return;\n\n const cleanupFn = (rootElement as ElementWithCleanup)[CLEANUP_SYMBOL];\n if (typeof cleanupFn === 'function') {\n cleanupFn();\n }\n\n instancesByRoot.delete(rootElement);\n}\n\n/**\n * Check whether an app is mounted on the given root\n */\nexport function hasApp(root: Element | string): boolean {\n const rootElement =\n typeof root === 'string' ? document.getElementById(root) : root;\n\n if (!rootElement) return false;\n return instancesByRoot.has(rootElement);\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"control.js","names":[],"sources":["../../src/common/control.ts"],"sourcesContent":["import type { Props } from './props';\n\nexport const __CONTROL_BOUNDARY__ = Symbol('__CONTROL_BOUNDARY__');\n\nconst EAGER_CONTROL_PRIMITIVE = Symbol('__ASKR_EAGER_CONTROL_PRIMITIVE__');\n\nexport type EagerControlPrimitive<P extends Props = Props> = ((\n props: P\n) => unknown) & {\n [EAGER_CONTROL_PRIMITIVE]?: true;\n};\n\nexport function markEagerControlPrimitive<\n T extends (...args: never[]) => unknown,\n>(primitive: T): T {\n (\n primitive as T & {\n [EAGER_CONTROL_PRIMITIVE]?: true;\n }\n )[EAGER_CONTROL_PRIMITIVE] = true;\n return primitive;\n}\n\nexport function isEagerControlPrimitive(\n value: unknown\n): value is EagerControlPrimitive {\n return (\n typeof value === 'function' &&\n Boolean(\n (\n value as {\n [EAGER_CONTROL_PRIMITIVE]?: true;\n }\n )[EAGER_CONTROL_PRIMITIVE]\n )\n );\n}\n"],"mappings":";AAEA,MAAa,uBAAuB,OAAO,uBAAuB;AAElE,MAAM,0BAA0B,OAAO,mCAAmC;AAQ1E,SAAgB,0BAEd,WAAiB;
|
|
1
|
+
{"version":3,"file":"control.js","names":[],"sources":["../../src/common/control.ts"],"sourcesContent":["import type { Props } from './props';\n\nexport const __CONTROL_BOUNDARY__ = Symbol('__CONTROL_BOUNDARY__');\n\nconst EAGER_CONTROL_PRIMITIVE = Symbol('__ASKR_EAGER_CONTROL_PRIMITIVE__');\n\nexport type EagerControlPrimitive<P extends Props = Props> = ((\n props: P\n) => unknown) & {\n [EAGER_CONTROL_PRIMITIVE]?: true;\n};\n\nexport function markEagerControlPrimitive<\n T extends (...args: never[]) => unknown,\n>(primitive: T): T {\n (\n primitive as T & {\n [EAGER_CONTROL_PRIMITIVE]?: true;\n }\n )[EAGER_CONTROL_PRIMITIVE] = true;\n return primitive;\n}\n\nexport function isEagerControlPrimitive(\n value: unknown\n): value is EagerControlPrimitive {\n return (\n typeof value === 'function' &&\n Boolean(\n (\n value as {\n [EAGER_CONTROL_PRIMITIVE]?: true;\n }\n )[EAGER_CONTROL_PRIMITIVE]\n )\n );\n}\n"],"mappings":";AAEA,MAAa,uBAAuB,OAAO,uBAAuB;AAElE,MAAM,0BAA0B,OAAO,mCAAmC;AAQ1E,SAAgB,0BAEd,WAAiB;CACjB,UAIE,2BAA2B;CAC7B,OAAO;;AAGT,SAAgB,wBACd,OACgC;CAChC,OACE,OAAO,UAAU,cACjB,QAEI,MAGA,yBACH"}
|
package/dist/common/env.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","names":[],"sources":["../../src/common/env.ts"],"sourcesContent":["export type RuntimeEnv = Record<string, string | undefined>;\n\ntype RuntimeEnvGlobal = typeof globalThis & {\n process?: {\n env?: RuntimeEnv;\n };\n};\n\ntype BuildTimeEnv = Record<string, unknown> & {\n NODE_ENV?: unknown;\n MODE?: unknown;\n PROD?: unknown;\n DEV?: unknown;\n};\n\nfunction normalizeEnv(source?: Record<string, unknown>): RuntimeEnv {\n if (!source) {\n return {};\n }\n\n const normalized: RuntimeEnv = {};\n for (const [key, value] of Object.entries(source)) {\n if (typeof value === 'string') {\n normalized[key] = value;\n } else if (typeof value === 'boolean' || typeof value === 'number') {\n normalized[key] = String(value);\n }\n }\n\n return normalized;\n}\n\nfunction getBuildTimeEnv(): RuntimeEnv {\n const metaEnv = (import.meta as ImportMeta & { env?: BuildTimeEnv }).env;\n const normalized = normalizeEnv(metaEnv);\n\n if (normalized.NODE_ENV == null && metaEnv) {\n if (typeof metaEnv.MODE === 'string' && metaEnv.MODE.length > 0) {\n normalized.NODE_ENV = metaEnv.MODE;\n } else if (metaEnv.PROD === true || metaEnv.PROD === 'true') {\n normalized.NODE_ENV = 'production';\n } else if (metaEnv.DEV === true || metaEnv.DEV === 'true') {\n normalized.NODE_ENV = 'development';\n }\n }\n\n return normalized;\n}\n\nfunction resolveNodeEnv(\n processNodeEnv: string | undefined,\n buildNodeEnv: string | undefined\n): string | undefined {\n if (buildNodeEnv === 'production') {\n return 'production';\n }\n\n if (processNodeEnv != null) {\n return processNodeEnv;\n }\n\n if (buildNodeEnv === 'development') {\n return 'development';\n }\n\n return buildNodeEnv === 'test' ? undefined : buildNodeEnv;\n}\n\nexport function getRuntimeEnv(): RuntimeEnv {\n const processEnv = (globalThis as RuntimeEnvGlobal).process?.env ?? {};\n const buildTimeEnv = getBuildTimeEnv();\n\n return {\n ...processEnv,\n ...buildTimeEnv,\n NODE_ENV: resolveNodeEnv(processEnv.NODE_ENV, buildTimeEnv.NODE_ENV),\n };\n}\n\nexport function isProductionEnvironment(): boolean {\n return getRuntimeEnv().NODE_ENV === 'production';\n}\n\nexport function isDevelopmentEnvironment(): boolean {\n return !isProductionEnvironment();\n}\n\nexport function isRuntimeEnvFlagEnabled(name: string): boolean {\n const value = getRuntimeEnv()[name];\n return value === '1' || value === 'true';\n}\n"],"mappings":";AAeA,SAAS,aAAa,QAA8C;
|
|
1
|
+
{"version":3,"file":"env.js","names":[],"sources":["../../src/common/env.ts"],"sourcesContent":["export type RuntimeEnv = Record<string, string | undefined>;\n\ntype RuntimeEnvGlobal = typeof globalThis & {\n process?: {\n env?: RuntimeEnv;\n };\n};\n\ntype BuildTimeEnv = Record<string, unknown> & {\n NODE_ENV?: unknown;\n MODE?: unknown;\n PROD?: unknown;\n DEV?: unknown;\n};\n\nfunction normalizeEnv(source?: Record<string, unknown>): RuntimeEnv {\n if (!source) {\n return {};\n }\n\n const normalized: RuntimeEnv = {};\n for (const [key, value] of Object.entries(source)) {\n if (typeof value === 'string') {\n normalized[key] = value;\n } else if (typeof value === 'boolean' || typeof value === 'number') {\n normalized[key] = String(value);\n }\n }\n\n return normalized;\n}\n\nfunction getBuildTimeEnv(): RuntimeEnv {\n const metaEnv = (import.meta as ImportMeta & { env?: BuildTimeEnv }).env;\n const normalized = normalizeEnv(metaEnv);\n\n if (normalized.NODE_ENV == null && metaEnv) {\n if (typeof metaEnv.MODE === 'string' && metaEnv.MODE.length > 0) {\n normalized.NODE_ENV = metaEnv.MODE;\n } else if (metaEnv.PROD === true || metaEnv.PROD === 'true') {\n normalized.NODE_ENV = 'production';\n } else if (metaEnv.DEV === true || metaEnv.DEV === 'true') {\n normalized.NODE_ENV = 'development';\n }\n }\n\n return normalized;\n}\n\nfunction resolveNodeEnv(\n processNodeEnv: string | undefined,\n buildNodeEnv: string | undefined\n): string | undefined {\n if (buildNodeEnv === 'production') {\n return 'production';\n }\n\n if (processNodeEnv != null) {\n return processNodeEnv;\n }\n\n if (buildNodeEnv === 'development') {\n return 'development';\n }\n\n return buildNodeEnv === 'test' ? undefined : buildNodeEnv;\n}\n\nexport function getRuntimeEnv(): RuntimeEnv {\n const processEnv = (globalThis as RuntimeEnvGlobal).process?.env ?? {};\n const buildTimeEnv = getBuildTimeEnv();\n\n return {\n ...processEnv,\n ...buildTimeEnv,\n NODE_ENV: resolveNodeEnv(processEnv.NODE_ENV, buildTimeEnv.NODE_ENV),\n };\n}\n\nexport function isProductionEnvironment(): boolean {\n return getRuntimeEnv().NODE_ENV === 'production';\n}\n\nexport function isDevelopmentEnvironment(): boolean {\n return !isProductionEnvironment();\n}\n\nexport function isRuntimeEnvFlagEnabled(name: string): boolean {\n const value = getRuntimeEnv()[name];\n return value === '1' || value === 'true';\n}\n"],"mappings":";AAeA,SAAS,aAAa,QAA8C;CAClE,IAAI,CAAC,QACH,OAAO,EAAE;CAGX,MAAM,aAAyB,EAAE;CACjC,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,EAC/C,IAAI,OAAO,UAAU,UACnB,WAAW,OAAO;MACb,IAAI,OAAO,UAAU,aAAa,OAAO,UAAU,UACxD,WAAW,OAAO,OAAO,MAAM;CAInC,OAAO;;AAGT,SAAS,kBAA8B;CACrC,MAAM,UAAW,OAAO,KAA6C;CACrE,MAAM,aAAa,aAAa,QAAQ;CAExC,IAAI,WAAW,YAAY,QAAQ,SACjC;MAAI,OAAO,QAAQ,SAAS,YAAY,QAAQ,KAAK,SAAS,GAC5D,WAAW,WAAW,QAAQ;OACzB,IAAI,QAAQ,SAAS,QAAQ,QAAQ,SAAS,QACnD,WAAW,WAAW;OACjB,IAAI,QAAQ,QAAQ,QAAQ,QAAQ,QAAQ,QACjD,WAAW,WAAW;;CAI1B,OAAO;;AAGT,SAAS,eACP,gBACA,cACoB;CACpB,IAAI,iBAAiB,cACnB,OAAO;CAGT,IAAI,kBAAkB,MACpB,OAAO;CAGT,IAAI,iBAAiB,eACnB,OAAO;CAGT,OAAO,iBAAiB,SAAS,SAAY;;AAG/C,SAAgB,gBAA4B;CAC1C,MAAM,aAAc,WAAgC,SAAS,OAAO,EAAE;CACtE,MAAM,eAAe,iBAAiB;CAEtC,OAAO;EACL,GAAG;EACH,GAAG;EACH,UAAU,eAAe,WAAW,UAAU,aAAa,SAAS;EACrE;;AAGH,SAAgB,0BAAmC;CACjD,OAAO,eAAe,CAAC,aAAa;;AAGtC,SAAgB,2BAAoC;CAClD,OAAO,CAAC,yBAAyB;;AAGnC,SAAgB,wBAAwB,MAAuB;CAC7D,MAAM,QAAQ,eAAe,CAAC;CAC9B,OAAO,UAAU,OAAO,UAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ssr-errors.js","names":[],"sources":["../../src/common/ssr-errors.ts"],"sourcesContent":["/**\n * Common call contracts: SSR error types\n */\n\nexport class SSRDataMissingError extends Error {\n readonly code = 'SSR_DATA_MISSING';\n constructor(\n message = 'Server-side rendering requires all data to be available synchronously. This component attempted to use async data during SSR.'\n ) {\n super(message);\n this.name = 'SSRDataMissingError';\n Object.setPrototypeOf(this, SSRDataMissingError.prototype);\n }\n}\n"],"mappings":";;;;AAIA,IAAa,sBAAb,MAAa,4BAA4B,MAAM;CAE7C,YACE,UAAU,iIACV;
|
|
1
|
+
{"version":3,"file":"ssr-errors.js","names":[],"sources":["../../src/common/ssr-errors.ts"],"sourcesContent":["/**\n * Common call contracts: SSR error types\n */\n\nexport class SSRDataMissingError extends Error {\n readonly code = 'SSR_DATA_MISSING';\n constructor(\n message = 'Server-side rendering requires all data to be available synchronously. This component attempted to use async data during SSR.'\n ) {\n super(message);\n this.name = 'SSRDataMissingError';\n Object.setPrototypeOf(this, SSRDataMissingError.prototype);\n }\n}\n"],"mappings":";;;;AAIA,IAAa,sBAAb,MAAa,4BAA4B,MAAM;CAE7C,YACE,UAAU,iIACV;EACA,MAAM,QAAQ;cAJA;EAKd,KAAK,OAAO;EACZ,OAAO,eAAe,MAAM,oBAAoB,UAAU"}
|
package/dist/common/vnode.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"vnode.js","names":[],"sources":["../../src/common/vnode.ts"],"sourcesContent":["/**\n * Common call contracts: VNode / virtual DOM shapes\n */\n\nimport type { Props } from './props';\nimport type { ControlBoundaryState } from '../runtime/control';\nimport type { ForState } from '../runtime/for';\nexport { __CONTROL_BOUNDARY__ } from './control';\nimport { __CONTROL_BOUNDARY__ } from './control';\n\nexport const __ERROR_BOUNDARY__ = Symbol.for('askr.error-boundary');\n\nexport interface DOMElement {\n // Element `type` can be an intrinsic tag name, a component function, or\n // a special symbol (e.g. `Fragment`). Include `symbol` in the type union\n // so runtime comparisons against `Fragment` are type-safe.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type: string | ((props: Props) => any) | symbol;\n props?: Props;\n children?: VNode[];\n key?: string | number | null;\n [Symbol.iterator]?: never;\n _controlState?: ControlBoundaryState; // Internal: control boundary state\n _forState?: ForState<unknown>; // Deprecated internal alias during migration\n}\n\n// Type for virtual DOM nodes\nexport type VNode = DOMElement | string | number | boolean | null | undefined;\n\n// Backward-compatible internal alias while renderer/runtime migrates away from\n// the old For-only boundary naming.\nexport const __FOR_BOUNDARY__ = __CONTROL_BOUNDARY__;\n\nexport function _isDOMElement(node: unknown): node is DOMElement {\n return typeof node === 'object' && node !== null && 'type' in node;\n}\n"],"mappings":";;AAUA,MAAa,qBAAqB,OAAO,IAAI,sBAAsB;AAqBnE,MAAa,mBAAmB;AAEhC,SAAgB,cAAc,MAAmC;
|
|
1
|
+
{"version":3,"file":"vnode.js","names":[],"sources":["../../src/common/vnode.ts"],"sourcesContent":["/**\n * Common call contracts: VNode / virtual DOM shapes\n */\n\nimport type { Props } from './props';\nimport type { ControlBoundaryState } from '../runtime/control';\nimport type { ForState } from '../runtime/for';\nexport { __CONTROL_BOUNDARY__ } from './control';\nimport { __CONTROL_BOUNDARY__ } from './control';\n\nexport const __ERROR_BOUNDARY__ = Symbol.for('askr.error-boundary');\n\nexport interface DOMElement {\n // Element `type` can be an intrinsic tag name, a component function, or\n // a special symbol (e.g. `Fragment`). Include `symbol` in the type union\n // so runtime comparisons against `Fragment` are type-safe.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n type: string | ((props: Props) => any) | symbol;\n props?: Props;\n children?: VNode[];\n key?: string | number | null;\n [Symbol.iterator]?: never;\n _controlState?: ControlBoundaryState; // Internal: control boundary state\n _forState?: ForState<unknown>; // Deprecated internal alias during migration\n}\n\n// Type for virtual DOM nodes\nexport type VNode = DOMElement | string | number | boolean | null | undefined;\n\n// Backward-compatible internal alias while renderer/runtime migrates away from\n// the old For-only boundary naming.\nexport const __FOR_BOUNDARY__ = __CONTROL_BOUNDARY__;\n\nexport function _isDOMElement(node: unknown): node is DOMElement {\n return typeof node === 'object' && node !== null && 'type' in node;\n}\n"],"mappings":";;AAUA,MAAa,qBAAqB,OAAO,IAAI,sBAAsB;AAqBnE,MAAa,mBAAmB;AAEhC,SAAgB,cAAc,MAAmC;CAC/D,OAAO,OAAO,SAAS,YAAY,SAAS,QAAQ,UAAU"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-boundary.js","names":[],"sources":["../../src/components/error-boundary.tsx"],"sourcesContent":["import type { Props } from '../common/props';\nimport { ELEMENT_TYPE, type JSXElement } from '../common/jsx';\nimport { __ERROR_BOUNDARY__ } from '../common/vnode';\nimport {\n getCurrentComponentInstance,\n type ComponentInstance,\n} from '../runtime/component';\nimport { logger } from '../dev/logger';\nimport { isDevelopmentEnvironment } from '../common/env';\n\nexport type ErrorBoundaryFallbackRender = (\n error: unknown,\n reset: () => void\n) => unknown;\n\nexport interface ErrorBoundaryProps extends Props {\n children?: unknown;\n fallback?: unknown | ErrorBoundaryFallbackRender;\n onError?: (error: unknown) => void;\n resetKey?: unknown;\n}\n\ntype ErrorBoundaryState = NonNullable<ComponentInstance['errorBoundaryState']>;\n\ntype ErrorBoundaryVNode = JSXElement & {\n __instance?: ComponentInstance;\n};\n\nfunction getBoundaryMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message || error.name || 'Unknown error';\n }\n\n if (typeof error === 'string') {\n return error;\n }\n\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n\nfunction ensureBoundaryState(\n instance: ComponentInstance,\n resetKey: unknown\n): ErrorBoundaryState {\n const boundaryState =\n instance.errorBoundaryState ??\n (instance.errorBoundaryState = {\n error: null,\n resetKey,\n notified: false,\n });\n\n if (!Object.is(boundaryState.resetKey, resetKey)) {\n boundaryState.error = null;\n boundaryState.resetKey = resetKey;\n boundaryState.notified = false;\n }\n\n return boundaryState;\n}\n\nfunction createBoundaryVNode(\n instance: ComponentInstance,\n props: ErrorBoundaryProps\n): ErrorBoundaryVNode {\n const key =\n typeof props.key === 'symbol'\n ? null\n : ((props.key ?? null) as string | number | null);\n\n return {\n $$typeof: ELEMENT_TYPE,\n type: __ERROR_BOUNDARY__,\n props: {\n children: props.children,\n fallback: props.fallback,\n onError: props.onError,\n resetKey: props.resetKey,\n },\n key,\n __instance: instance,\n };\n}\n\nexport function ErrorBoundary(props: ErrorBoundaryProps): JSXElement {\n const instance = getCurrentComponentInstance();\n if (!instance) {\n throw new Error(\n '[Askr] ErrorBoundary() can only be used during component render execution.'\n );\n }\n\n ensureBoundaryState(instance, props.resetKey);\n return createBoundaryVNode(instance, props);\n}\n\nexport function isErrorBoundaryVNode(\n value: unknown\n): value is ErrorBoundaryVNode {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as ErrorBoundaryVNode).type === __ERROR_BOUNDARY__\n );\n}\n\nexport function resolveErrorBoundaryState(\n vnode: ErrorBoundaryVNode\n): ErrorBoundaryState | null {\n return vnode.__instance?.errorBoundaryState ?? null;\n}\n\nexport function resolveErrorBoundaryFallback(\n fallback: ErrorBoundaryProps['fallback'],\n error: unknown,\n reset: () => void\n): unknown {\n if (typeof fallback === 'function') {\n return fallback(error, reset);\n }\n\n if (fallback !== undefined) {\n return fallback;\n }\n\n const message = getBoundaryMessage(error);\n const wrapper = document.createElement('div');\n wrapper.setAttribute('role', 'alert');\n wrapper.setAttribute('data-askr-error-boundary', 'true');\n wrapper.style.boxSizing = 'border-box';\n wrapper.style.padding = '1rem';\n wrapper.style.border = '1px solid currentColor';\n wrapper.style.borderRadius = '0.75rem';\n wrapper.style.display = 'grid';\n wrapper.style.gap = '0.75rem';\n wrapper.style.maxWidth = '100%';\n\n const title = document.createElement('strong');\n title.textContent = 'Something went wrong while rendering this view.';\n\n const summary = document.createElement('p');\n summary.textContent =\n 'The app recovered into a visible fallback so the error is not hidden in the console.';\n summary.style.margin = '0';\n\n const details = document.createElement('details');\n details.open = isDevelopmentEnvironment();\n\n const detailsSummary = document.createElement('summary');\n detailsSummary.textContent = 'Error details';\n\n const pre = document.createElement('pre');\n pre.textContent = message;\n pre.style.margin = '0';\n pre.style.whiteSpace = 'pre-wrap';\n pre.style.wordBreak = 'break-word';\n\n const button = document.createElement('button');\n button.type = 'button';\n button.textContent = 'Try again';\n button.addEventListener('click', () => reset());\n\n details.append(detailsSummary, pre);\n wrapper.append(title, summary, details, button);\n return wrapper;\n}\n\nexport function createBoundaryReset(instance: ComponentInstance): () => void {\n return () => {\n const boundaryState = instance.errorBoundaryState;\n if (!boundaryState) {\n return;\n }\n boundaryState.error = null;\n boundaryState.notified = false;\n queueMicrotask(() => {\n instance._enqueueRun?.();\n });\n };\n}\n\nexport function reportBoundaryError(\n instance: ComponentInstance,\n error: unknown,\n onError?: (error: unknown) => void\n): void {\n const boundaryState = instance.errorBoundaryState;\n if (\n boundaryState &&\n Object.is(boundaryState.error, error) &&\n boundaryState.notified\n ) {\n return;\n }\n\n if (boundaryState) {\n boundaryState.error = error;\n boundaryState.notified = true;\n }\n\n try {\n onError?.(error);\n } catch (hookError) {\n logger.error('[Askr] ErrorBoundary onError handler threw:', hookError);\n }\n\n logger.error('[Askr] ErrorBoundary caught render error:', error);\n}\n"],"mappings":";;;;;;AA4BA,SAAS,mBAAmB,OAAwB;
|
|
1
|
+
{"version":3,"file":"error-boundary.js","names":[],"sources":["../../src/components/error-boundary.tsx"],"sourcesContent":["import type { Props } from '../common/props';\nimport { ELEMENT_TYPE, type JSXElement } from '../common/jsx';\nimport { __ERROR_BOUNDARY__ } from '../common/vnode';\nimport {\n getCurrentComponentInstance,\n type ComponentInstance,\n} from '../runtime/component';\nimport { logger } from '../dev/logger';\nimport { isDevelopmentEnvironment } from '../common/env';\n\nexport type ErrorBoundaryFallbackRender = (\n error: unknown,\n reset: () => void\n) => unknown;\n\nexport interface ErrorBoundaryProps extends Props {\n children?: unknown;\n fallback?: unknown | ErrorBoundaryFallbackRender;\n onError?: (error: unknown) => void;\n resetKey?: unknown;\n}\n\ntype ErrorBoundaryState = NonNullable<ComponentInstance['errorBoundaryState']>;\n\ntype ErrorBoundaryVNode = JSXElement & {\n __instance?: ComponentInstance;\n};\n\nfunction getBoundaryMessage(error: unknown): string {\n if (error instanceof Error) {\n return error.message || error.name || 'Unknown error';\n }\n\n if (typeof error === 'string') {\n return error;\n }\n\n try {\n return JSON.stringify(error);\n } catch {\n return String(error);\n }\n}\n\nfunction ensureBoundaryState(\n instance: ComponentInstance,\n resetKey: unknown\n): ErrorBoundaryState {\n const boundaryState =\n instance.errorBoundaryState ??\n (instance.errorBoundaryState = {\n error: null,\n resetKey,\n notified: false,\n });\n\n if (!Object.is(boundaryState.resetKey, resetKey)) {\n boundaryState.error = null;\n boundaryState.resetKey = resetKey;\n boundaryState.notified = false;\n }\n\n return boundaryState;\n}\n\nfunction createBoundaryVNode(\n instance: ComponentInstance,\n props: ErrorBoundaryProps\n): ErrorBoundaryVNode {\n const key =\n typeof props.key === 'symbol'\n ? null\n : ((props.key ?? null) as string | number | null);\n\n return {\n $$typeof: ELEMENT_TYPE,\n type: __ERROR_BOUNDARY__,\n props: {\n children: props.children,\n fallback: props.fallback,\n onError: props.onError,\n resetKey: props.resetKey,\n },\n key,\n __instance: instance,\n };\n}\n\nexport function ErrorBoundary(props: ErrorBoundaryProps): JSXElement {\n const instance = getCurrentComponentInstance();\n if (!instance) {\n throw new Error(\n '[Askr] ErrorBoundary() can only be used during component render execution.'\n );\n }\n\n ensureBoundaryState(instance, props.resetKey);\n return createBoundaryVNode(instance, props);\n}\n\nexport function isErrorBoundaryVNode(\n value: unknown\n): value is ErrorBoundaryVNode {\n return (\n typeof value === 'object' &&\n value !== null &&\n (value as ErrorBoundaryVNode).type === __ERROR_BOUNDARY__\n );\n}\n\nexport function resolveErrorBoundaryState(\n vnode: ErrorBoundaryVNode\n): ErrorBoundaryState | null {\n return vnode.__instance?.errorBoundaryState ?? null;\n}\n\nexport function resolveErrorBoundaryFallback(\n fallback: ErrorBoundaryProps['fallback'],\n error: unknown,\n reset: () => void\n): unknown {\n if (typeof fallback === 'function') {\n return fallback(error, reset);\n }\n\n if (fallback !== undefined) {\n return fallback;\n }\n\n const message = getBoundaryMessage(error);\n const wrapper = document.createElement('div');\n wrapper.setAttribute('role', 'alert');\n wrapper.setAttribute('data-askr-error-boundary', 'true');\n wrapper.style.boxSizing = 'border-box';\n wrapper.style.padding = '1rem';\n wrapper.style.border = '1px solid currentColor';\n wrapper.style.borderRadius = '0.75rem';\n wrapper.style.display = 'grid';\n wrapper.style.gap = '0.75rem';\n wrapper.style.maxWidth = '100%';\n\n const title = document.createElement('strong');\n title.textContent = 'Something went wrong while rendering this view.';\n\n const summary = document.createElement('p');\n summary.textContent =\n 'The app recovered into a visible fallback so the error is not hidden in the console.';\n summary.style.margin = '0';\n\n const details = document.createElement('details');\n details.open = isDevelopmentEnvironment();\n\n const detailsSummary = document.createElement('summary');\n detailsSummary.textContent = 'Error details';\n\n const pre = document.createElement('pre');\n pre.textContent = message;\n pre.style.margin = '0';\n pre.style.whiteSpace = 'pre-wrap';\n pre.style.wordBreak = 'break-word';\n\n const button = document.createElement('button');\n button.type = 'button';\n button.textContent = 'Try again';\n button.addEventListener('click', () => reset());\n\n details.append(detailsSummary, pre);\n wrapper.append(title, summary, details, button);\n return wrapper;\n}\n\nexport function createBoundaryReset(instance: ComponentInstance): () => void {\n return () => {\n const boundaryState = instance.errorBoundaryState;\n if (!boundaryState) {\n return;\n }\n boundaryState.error = null;\n boundaryState.notified = false;\n queueMicrotask(() => {\n instance._enqueueRun?.();\n });\n };\n}\n\nexport function reportBoundaryError(\n instance: ComponentInstance,\n error: unknown,\n onError?: (error: unknown) => void\n): void {\n const boundaryState = instance.errorBoundaryState;\n if (\n boundaryState &&\n Object.is(boundaryState.error, error) &&\n boundaryState.notified\n ) {\n return;\n }\n\n if (boundaryState) {\n boundaryState.error = error;\n boundaryState.notified = true;\n }\n\n try {\n onError?.(error);\n } catch (hookError) {\n logger.error('[Askr] ErrorBoundary onError handler threw:', hookError);\n }\n\n logger.error('[Askr] ErrorBoundary caught render error:', error);\n}\n"],"mappings":";;;;;;AA4BA,SAAS,mBAAmB,OAAwB;CAClD,IAAI,iBAAiB,OACnB,OAAO,MAAM,WAAW,MAAM,QAAQ;CAGxC,IAAI,OAAO,UAAU,UACnB,OAAO;CAGT,IAAI;EACF,OAAO,KAAK,UAAU,MAAM;SACtB;EACN,OAAO,OAAO,MAAM;;;AAIxB,SAAS,oBACP,UACA,UACoB;CACpB,MAAM,gBACJ,SAAS,uBACR,SAAS,qBAAqB;EAC7B,OAAO;EACP;EACA,UAAU;EACX;CAEH,IAAI,CAAC,OAAO,GAAG,cAAc,UAAU,SAAS,EAAE;EAChD,cAAc,QAAQ;EACtB,cAAc,WAAW;EACzB,cAAc,WAAW;;CAG3B,OAAO;;AAGT,SAAS,oBACP,UACA,OACoB;CACpB,MAAM,MACJ,OAAO,MAAM,QAAQ,WACjB,OACE,MAAM,OAAO;CAErB,OAAO;EACL,UAAU;EACV,MAAM;EACN,OAAO;GACL,UAAU,MAAM;GAChB,UAAU,MAAM;GAChB,SAAS,MAAM;GACf,UAAU,MAAM;GACjB;EACD;EACA,YAAY;EACb;;AAGH,SAAgB,cAAc,OAAuC;CACnE,MAAM,WAAW,6BAA6B;CAC9C,IAAI,CAAC,UACH,MAAM,IAAI,MACR,6EACD;CAGH,oBAAoB,UAAU,MAAM,SAAS;CAC7C,OAAO,oBAAoB,UAAU,MAAM;;AAmB7C,SAAgB,6BACd,UACA,OACA,OACS;CACT,IAAI,OAAO,aAAa,YACtB,OAAO,SAAS,OAAO,MAAM;CAG/B,IAAI,aAAa,QACf,OAAO;CAGT,MAAM,UAAU,mBAAmB,MAAM;CACzC,MAAM,UAAU,SAAS,cAAc,MAAM;CAC7C,QAAQ,aAAa,QAAQ,QAAQ;CACrC,QAAQ,aAAa,4BAA4B,OAAO;CACxD,QAAQ,MAAM,YAAY;CAC1B,QAAQ,MAAM,UAAU;CACxB,QAAQ,MAAM,SAAS;CACvB,QAAQ,MAAM,eAAe;CAC7B,QAAQ,MAAM,UAAU;CACxB,QAAQ,MAAM,MAAM;CACpB,QAAQ,MAAM,WAAW;CAEzB,MAAM,QAAQ,SAAS,cAAc,SAAS;CAC9C,MAAM,cAAc;CAEpB,MAAM,UAAU,SAAS,cAAc,IAAI;CAC3C,QAAQ,cACN;CACF,QAAQ,MAAM,SAAS;CAEvB,MAAM,UAAU,SAAS,cAAc,UAAU;CACjD,QAAQ,OAAO,0BAA0B;CAEzC,MAAM,iBAAiB,SAAS,cAAc,UAAU;CACxD,eAAe,cAAc;CAE7B,MAAM,MAAM,SAAS,cAAc,MAAM;CACzC,IAAI,cAAc;CAClB,IAAI,MAAM,SAAS;CACnB,IAAI,MAAM,aAAa;CACvB,IAAI,MAAM,YAAY;CAEtB,MAAM,SAAS,SAAS,cAAc,SAAS;CAC/C,OAAO,OAAO;CACd,OAAO,cAAc;CACrB,OAAO,iBAAiB,eAAe,OAAO,CAAC;CAE/C,QAAQ,OAAO,gBAAgB,IAAI;CACnC,QAAQ,OAAO,OAAO,SAAS,SAAS,OAAO;CAC/C,OAAO;;AAGT,SAAgB,oBAAoB,UAAyC;CAC3E,aAAa;EACX,MAAM,gBAAgB,SAAS;EAC/B,IAAI,CAAC,eACH;EAEF,cAAc,QAAQ;EACtB,cAAc,WAAW;EACzB,qBAAqB;GACnB,SAAS,eAAe;IACxB;;;AAIN,SAAgB,oBACd,UACA,OACA,SACM;CACN,MAAM,gBAAgB,SAAS;CAC/B,IACE,iBACA,OAAO,GAAG,cAAc,OAAO,MAAM,IACrC,cAAc,UAEd;CAGF,IAAI,eAAe;EACjB,cAAc,QAAQ;EACtB,cAAc,WAAW;;CAG3B,IAAI;EACF,UAAU,MAAM;UACT,WAAW;EAClB,OAAO,MAAM,+CAA+C,UAAU;;CAGxE,OAAO,MAAM,6CAA6C,MAAM"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"link.js","names":[],"sources":["../../src/components/link.tsx"],"sourcesContent":["/**\n * Link component for client-side navigation\n */\n\nimport { navigate } from '../router/navigate';\nimport { applyInteractionPolicy } from '../foundations/interactions/interaction-policy';\nimport { mergeProps } from '../foundations/utilities/merge-props';\n\nexport type LinkProps = Omit<\n JSX.IntrinsicElements['a'],\n | 'children'\n | 'href'\n | 'class'\n | 'rel'\n | 'target'\n | 'aria-current'\n | 'aria-label'\n> & {\n href: string;\n class?: string;\n children?: unknown;\n /**\n * Optional rel attribute for link relationships.\n * Common values: \"noopener\", \"noreferrer\", \"nofollow\"\n */\n rel?: string;\n /**\n * Optional target attribute.\n * Use \"_blank\" for new tab/window.\n */\n target?: string;\n /**\n * Optional aria-current attribute for indicating current page/location.\n * Use \"page\" for the current page in navigation.\n */\n 'aria-current'?:\n | 'page'\n | 'step'\n | 'location'\n | 'date'\n | 'time'\n | 'true'\n | 'false';\n /**\n * Optional aria-label for accessibility when link text isn't descriptive enough.\n */\n 'aria-label'?: string;\n};\n\n/**\n * Link component that prevents default navigation and uses navigate()\n * Provides declarative way to navigate between routes\n *\n * Accessibility features:\n * - Proper semantic <a> element (not a button)\n * - Supports aria-current for indicating active page\n * - Supports aria-label for descriptive labels\n * - Keyboard accessible (Enter key handled by native <a> element)\n *\n * Respects native browser behaviors:\n * - Middle-click (opens in new tab)\n * - Ctrl/Cmd+click (opens in new tab)\n * - Shift+click (opens in new window)\n * - Alt+click (downloads link)\n * - Right-click context menu\n *\n * Best practices:\n * - Use target=\"_blank\" with rel=\"noopener noreferrer\" for external links\n * - Use aria-current=\"page\" for the current page in navigation\n * - Provide descriptive link text or aria-label\n *\n * Uses applyInteractionPolicy to enforce pit-of-success principles:\n * - Interaction behavior centralized in foundations\n * - Keyboard handling automatic\n * - Composable via mergeProps\n */\nexport function Link({\n href,\n class: className,\n children,\n rel,\n target,\n 'aria-current': ariaCurrent,\n 'aria-label': ariaLabel,\n ...rest\n}: LinkProps): JSX.Element {\n const interaction = applyInteractionPolicy({\n isNative: true,\n disabled: false,\n onPress: (e: Event) => {\n const event = e as MouseEvent;\n\n // Only intercept left-click without modifiers\n // Default button to 0 if undefined (for mock events in tests)\n const button = event.button ?? 0;\n if (\n button !== 0 || // not left-click\n event.ctrlKey || // Ctrl/Cmd+click\n event.metaKey || // Cmd on Mac\n event.shiftKey || // Shift+click\n event.altKey // Alt+click\n ) {\n return; // Let browser handle it (new tab, etc.)\n }\n\n // Don't intercept external links or explicit target\n if (target) {\n return; // Let browser handle it\n }\n\n event.preventDefault();\n navigate(href);\n },\n });\n\n const props = mergeProps(rest, {\n ...interaction,\n href,\n class: className,\n rel,\n target,\n 'aria-current': ariaCurrent,\n 'aria-label': ariaLabel,\n });\n\n return <a {...props}>{children}</a>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,SAAgB,KAAK,EACnB,MACA,OAAO,WACP,UACA,KACA,QACA,gBAAgB,aAChB,cAAc,WACd,GAAG,QACsB;
|
|
1
|
+
{"version":3,"file":"link.js","names":[],"sources":["../../src/components/link.tsx"],"sourcesContent":["/**\n * Link component for client-side navigation\n */\n\nimport { navigate } from '../router/navigate';\nimport { applyInteractionPolicy } from '../foundations/interactions/interaction-policy';\nimport { mergeProps } from '../foundations/utilities/merge-props';\n\nexport type LinkProps = Omit<\n JSX.IntrinsicElements['a'],\n | 'children'\n | 'href'\n | 'class'\n | 'rel'\n | 'target'\n | 'aria-current'\n | 'aria-label'\n> & {\n href: string;\n class?: string;\n children?: unknown;\n /**\n * Optional rel attribute for link relationships.\n * Common values: \"noopener\", \"noreferrer\", \"nofollow\"\n */\n rel?: string;\n /**\n * Optional target attribute.\n * Use \"_blank\" for new tab/window.\n */\n target?: string;\n /**\n * Optional aria-current attribute for indicating current page/location.\n * Use \"page\" for the current page in navigation.\n */\n 'aria-current'?:\n | 'page'\n | 'step'\n | 'location'\n | 'date'\n | 'time'\n | 'true'\n | 'false';\n /**\n * Optional aria-label for accessibility when link text isn't descriptive enough.\n */\n 'aria-label'?: string;\n};\n\n/**\n * Link component that prevents default navigation and uses navigate()\n * Provides declarative way to navigate between routes\n *\n * Accessibility features:\n * - Proper semantic <a> element (not a button)\n * - Supports aria-current for indicating active page\n * - Supports aria-label for descriptive labels\n * - Keyboard accessible (Enter key handled by native <a> element)\n *\n * Respects native browser behaviors:\n * - Middle-click (opens in new tab)\n * - Ctrl/Cmd+click (opens in new tab)\n * - Shift+click (opens in new window)\n * - Alt+click (downloads link)\n * - Right-click context menu\n *\n * Best practices:\n * - Use target=\"_blank\" with rel=\"noopener noreferrer\" for external links\n * - Use aria-current=\"page\" for the current page in navigation\n * - Provide descriptive link text or aria-label\n *\n * Uses applyInteractionPolicy to enforce pit-of-success principles:\n * - Interaction behavior centralized in foundations\n * - Keyboard handling automatic\n * - Composable via mergeProps\n */\nexport function Link({\n href,\n class: className,\n children,\n rel,\n target,\n 'aria-current': ariaCurrent,\n 'aria-label': ariaLabel,\n ...rest\n}: LinkProps): JSX.Element {\n const interaction = applyInteractionPolicy({\n isNative: true,\n disabled: false,\n onPress: (e: Event) => {\n const event = e as MouseEvent;\n\n // Only intercept left-click without modifiers\n // Default button to 0 if undefined (for mock events in tests)\n const button = event.button ?? 0;\n if (\n button !== 0 || // not left-click\n event.ctrlKey || // Ctrl/Cmd+click\n event.metaKey || // Cmd on Mac\n event.shiftKey || // Shift+click\n event.altKey // Alt+click\n ) {\n return; // Let browser handle it (new tab, etc.)\n }\n\n // Don't intercept external links or explicit target\n if (target) {\n return; // Let browser handle it\n }\n\n event.preventDefault();\n navigate(href);\n },\n });\n\n const props = mergeProps(rest, {\n ...interaction,\n href,\n class: className,\n rel,\n target,\n 'aria-current': ariaCurrent,\n 'aria-label': ariaLabel,\n });\n\n return <a {...props}>{children}</a>;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4EA,SAAgB,KAAK,EACnB,MACA,OAAO,WACP,UACA,KACA,QACA,gBAAgB,aAChB,cAAc,WACd,GAAG,QACsB;CAwCzB,OAAO,oBAAC,KAAD;EAAG,GAVI,WAAW,MAAM;GAC7B,GA9BkB,uBAAuB;IACzC,UAAU;IACV,UAAU;IACV,UAAU,MAAa;KACrB,MAAM,QAAQ;KAKd,KADe,MAAM,UAAU,OAElB,KACX,MAAM,WACN,MAAM,WACN,MAAM,YACN,MAAM,QAEN;KAIF,IAAI,QACF;KAGF,MAAM,gBAAgB;KACtB,SAAS,KAAK;;IAEjB,CAGI;GACH;GACA,OAAO;GACP;GACA;GACA,gBAAgB;GAChB,cAAc;GACf,CAEa;EAAQ;EAAa,CAAA"}
|
package/dist/control/case.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"case.js","names":[],"sources":["../../src/control/case.ts"],"sourcesContent":["import {\n __CONTROL_BOUNDARY__,\n markEagerControlPrimitive,\n} from '../common/control';\nimport type { JSXElement } from '../common/jsx';\nimport type { VNode } from '../common/vnode';\nimport { isDevelopmentEnvironment } from '../common/env';\nimport {\n createCaseState,\n type CaseState,\n type MatchBranch,\n} from '../runtime/control';\nimport { state } from '../runtime/state';\nimport { normalizeBoundaryChild } from './shared';\n\nexport type MatchProps = {\n key?: string | number | null;\n when: unknown;\n children: unknown;\n};\n\nexport type CaseProps = {\n fallback?: unknown;\n children?: unknown;\n};\n\nfunction flattenChildren(children: unknown): unknown[] {\n if (!Array.isArray(children)) {\n return children == null || children === false ? [] : [children];\n }\n\n const result: unknown[] = [];\n for (const child of children) {\n if (Array.isArray(child)) {\n result.push(...flattenChildren(child));\n continue;\n }\n result.push(child);\n }\n return result;\n}\n\nexport function Match(_props: MatchProps): null {\n if (isDevelopmentEnvironment()) {\n throw new Error(\n '[askr] <Match> may only be used as a direct child of <Case>.'\n );\n }\n return null;\n}\n\nfunction createCaseFallbackRenderer(fallback: unknown): (() => VNode) | null {\n const normalizedFallback = normalizeBoundaryChild(fallback);\n if (normalizedFallback == null || normalizedFallback === false) {\n return null;\n }\n return () => normalizedFallback;\n}\n\nfunction createMatchBranchKey(\n key: string | number | null | undefined,\n index: number\n): string | number {\n if (key == null) {\n return index;\n }\n return `match:${index}:${typeof key}:${String(key)}`;\n}\n\nfunction readMatchBranches(children: unknown): MatchBranch[] {\n const branches: MatchBranch[] = [];\n const flatChildren = flattenChildren(children);\n\n for (let index = 0; index < flatChildren.length; index += 1) {\n const child = flatChildren[index];\n if (child == null || child === false) {\n continue;\n }\n\n if (\n typeof child === 'object' &&\n child !== null &&\n 'type' in child &&\n (child as JSXElement).type === Match\n ) {\n const element = child as JSXElement;\n const props = (element.props ?? {}) as MatchProps;\n const key = createMatchBranchKey(element.key, index);\n const render =\n typeof props.children === 'function'\n ? () =>\n normalizeBoundaryChild((props.children as () => VNode)()) as VNode\n : () => normalizeBoundaryChild(props.children) as VNode;\n branches.push({\n key,\n render,\n when: props.when,\n });\n continue;\n }\n\n if (isDevelopmentEnvironment()) {\n throw new Error('[askr] <Case> only accepts <Match> children.');\n }\n }\n\n return branches;\n}\n\nfunction CasePrimitive(props: CaseProps): JSXElement {\n const matches = readMatchBranches(props.children);\n const fallback = createCaseFallbackRenderer(props.fallback);\n\n const caseStateContainer = state<CaseState>(\n createCaseState(matches, fallback)\n );\n const caseState = caseStateContainer();\n\n caseState.matches = matches;\n caseState.fallback = fallback;\n\n return {\n type: __CONTROL_BOUNDARY__,\n _controlState: caseState,\n } as unknown as JSXElement;\n}\n\nexport const Case = markEagerControlPrimitive(\n CasePrimitive as (props: CaseProps) => JSXElement\n) as (props: CaseProps) => JSXElement;\n"],"mappings":";;;;;;AA0BA,SAAS,gBAAgB,UAA8B;
|
|
1
|
+
{"version":3,"file":"case.js","names":[],"sources":["../../src/control/case.ts"],"sourcesContent":["import {\n __CONTROL_BOUNDARY__,\n markEagerControlPrimitive,\n} from '../common/control';\nimport type { JSXElement } from '../common/jsx';\nimport type { VNode } from '../common/vnode';\nimport { isDevelopmentEnvironment } from '../common/env';\nimport {\n createCaseState,\n type CaseState,\n type MatchBranch,\n} from '../runtime/control';\nimport { state } from '../runtime/state';\nimport { normalizeBoundaryChild } from './shared';\n\nexport type MatchProps = {\n key?: string | number | null;\n when: unknown;\n children: unknown;\n};\n\nexport type CaseProps = {\n fallback?: unknown;\n children?: unknown;\n};\n\nfunction flattenChildren(children: unknown): unknown[] {\n if (!Array.isArray(children)) {\n return children == null || children === false ? [] : [children];\n }\n\n const result: unknown[] = [];\n for (const child of children) {\n if (Array.isArray(child)) {\n result.push(...flattenChildren(child));\n continue;\n }\n result.push(child);\n }\n return result;\n}\n\nexport function Match(_props: MatchProps): null {\n if (isDevelopmentEnvironment()) {\n throw new Error(\n '[askr] <Match> may only be used as a direct child of <Case>.'\n );\n }\n return null;\n}\n\nfunction createCaseFallbackRenderer(fallback: unknown): (() => VNode) | null {\n const normalizedFallback = normalizeBoundaryChild(fallback);\n if (normalizedFallback == null || normalizedFallback === false) {\n return null;\n }\n return () => normalizedFallback;\n}\n\nfunction createMatchBranchKey(\n key: string | number | null | undefined,\n index: number\n): string | number {\n if (key == null) {\n return index;\n }\n return `match:${index}:${typeof key}:${String(key)}`;\n}\n\nfunction readMatchBranches(children: unknown): MatchBranch[] {\n const branches: MatchBranch[] = [];\n const flatChildren = flattenChildren(children);\n\n for (let index = 0; index < flatChildren.length; index += 1) {\n const child = flatChildren[index];\n if (child == null || child === false) {\n continue;\n }\n\n if (\n typeof child === 'object' &&\n child !== null &&\n 'type' in child &&\n (child as JSXElement).type === Match\n ) {\n const element = child as JSXElement;\n const props = (element.props ?? {}) as MatchProps;\n const key = createMatchBranchKey(element.key, index);\n const render =\n typeof props.children === 'function'\n ? () =>\n normalizeBoundaryChild((props.children as () => VNode)()) as VNode\n : () => normalizeBoundaryChild(props.children) as VNode;\n branches.push({\n key,\n render,\n when: props.when,\n });\n continue;\n }\n\n if (isDevelopmentEnvironment()) {\n throw new Error('[askr] <Case> only accepts <Match> children.');\n }\n }\n\n return branches;\n}\n\nfunction CasePrimitive(props: CaseProps): JSXElement {\n const matches = readMatchBranches(props.children);\n const fallback = createCaseFallbackRenderer(props.fallback);\n\n const caseStateContainer = state<CaseState>(\n createCaseState(matches, fallback)\n );\n const caseState = caseStateContainer();\n\n caseState.matches = matches;\n caseState.fallback = fallback;\n\n return {\n type: __CONTROL_BOUNDARY__,\n _controlState: caseState,\n } as unknown as JSXElement;\n}\n\nexport const Case = markEagerControlPrimitive(\n CasePrimitive as (props: CaseProps) => JSXElement\n) as (props: CaseProps) => JSXElement;\n"],"mappings":";;;;;;AA0BA,SAAS,gBAAgB,UAA8B;CACrD,IAAI,CAAC,MAAM,QAAQ,SAAS,EAC1B,OAAO,YAAY,QAAQ,aAAa,QAAQ,EAAE,GAAG,CAAC,SAAS;CAGjE,MAAM,SAAoB,EAAE;CAC5B,KAAK,MAAM,SAAS,UAAU;EAC5B,IAAI,MAAM,QAAQ,MAAM,EAAE;GACxB,OAAO,KAAK,GAAG,gBAAgB,MAAM,CAAC;GACtC;;EAEF,OAAO,KAAK,MAAM;;CAEpB,OAAO;;AAGT,SAAgB,MAAM,QAA0B;CAC9C,IAAI,0BAA0B,EAC5B,MAAM,IAAI,MACR,+DACD;CAEH,OAAO;;AAGT,SAAS,2BAA2B,UAAyC;CAC3E,MAAM,qBAAqB,uBAAuB,SAAS;CAC3D,IAAI,sBAAsB,QAAQ,uBAAuB,OACvD,OAAO;CAET,aAAa;;AAGf,SAAS,qBACP,KACA,OACiB;CACjB,IAAI,OAAO,MACT,OAAO;CAET,OAAO,SAAS,MAAM,GAAG,OAAO,IAAI,GAAG,OAAO,IAAI;;AAGpD,SAAS,kBAAkB,UAAkC;CAC3D,MAAM,WAA0B,EAAE;CAClC,MAAM,eAAe,gBAAgB,SAAS;CAE9C,KAAK,IAAI,QAAQ,GAAG,QAAQ,aAAa,QAAQ,SAAS,GAAG;EAC3D,MAAM,QAAQ,aAAa;EAC3B,IAAI,SAAS,QAAQ,UAAU,OAC7B;EAGF,IACE,OAAO,UAAU,YACjB,UAAU,QACV,UAAU,SACT,MAAqB,SAAS,OAC/B;GACA,MAAM,UAAU;GAChB,MAAM,QAAS,QAAQ,SAAS,EAAE;GAClC,MAAM,MAAM,qBAAqB,QAAQ,KAAK,MAAM;GACpD,MAAM,SACJ,OAAO,MAAM,aAAa,mBAEpB,uBAAwB,MAAM,UAA0B,CAAC,SACrD,uBAAuB,MAAM,SAAS;GAClD,SAAS,KAAK;IACZ;IACA;IACA,MAAM,MAAM;IACb,CAAC;GACF;;EAGF,IAAI,0BAA0B,EAC5B,MAAM,IAAI,MAAM,+CAA+C;;CAInE,OAAO;;AAGT,SAAS,cAAc,OAA8B;CACnD,MAAM,UAAU,kBAAkB,MAAM,SAAS;CACjD,MAAM,WAAW,2BAA2B,MAAM,SAAS;CAK3D,MAAM,YAHqB,MACzB,gBAAgB,SAAS,SAAS,CAElB,EAAoB;CAEtC,UAAU,UAAU;CACpB,UAAU,WAAW;CAErB,OAAO;EACL,MAAM;EACN,eAAe;EAChB;;AAGH,MAAa,OAAO,0BAClB,cACD"}
|
package/dist/control/for.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"for.js","names":[],"sources":["../../src/control/for.ts"],"sourcesContent":["/**\n * For JSX primitive\n *\n * Creates a keyed child-scope boundary for efficient list rendering.\n */\n\nimport {\n __CONTROL_BOUNDARY__,\n markEagerControlPrimitive,\n} from '../common/control';\nimport type { JSXElement } from '../common/jsx';\nimport type { DOMElement, VNode } from '../common/vnode';\nimport { createFineGrainedEffect } from '../runtime/effect';\nimport { type ForState, useForState } from '../runtime/for';\nimport { normalizeBoundaryChild } from './shared';\n\nexport type ForEachSource<T> = T[] | (() => T[]);\n\ntype ForBaseProps<T> = {\n each: ForEachSource<T>;\n fallback?: unknown;\n children: (item: T, index: () => number) => VNode;\n};\n\ntype KeyedForProps<T, K extends string | number> = ForBaseProps<T> & {\n by: (item: T, index: number) => K;\n byIndex?: never;\n};\n\ntype IndexedForProps<T> = ForBaseProps<T> & {\n by?: never;\n byIndex: true;\n};\n\nexport type ForProps<T, K extends string | number = string | number> =\n | KeyedForProps<T, K>\n | IndexedForProps<T>;\n\nfunction resolveEach<T>(each: ForEachSource<T>): T[] {\n const resolved = typeof each === 'function' ? each() : each;\n if (!Array.isArray(resolved)) {\n throw new Error('For each must resolve to an array.');\n }\n return resolved;\n}\n\nfunction resolveKeyFn<T>(\n props: ForProps<T>\n): (item: T, index: number) => string | number {\n if ('by' in props && typeof props.by === 'function') {\n return props.by;\n }\n if ('byIndex' in props && props.byIndex === true) {\n return (_item, index) => index;\n }\n throw new Error(\n '[askr] <For> requires a stable `by` key function. Use `byIndex` only as an explicit positional escape hatch.'\n );\n}\n\nfunction createForBoundary<T>(props: ForProps<T>): DOMElement {\n if ('by' in props && typeof props.by === 'function' && 'byIndex' in props) {\n throw new Error('[askr] <For> accepts either `by` or `byIndex`, not both.');\n }\n\n const byFn = resolveKeyFn(props);\n const fallback = normalizeBoundaryChild(props.fallback);\n const forState = useForState(props.each, byFn, props.children, fallback);\n\n const computeItems = () => resolveEach(forState.eachSource);\n if (!forState._sourceEffect) {\n forState._suspendSourceCommit = true;\n forState._sourceEffect = createFineGrainedEffect({\n lane: 'reactive',\n compute: computeItems,\n commit: (items) => {\n forState.currentItems = items;\n\n if (forState._suspendSourceCommit) {\n return;\n }\n\n forState._needsSourceReconcile = true;\n\n if (forState._enqueueBoundaryCommit) {\n forState._enqueueBoundaryCommit();\n return;\n }\n\n forState.parentInstance?._enqueueRun?.();\n },\n });\n forState._suspendSourceCommit = false;\n } else {\n forState._suspendSourceCommit = true;\n forState._sourceEffect.updateCompute(computeItems);\n forState._suspendSourceCommit = false;\n }\n\n return {\n type: __CONTROL_BOUNDARY__,\n _controlState: forState as unknown as ForState<unknown>,\n _forState: forState as unknown as ForState<unknown>,\n };\n}\n\nfunction ForPrimitive<T>(props: ForProps<T>): JSXElement {\n return createForBoundary(props) as unknown as JSXElement;\n}\n\nexport const For = markEagerControlPrimitive(\n ForPrimitive as <T, K extends string | number = string | number>(\n props: ForProps<T, K>\n ) => JSXElement\n) as <T, K extends string | number = string | number>(\n props: ForProps<T, K>\n) => JSXElement;\n"],"mappings":";;;;;;;;;;AAsCA,SAAS,YAAe,MAA6B;CACnD,MAAM,WAAW,OAAO,SAAS,aAAa,MAAM,GAAG;
|
|
1
|
+
{"version":3,"file":"for.js","names":[],"sources":["../../src/control/for.ts"],"sourcesContent":["/**\n * For JSX primitive\n *\n * Creates a keyed child-scope boundary for efficient list rendering.\n */\n\nimport {\n __CONTROL_BOUNDARY__,\n markEagerControlPrimitive,\n} from '../common/control';\nimport type { JSXElement } from '../common/jsx';\nimport type { DOMElement, VNode } from '../common/vnode';\nimport { createFineGrainedEffect } from '../runtime/effect';\nimport { type ForState, useForState } from '../runtime/for';\nimport { normalizeBoundaryChild } from './shared';\n\nexport type ForEachSource<T> = T[] | (() => T[]);\n\ntype ForBaseProps<T> = {\n each: ForEachSource<T>;\n fallback?: unknown;\n children: (item: T, index: () => number) => VNode;\n};\n\ntype KeyedForProps<T, K extends string | number> = ForBaseProps<T> & {\n by: (item: T, index: number) => K;\n byIndex?: never;\n};\n\ntype IndexedForProps<T> = ForBaseProps<T> & {\n by?: never;\n byIndex: true;\n};\n\nexport type ForProps<T, K extends string | number = string | number> =\n | KeyedForProps<T, K>\n | IndexedForProps<T>;\n\nfunction resolveEach<T>(each: ForEachSource<T>): T[] {\n const resolved = typeof each === 'function' ? each() : each;\n if (!Array.isArray(resolved)) {\n throw new Error('For each must resolve to an array.');\n }\n return resolved;\n}\n\nfunction resolveKeyFn<T>(\n props: ForProps<T>\n): (item: T, index: number) => string | number {\n if ('by' in props && typeof props.by === 'function') {\n return props.by;\n }\n if ('byIndex' in props && props.byIndex === true) {\n return (_item, index) => index;\n }\n throw new Error(\n '[askr] <For> requires a stable `by` key function. Use `byIndex` only as an explicit positional escape hatch.'\n );\n}\n\nfunction createForBoundary<T>(props: ForProps<T>): DOMElement {\n if ('by' in props && typeof props.by === 'function' && 'byIndex' in props) {\n throw new Error('[askr] <For> accepts either `by` or `byIndex`, not both.');\n }\n\n const byFn = resolveKeyFn(props);\n const fallback = normalizeBoundaryChild(props.fallback);\n const forState = useForState(props.each, byFn, props.children, fallback);\n\n const computeItems = () => resolveEach(forState.eachSource);\n if (!forState._sourceEffect) {\n forState._suspendSourceCommit = true;\n forState._sourceEffect = createFineGrainedEffect({\n lane: 'reactive',\n compute: computeItems,\n commit: (items) => {\n forState.currentItems = items;\n\n if (forState._suspendSourceCommit) {\n return;\n }\n\n forState._needsSourceReconcile = true;\n\n if (forState._enqueueBoundaryCommit) {\n forState._enqueueBoundaryCommit();\n return;\n }\n\n forState.parentInstance?._enqueueRun?.();\n },\n });\n forState._suspendSourceCommit = false;\n } else {\n forState._suspendSourceCommit = true;\n forState._sourceEffect.updateCompute(computeItems);\n forState._suspendSourceCommit = false;\n }\n\n return {\n type: __CONTROL_BOUNDARY__,\n _controlState: forState as unknown as ForState<unknown>,\n _forState: forState as unknown as ForState<unknown>,\n };\n}\n\nfunction ForPrimitive<T>(props: ForProps<T>): JSXElement {\n return createForBoundary(props) as unknown as JSXElement;\n}\n\nexport const For = markEagerControlPrimitive(\n ForPrimitive as <T, K extends string | number = string | number>(\n props: ForProps<T, K>\n ) => JSXElement\n) as <T, K extends string | number = string | number>(\n props: ForProps<T, K>\n) => JSXElement;\n"],"mappings":";;;;;;;;;;AAsCA,SAAS,YAAe,MAA6B;CACnD,MAAM,WAAW,OAAO,SAAS,aAAa,MAAM,GAAG;CACvD,IAAI,CAAC,MAAM,QAAQ,SAAS,EAC1B,MAAM,IAAI,MAAM,qCAAqC;CAEvD,OAAO;;AAGT,SAAS,aACP,OAC6C;CAC7C,IAAI,QAAQ,SAAS,OAAO,MAAM,OAAO,YACvC,OAAO,MAAM;CAEf,IAAI,aAAa,SAAS,MAAM,YAAY,MAC1C,QAAQ,OAAO,UAAU;CAE3B,MAAM,IAAI,MACR,+GACD;;AAGH,SAAS,kBAAqB,OAAgC;CAC5D,IAAI,QAAQ,SAAS,OAAO,MAAM,OAAO,cAAc,aAAa,OAClE,MAAM,IAAI,MAAM,2DAA2D;CAG7E,MAAM,OAAO,aAAa,MAAM;CAChC,MAAM,WAAW,uBAAuB,MAAM,SAAS;CACvD,MAAM,WAAW,YAAY,MAAM,MAAM,MAAM,MAAM,UAAU,SAAS;CAExE,MAAM,qBAAqB,YAAY,SAAS,WAAW;CAC3D,IAAI,CAAC,SAAS,eAAe;EAC3B,SAAS,uBAAuB;EAChC,SAAS,gBAAgB,wBAAwB;GAC/C,MAAM;GACN,SAAS;GACT,SAAS,UAAU;IACjB,SAAS,eAAe;IAExB,IAAI,SAAS,sBACX;IAGF,SAAS,wBAAwB;IAEjC,IAAI,SAAS,wBAAwB;KACnC,SAAS,wBAAwB;KACjC;;IAGF,SAAS,gBAAgB,eAAe;;GAE3C,CAAC;EACF,SAAS,uBAAuB;QAC3B;EACL,SAAS,uBAAuB;EAChC,SAAS,cAAc,cAAc,aAAa;EAClD,SAAS,uBAAuB;;CAGlC,OAAO;EACL,MAAM;EACN,eAAe;EACf,WAAW;EACZ;;AAGH,SAAS,aAAgB,OAAgC;CACvD,OAAO,kBAAkB,MAAM;;AAGjC,MAAa,MAAM,0BACjB,aAGD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.js","names":[],"sources":["../../src/control/shared.ts"],"sourcesContent":["import { Fragment } from '../common/jsx';\nimport type { DOMElement, VNode } from '../common/vnode';\n\nexport function normalizeBoundaryChild(value: unknown): VNode | null {\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return null;\n }\n if (value.length === 1) {\n return normalizeBoundaryChild(value[0]);\n }\n return {\n type: Fragment,\n props: {\n children: value,\n },\n } as DOMElement;\n }\n\n return (value ?? null) as VNode | null;\n}\n\nexport function resolveMaybeAccessor<T>(value: T | (() => T)): T {\n return typeof value === 'function' ? (value as () => T)() : value;\n}\n"],"mappings":";;AAGA,SAAgB,uBAAuB,OAA8B;
|
|
1
|
+
{"version":3,"file":"shared.js","names":[],"sources":["../../src/control/shared.ts"],"sourcesContent":["import { Fragment } from '../common/jsx';\nimport type { DOMElement, VNode } from '../common/vnode';\n\nexport function normalizeBoundaryChild(value: unknown): VNode | null {\n if (Array.isArray(value)) {\n if (value.length === 0) {\n return null;\n }\n if (value.length === 1) {\n return normalizeBoundaryChild(value[0]);\n }\n return {\n type: Fragment,\n props: {\n children: value,\n },\n } as DOMElement;\n }\n\n return (value ?? null) as VNode | null;\n}\n\nexport function resolveMaybeAccessor<T>(value: T | (() => T)): T {\n return typeof value === 'function' ? (value as () => T)() : value;\n}\n"],"mappings":";;AAGA,SAAgB,uBAAuB,OAA8B;CACnE,IAAI,MAAM,QAAQ,MAAM,EAAE;EACxB,IAAI,MAAM,WAAW,GACnB,OAAO;EAET,IAAI,MAAM,WAAW,GACnB,OAAO,uBAAuB,MAAM,GAAG;EAEzC,OAAO;GACL,MAAM;GACN,OAAO,EACL,UAAU,OACX;GACF;;CAGH,OAAQ,SAAS;;AAGnB,SAAgB,qBAAwB,OAAyB;CAC/D,OAAO,OAAO,UAAU,aAAc,OAAmB,GAAG"}
|
package/dist/control/show.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"show.js","names":[],"sources":["../../src/control/show.ts"],"sourcesContent":["import {\n __CONTROL_BOUNDARY__,\n markEagerControlPrimitive,\n} from '../common/control';\nimport type { JSXElement } from '../common/jsx';\nimport type { VNode } from '../common/vnode';\nimport { createShowState, type ShowState } from '../runtime/control';\nimport { state } from '../runtime/state';\nimport { normalizeBoundaryChild, resolveMaybeAccessor } from './shared';\n\ntype ShowSource<T> = T | (() => T);\n\nexport type ShowProps<T> = {\n when: ShowSource<T>;\n fallback?: unknown;\n children: unknown | ((value: NonNullable<T>) => VNode);\n};\n\nfunction createTruthyRenderer<T>(\n props: ShowProps<T>\n): ShowState['renderTruthy'] {\n if (typeof props.children === 'function') {\n return (value: unknown) =>\n normalizeBoundaryChild(\n (props.children as (resolved: NonNullable<T>) => VNode)(\n value as NonNullable<T>\n )\n ) as VNode;\n }\n\n const staticChild = normalizeBoundaryChild(props.children);\n return () => staticChild as VNode;\n}\n\nfunction createFallbackRenderer(fallback: unknown): (() => VNode) | null {\n const normalizedFallback = normalizeBoundaryChild(fallback);\n if (normalizedFallback == null || normalizedFallback === false) {\n return null;\n }\n return () => normalizedFallback;\n}\n\nfunction ShowPrimitive<T>(props: ShowProps<T>): JSXElement {\n const selectedValue = resolveMaybeAccessor(props.when);\n const renderTruthy = createTruthyRenderer(props);\n const renderFallback = createFallbackRenderer(props.fallback);\n\n const showStateContainer = state<ShowState>(\n createShowState(selectedValue, renderTruthy, renderFallback)\n );\n const showState = showStateContainer();\n\n showState.selectedValue = selectedValue;\n showState.renderTruthy = renderTruthy;\n showState.renderFallback = renderFallback;\n\n return {\n type: __CONTROL_BOUNDARY__,\n _controlState: showState,\n } as unknown as JSXElement;\n}\n\nexport const Show = markEagerControlPrimitive(\n ShowPrimitive as <T>(props: ShowProps<T>) => JSXElement\n) as <T>(props: ShowProps<T>) => JSXElement;\n"],"mappings":";;;;;AAkBA,SAAS,qBACP,OAC2B;
|
|
1
|
+
{"version":3,"file":"show.js","names":[],"sources":["../../src/control/show.ts"],"sourcesContent":["import {\n __CONTROL_BOUNDARY__,\n markEagerControlPrimitive,\n} from '../common/control';\nimport type { JSXElement } from '../common/jsx';\nimport type { VNode } from '../common/vnode';\nimport { createShowState, type ShowState } from '../runtime/control';\nimport { state } from '../runtime/state';\nimport { normalizeBoundaryChild, resolveMaybeAccessor } from './shared';\n\ntype ShowSource<T> = T | (() => T);\n\nexport type ShowProps<T> = {\n when: ShowSource<T>;\n fallback?: unknown;\n children: unknown | ((value: NonNullable<T>) => VNode);\n};\n\nfunction createTruthyRenderer<T>(\n props: ShowProps<T>\n): ShowState['renderTruthy'] {\n if (typeof props.children === 'function') {\n return (value: unknown) =>\n normalizeBoundaryChild(\n (props.children as (resolved: NonNullable<T>) => VNode)(\n value as NonNullable<T>\n )\n ) as VNode;\n }\n\n const staticChild = normalizeBoundaryChild(props.children);\n return () => staticChild as VNode;\n}\n\nfunction createFallbackRenderer(fallback: unknown): (() => VNode) | null {\n const normalizedFallback = normalizeBoundaryChild(fallback);\n if (normalizedFallback == null || normalizedFallback === false) {\n return null;\n }\n return () => normalizedFallback;\n}\n\nfunction ShowPrimitive<T>(props: ShowProps<T>): JSXElement {\n const selectedValue = resolveMaybeAccessor(props.when);\n const renderTruthy = createTruthyRenderer(props);\n const renderFallback = createFallbackRenderer(props.fallback);\n\n const showStateContainer = state<ShowState>(\n createShowState(selectedValue, renderTruthy, renderFallback)\n );\n const showState = showStateContainer();\n\n showState.selectedValue = selectedValue;\n showState.renderTruthy = renderTruthy;\n showState.renderFallback = renderFallback;\n\n return {\n type: __CONTROL_BOUNDARY__,\n _controlState: showState,\n } as unknown as JSXElement;\n}\n\nexport const Show = markEagerControlPrimitive(\n ShowPrimitive as <T>(props: ShowProps<T>) => JSXElement\n) as <T>(props: ShowProps<T>) => JSXElement;\n"],"mappings":";;;;;AAkBA,SAAS,qBACP,OAC2B;CAC3B,IAAI,OAAO,MAAM,aAAa,YAC5B,QAAQ,UACN,uBACG,MAAM,SACL,MACD,CACF;CAGL,MAAM,cAAc,uBAAuB,MAAM,SAAS;CAC1D,aAAa;;AAGf,SAAS,uBAAuB,UAAyC;CACvE,MAAM,qBAAqB,uBAAuB,SAAS;CAC3D,IAAI,sBAAsB,QAAQ,uBAAuB,OACvD,OAAO;CAET,aAAa;;AAGf,SAAS,cAAiB,OAAiC;CACzD,MAAM,gBAAgB,qBAAqB,MAAM,KAAK;CACtD,MAAM,eAAe,qBAAqB,MAAM;CAChD,MAAM,iBAAiB,uBAAuB,MAAM,SAAS;CAK7D,MAAM,YAHqB,MACzB,gBAAgB,eAAe,cAAc,eAAe,CAE5C,EAAoB;CAEtC,UAAU,gBAAgB;CAC1B,UAAU,eAAe;CACzB,UAAU,iBAAiB;CAE3B,OAAO;EACL,MAAM;EACN,eAAe;EAChB;;AAGH,MAAa,OAAO,0BAClB,cACD"}
|
package/dist/data/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/data/index.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/data/index.ts"],"mappings":";KAeY,gBAAA;AAAA,KAMA,KAAA;EACV,IAAA,EAAM,CAAA;EACN,KAAA;EACA,OAAA;EACA,UAAA;EACA,KAAA;EACA,WAAA,EAAa,gBAAA;EAEb,OAAA,IAAW,OAAA;AAAA;AAAA,KAGD,QAAA;EACV,MAAA;EACA,OAAA;EACA,KAAA;EACA,MAAA,EAAQ,OAAA;EAER,OAAA,CAAQ,KAAA,EAAO,MAAA,GAAS,OAAA,CAAQ,OAAA;EAChC,KAAA;EACA,KAAA;AAAA;AAAA,KAGG,YAAA;EACH,GAAA;EACA,KAAA,GAAQ,GAAA;IAAO,MAAA,EAAQ,WAAA;EAAA,MAAkB,OAAA,CAAQ,CAAA;EACjD,YAAA,IAAgB,IAAA,EAAM,CAAA;EACtB,SAAA,IAAa,IAAA,EAAM,CAAA,EAAG,GAAA;IAAO,GAAA;EAAA,MAAkB,OAAA;AAAA;AAAA,KAG5C,eAAA;EACH,MAAA,GAAS,KAAA,EAAO,MAAA,EAAQ,GAAA;IAAO,MAAA,EAAQ,WAAA;EAAA,MAAkB,OAAA,CAAQ,OAAA;EACjE,OAAA,IAAW,KAAA,EAAO,MAAA,EAAQ,MAAA,EAAQ,OAAA;EAClC,YAAA;AAAA;AAAA,iBAqjBc,WAAA,GAAA,CAAe,OAAA,EAAS,YAAA,CAAa,CAAA,IAAK,KAAA,CAAM,CAAA;AAAA,iBAyChD,UAAA,CAAW,MAAA;AAAA,iBAIX,cAAA,iBAAA,CACd,OAAA,EAAS,eAAA,CAAgB,MAAA,EAAQ,OAAA,IAChC,QAAA,CAAS,MAAA,EAAQ,OAAA"}
|