@askrjs/askr 0.0.35 → 0.0.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -9
- package/dist/bench/components/benchmark-row.js.map +1 -1
- package/dist/bench/components/benchmark-table.js +1 -1
- package/dist/bench/components/benchmark-table.js.map +1 -1
- package/dist/benchmark.js +3 -3
- package/dist/benchmark.js.map +1 -1
- package/dist/bin/askr-ssg.js.map +1 -1
- package/dist/boot/index.d.ts +1 -7
- package/dist/boot/index.d.ts.map +1 -1
- package/dist/boot/index.js +31 -42
- 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/index.d.ts +2 -0
- package/dist/components/index.js +2 -0
- package/dist/components/link.d.ts +4 -5
- package/dist/components/link.d.ts.map +1 -1
- package/dist/components/link.js +2 -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.d.ts.map +1 -1
- package/dist/foundations/icon/icon.js.map +1 -1
- package/dist/foundations/icon/icon.types.d.ts +2 -1
- package/dist/foundations/icon/icon.types.d.ts.map +1 -1
- package/dist/foundations/icon/index.d.ts +3 -0
- package/dist/foundations/icon/index.js +2 -0
- package/dist/foundations/index.d.ts +1 -18
- package/dist/foundations/index.js +2 -18
- 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/index.d.ts +7 -0
- package/dist/foundations/interactions/index.js +7 -0
- package/dist/foundations/interactions/interaction-policy.js +2 -2
- 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/state/index.d.ts +2 -0
- package/dist/foundations/state/index.js +2 -0
- package/dist/foundations/structures/collection.js.map +1 -1
- package/dist/foundations/structures/index.d.ts +8 -0
- package/dist/foundations/structures/index.js +7 -0
- 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/index.d.ts +7 -0
- package/dist/foundations/utilities/index.js +6 -0
- 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 -16
- package/dist/index.js +1 -17
- package/dist/index.js.map +1 -1
- package/dist/jsx/utils.js.map +1 -1
- package/dist/jsx-dev-runtime.d.ts +1 -1
- package/dist/jsx-dev-runtime.js.map +1 -1
- package/dist/jsx-runtime.d.ts +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 +92 -357
- 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 +20 -18
- package/dist/renderer/fastpath.js.map +1 -1
- package/dist/renderer/for-commit.js +486 -0
- package/dist/renderer/for-commit.js.map +1 -0
- package/dist/renderer/index.js.map +1 -1
- package/dist/renderer/keyed.js +84 -23
- package/dist/renderer/keyed.js.map +1 -1
- package/dist/renderer/reconcile.js +11 -18
- package/dist/renderer/reconcile.js.map +1 -1
- package/dist/renderer/utils.js +5 -1
- package/dist/renderer/utils.js.map +1 -1
- package/dist/resources/index.d.ts +1 -2
- package/dist/resources/index.js +1 -2
- 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 +2 -33
- 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 +90 -31
- package/dist/runtime/for.js.map +1 -1
- package/dist/runtime/operations.d.ts.map +1 -1
- package/dist/runtime/operations.js +10 -8
- 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 +182 -103
- 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/index.d.ts +1 -6
- package/dist/ssg/index.js +1 -6
- 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 +3 -1
- package/dist/ssr/context.d.ts.map +1 -1
- package/dist/ssr/context.js +31 -11
- 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 +2 -31
- package/dist/ssr/index.d.ts.map +1 -1
- package/dist/ssr/index.js +19 -49
- package/dist/ssr/index.js.map +1 -1
- package/dist/ssr/render-keys.js +1 -12
- package/dist/ssr/render-keys.js.map +1 -1
- package/dist/ssr/sink.js.map +1 -1
- package/dist/ssr/verify-hydration.js +25 -0
- package/dist/ssr/verify-hydration.js.map +1 -0
- package/package.json +44 -19
- package/dist/_virtual/_rolldown/runtime.js +0 -7
- package/dist/foundations/core.d.ts +0 -15
- package/dist/foundations/core.js +0 -13
- package/dist/foundations/structures.d.ts +0 -7
- package/dist/foundations/structures.js +0 -6
- package/dist/ssg/batch-render.d.ts +0 -16
- package/dist/ssg/batch-render.d.ts.map +0 -1
- package/dist/ssg/generate-metadata.d.ts +0 -24
- package/dist/ssg/generate-metadata.d.ts.map +0 -1
- package/dist/ssg/resolve-ssg-data.d.ts +0 -23
- package/dist/ssg/resolve-ssg-data.d.ts.map +0 -1
- package/dist/ssg/route-utils.d.ts +0 -16
- package/dist/ssg/route-utils.d.ts.map +0 -1
- package/dist/ssg/write-static-files.d.ts +0 -15
- package/dist/ssg/write-static-files.d.ts.map +0 -1
- package/dist/ssr/render-keys.d.ts +0 -37
- package/dist/ssr/render-keys.d.ts.map +0 -1
- package/dist/ssr/stream-render.js +0 -76
- package/dist/ssr/stream-render.js.map +0 -1
|
@@ -2,9 +2,10 @@ import { globalScheduler } from "./scheduler.js";
|
|
|
2
2
|
import { getCurrentContextFrame } from "./context.js";
|
|
3
3
|
import { getCurrentComponentInstance, registerMountOperation } from "./component.js";
|
|
4
4
|
import { state } from "./state.js";
|
|
5
|
+
import { brandSnapshotSource } from "./snapshot-source.js";
|
|
6
|
+
import { SSRDataMissingError } from "../common/ssr-errors.js";
|
|
5
7
|
import { getSSRBridge } from "./ssr-bridge.js";
|
|
6
8
|
import { ResourceCell } from "./resource-cell.js";
|
|
7
|
-
import { SSRDataMissingError } from "../common/ssr-errors.js";
|
|
8
9
|
//#region src/runtime/operations.ts
|
|
9
10
|
/**
|
|
10
11
|
* Resource primitive — simple, deterministic async primitive
|
|
@@ -25,12 +26,13 @@ function resource(fn, deps = []) {
|
|
|
25
26
|
if (renderData) {
|
|
26
27
|
const key = ssr.getNextKey();
|
|
27
28
|
if (!(key in renderData)) ssr.throwSSRDataMissing();
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
const val = renderData[key];
|
|
30
|
+
return brandSnapshotSource({
|
|
31
|
+
value: val,
|
|
30
32
|
pending: false,
|
|
31
33
|
error: null,
|
|
32
34
|
refresh: () => {}
|
|
33
|
-
};
|
|
35
|
+
});
|
|
34
36
|
}
|
|
35
37
|
if (ssr.getCurrentSSRContext()) ssr.throwSSRDataMissing();
|
|
36
38
|
throw new Error("[Askr] resource() must be called during component render inside an app. Do not create resources at module scope or outside render.");
|
|
@@ -43,12 +45,12 @@ function resource(fn, deps = []) {
|
|
|
43
45
|
const val = renderData[key];
|
|
44
46
|
const holder = state({
|
|
45
47
|
cell: void 0,
|
|
46
|
-
snapshot: {
|
|
48
|
+
snapshot: brandSnapshotSource({
|
|
47
49
|
value: val,
|
|
48
50
|
pending: false,
|
|
49
51
|
error: null,
|
|
50
52
|
refresh: () => {}
|
|
51
|
-
}
|
|
53
|
+
})
|
|
52
54
|
});
|
|
53
55
|
const h = holder();
|
|
54
56
|
h.snapshot.value = val;
|
|
@@ -59,12 +61,12 @@ function resource(fn, deps = []) {
|
|
|
59
61
|
}
|
|
60
62
|
const holder = state({
|
|
61
63
|
cell: void 0,
|
|
62
|
-
snapshot: {
|
|
64
|
+
snapshot: brandSnapshotSource({
|
|
63
65
|
value: null,
|
|
64
66
|
pending: true,
|
|
65
67
|
error: null,
|
|
66
68
|
refresh: () => {}
|
|
67
|
-
}
|
|
69
|
+
})
|
|
68
70
|
});
|
|
69
71
|
const h = holder();
|
|
70
72
|
if (!h.cell) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operations.js","names":[],"sources":["../../src/runtime/operations.ts"],"sourcesContent":["import {\n getCurrentComponentInstance,\n registerMountOperation,\n type ComponentInstance,\n} from './component';\nimport { getCurrentContextFrame } from './context';\nimport { ResourceCell } from './resource-cell';\nimport { state } from './state';\nimport { globalScheduler } from './scheduler';\nimport { getSSRBridge } from './ssr-bridge';\nimport { SSRDataMissingError } from '../common/ssr-errors';\n\nexport interface ResourceResult<T> {\n value: T | null;\n pending: boolean;\n error: Error | null;\n refresh(): void;\n}\n\n/**\n * Resource primitive — simple, deterministic async primitive\n * Usage: resource(fn, deps)\n * - fn receives { signal }\n * - captures execution context once at creation (synchronous step only)\n * - executes at most once per generation; stale async results are ignored\n * - refresh() cancels in-flight execution, increments generation and re-runs\n * - exposes { value, pending, error, refresh }\n * - during SSR, async results are disallowed and will throw synchronously\n */\nexport function resource<T>(\n fn: (opts: { signal: AbortSignal }) => Promise<T> | T,\n deps: unknown[] = []\n): ResourceResult<T> {\n const instance = getCurrentComponentInstance();\n // Create a non-null alias early so it can be used in nested closures\n // without TypeScript complaining about possible null access.\n const inst = instance as ComponentInstance;\n\n if (!instance) {\n const ssr = getSSRBridge();\n // If we're in a synchronous SSR render that has resolved data, use it.\n const renderData = ssr.getCurrentRenderData();\n if (renderData) {\n const key = ssr.getNextKey();\n if (!(key in renderData)) {\n ssr.throwSSRDataMissing();\n }\n const val = renderData[key] as T;\n return {\n value: val,\n pending: false,\n error: null,\n refresh: () => {},\n } as ResourceResult<T>;\n }\n\n // If we are in an SSR render pass without supplied data, throw for clarity.\n const ssrCtx = ssr.getCurrentSSRContext();\n if (ssrCtx) {\n ssr.throwSSRDataMissing();\n }\n\n // No active component instance and not in SSR render with data.\n // Autopilot invariant: resources must be created during render within an app.\n throw new Error(\n '[Askr] resource() must be called during component render inside an app. ' +\n 'Do not create resources at module scope or outside render.'\n );\n }\n\n // Internal ResourceCell — pure state machine now moved to its own module\n // to keep component wiring separate and ensure no component access here.\n // (See ./resource-cell.ts)\n\n // If we're in a synchronous SSR render that was supplied resolved data, use it\n const ssr = getSSRBridge();\n const renderData = ssr.getCurrentRenderData();\n if (renderData) {\n // Deterministic key generation: the collection step and render step use\n // the same incremental key generation to align resources.\n const key = ssr.getNextKey();\n if (!(key in renderData)) {\n ssr.throwSSRDataMissing();\n }\n\n // Commit synchronous value from render data and return a stable snapshot\n const val = renderData[key] as T;\n\n const holder = state<{\n cell?: ResourceCell<T>;\n snapshot: ResourceResult<T>;\n }>({\n cell: undefined,\n snapshot: {\n value: val,\n pending: false,\n error: null,\n refresh: () => {},\n },\n });\n\n const h = holder();\n h.snapshot.value = val;\n h.snapshot.pending = false;\n h.snapshot.error = null;\n holder.set(h);\n return h.snapshot;\n }\n\n // Persist a holder so the snapshot identity is stable across renders.\n const holder = state<{ cell?: ResourceCell<T>; snapshot: ResourceResult<T> }>(\n {\n cell: undefined,\n snapshot: {\n value: null,\n pending: true,\n error: null,\n refresh: () => {},\n },\n }\n );\n\n const h = holder();\n\n // Initialize cell on first call\n if (!h.cell) {\n const frame = getCurrentContextFrame();\n const cell = new ResourceCell<T>(fn, deps, frame);\n // Attach debug label (component name) for richer logs\n cell.ownerName = inst.fn?.name || '<anonymous>';\n h.cell = cell;\n h.snapshot = cell.snapshot as ResourceResult<T>;\n\n // Subscribe and schedule component updates when cell changes\n const unsubscribe = cell.subscribe(() => {\n const cur = holder();\n cur.snapshot.value = cell.snapshot.value;\n cur.snapshot.pending = cell.snapshot.pending;\n cur.snapshot.error = cell.snapshot.error;\n holder.set(cur);\n try {\n inst._enqueueRun?.();\n } catch {\n // ignore\n }\n });\n\n // Cleanup on unmount\n (inst.cleanupFns ??= []).push(() => {\n unsubscribe();\n cell.abort();\n });\n\n // Render invariant: do NOT start async work during render on the client.\n // SSR remains strict/synchronous and must throw immediately if async is encountered.\n if (inst.ssr) {\n // SSR: must run synchronously so missing data throws during render\n cell.start(true, false);\n if (!cell.pending) {\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n }\n } else {\n // Client: start after render via scheduler (never inline)\n globalScheduler.enqueue(() => {\n try {\n cell.start(false, false);\n } catch (err) {\n // Non-SSR: reflect synchronous errors into snapshot via manual update\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = (err as Error) ?? null;\n holder.set(cur);\n inst._enqueueRun?.();\n return;\n }\n\n // If the resource completed synchronously, subscribers were not notified.\n // Force a re-render so the component can observe the value.\n if (!cell.pending) {\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n holder.set(cur);\n inst._enqueueRun?.();\n }\n });\n }\n }\n\n const cell = h.cell!;\n cell.setLoader(fn);\n\n // Detect dependency changes and refresh immediately\n const depsChanged =\n !cell.deps ||\n cell.deps.length !== deps.length ||\n cell.deps.some((d, i) => d !== deps[i]);\n\n if (depsChanged) {\n cell.deps = deps.slice();\n cell.generation++;\n cell.pending = true;\n cell.error = null;\n try {\n if (inst.ssr) {\n cell.start(true, false);\n if (!cell.pending) {\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n }\n } else {\n globalScheduler.enqueue(() => {\n cell.start(false, false);\n if (!cell.pending) {\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n holder.set(cur);\n inst._enqueueRun?.();\n }\n });\n }\n } catch (err) {\n if (err instanceof SSRDataMissingError) throw err;\n cell.error = err as Error;\n cell.pending = false;\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n // Do not call holder.set() here; this is still render.\n }\n }\n\n // Return the stable snapshot object owned by the cell\n return h.snapshot;\n}\n\nexport function on(\n target: EventTarget,\n event: string,\n handler: EventListener\n): void {\n const ownerIsRoot = getCurrentComponentInstance()?.isRoot ?? false;\n // Register the listener to be attached on mount. If the owner is not the\n // root app instance, fail loudly to prevent silent no-op behavior.\n registerMountOperation(() => {\n if (!ownerIsRoot) {\n throw new Error('[Askr] on() may only be used in root components');\n }\n target.addEventListener(event, handler);\n // Return cleanup function\n return () => {\n target.removeEventListener(event, handler);\n };\n });\n}\n\nexport function timer(intervalMs: number, fn: () => void): void {\n const ownerIsRoot = getCurrentComponentInstance()?.isRoot ?? false;\n // Register the timer to be started on mount. Fail loudly when used outside\n // of the root component to avoid silent no-ops.\n registerMountOperation(() => {\n if (!ownerIsRoot) {\n throw new Error('[Askr] timer() may only be used in root components');\n }\n const id = setInterval(fn, intervalMs);\n // Return cleanup function\n return () => {\n clearInterval(id);\n };\n });\n}\n\nexport function stream<T>(\n _source: unknown,\n _options?: Record<string, unknown>\n): { value: T | null; pending: boolean; error: Error | null } {\n // Stub implementation: no-op.\n return { value: null, pending: true, error: null };\n}\n\nexport function task(\n fn: () => void | (() => void) | Promise<void | (() => void)>\n): void {\n const ownerIsRoot = getCurrentComponentInstance()?.isRoot ?? false;\n // Register the task to run on mount. Fail loudly when used outside the root\n // component so callers get immediate feedback rather than silent no-op.\n registerMountOperation(async () => {\n if (!ownerIsRoot) {\n throw new Error('[Askr] task() may only be used in root components');\n }\n // Execute the task (may be async) and return its cleanup\n return await fn();\n });\n}\n\n/**\n * Capture the result of a synchronous expression at call time and return a\n * thunk that returns the captured value later. This is a low-level helper for\n * cases where async continuations need to observe a snapshot of values at the\n * moment scheduling occurred.\n *\n * Usage (public API):\n * const snapshot = capture(() => someState());\n * Promise.resolve().then(() => { use(snapshot()); });\n */\nexport function capture<T>(fn: () => T): () => T {\n const value = fn();\n return () => value;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"operations.js","names":[],"sources":["../../src/runtime/operations.ts"],"sourcesContent":["import {\n getCurrentComponentInstance,\n registerMountOperation,\n type ComponentInstance,\n} from './component';\nimport { getCurrentContextFrame } from './context';\nimport { ResourceCell } from './resource-cell';\nimport { state } from './state';\nimport { globalScheduler } from './scheduler';\nimport { getSSRBridge } from './ssr-bridge';\nimport { brandSnapshotSource } from './snapshot-source';\nimport { SSRDataMissingError } from '../common/ssr-errors';\n\nexport interface ResourceResult<T> {\n value: T | null;\n pending: boolean;\n error: Error | null;\n refresh(): void;\n}\n\n/**\n * Resource primitive — simple, deterministic async primitive\n * Usage: resource(fn, deps)\n * - fn receives { signal }\n * - captures execution context once at creation (synchronous step only)\n * - executes at most once per generation; stale async results are ignored\n * - refresh() cancels in-flight execution, increments generation and re-runs\n * - exposes { value, pending, error, refresh }\n * - during SSR, async results are disallowed and will throw synchronously\n */\nexport function resource<T>(\n fn: (opts: { signal: AbortSignal }) => Promise<T> | T,\n deps: unknown[] = []\n): ResourceResult<T> {\n const instance = getCurrentComponentInstance();\n // Create a non-null alias early so it can be used in nested closures\n // without TypeScript complaining about possible null access.\n const inst = instance as ComponentInstance;\n\n if (!instance) {\n const ssr = getSSRBridge();\n // If we're in a synchronous SSR render that has resolved data, use it.\n const renderData = ssr.getCurrentRenderData();\n if (renderData) {\n const key = ssr.getNextKey();\n if (!(key in renderData)) {\n ssr.throwSSRDataMissing();\n }\n const val = renderData[key] as T;\n return brandSnapshotSource({\n value: val,\n pending: false,\n error: null,\n refresh: () => {},\n }) as ResourceResult<T>;\n }\n\n // If we are in an SSR render pass without supplied data, throw for clarity.\n const ssrCtx = ssr.getCurrentSSRContext();\n if (ssrCtx) {\n ssr.throwSSRDataMissing();\n }\n\n // No active component instance and not in SSR render with data.\n // Autopilot invariant: resources must be created during render within an app.\n throw new Error(\n '[Askr] resource() must be called during component render inside an app. ' +\n 'Do not create resources at module scope or outside render.'\n );\n }\n\n // Internal ResourceCell — pure state machine now moved to its own module\n // to keep component wiring separate and ensure no component access here.\n // (See ./resource-cell.ts)\n\n // If we're in a synchronous SSR render that was supplied resolved data, use it\n const ssr = getSSRBridge();\n const renderData = ssr.getCurrentRenderData();\n if (renderData) {\n // Deterministic key generation: the collection step and render step use\n // the same incremental key generation to align resources.\n const key = ssr.getNextKey();\n if (!(key in renderData)) {\n ssr.throwSSRDataMissing();\n }\n\n // Commit synchronous value from render data and return a stable snapshot\n const val = renderData[key] as T;\n\n const holder = state<{\n cell?: ResourceCell<T>;\n snapshot: ResourceResult<T>;\n }>({\n cell: undefined,\n snapshot: brandSnapshotSource({\n value: val,\n pending: false,\n error: null,\n refresh: () => {},\n }) as ResourceResult<T>,\n });\n\n const h = holder();\n h.snapshot.value = val;\n h.snapshot.pending = false;\n h.snapshot.error = null;\n holder.set(h);\n return h.snapshot;\n }\n\n // Persist a holder so the snapshot identity is stable across renders.\n const holder = state<{ cell?: ResourceCell<T>; snapshot: ResourceResult<T> }>(\n {\n cell: undefined,\n snapshot: brandSnapshotSource({\n value: null,\n pending: true,\n error: null,\n refresh: () => {},\n }) as ResourceResult<T>,\n }\n );\n\n const h = holder();\n\n // Initialize cell on first call\n if (!h.cell) {\n const frame = getCurrentContextFrame();\n const cell = new ResourceCell<T>(fn, deps, frame);\n // Attach debug label (component name) for richer logs\n cell.ownerName = inst.fn?.name || '<anonymous>';\n h.cell = cell;\n h.snapshot = cell.snapshot as ResourceResult<T>;\n\n // Subscribe and schedule component updates when cell changes\n const unsubscribe = cell.subscribe(() => {\n const cur = holder();\n cur.snapshot.value = cell.snapshot.value;\n cur.snapshot.pending = cell.snapshot.pending;\n cur.snapshot.error = cell.snapshot.error;\n holder.set(cur);\n try {\n inst._enqueueRun?.();\n } catch {\n // ignore\n }\n });\n\n // Cleanup on unmount\n (inst.cleanupFns ??= []).push(() => {\n unsubscribe();\n cell.abort();\n });\n\n // Render invariant: do NOT start async work during render on the client.\n // SSR remains strict/synchronous and must throw immediately if async is encountered.\n if (inst.ssr) {\n // SSR: must run synchronously so missing data throws during render\n cell.start(true, false);\n if (!cell.pending) {\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n }\n } else {\n // Client: start after render via scheduler (never inline)\n globalScheduler.enqueue(() => {\n try {\n cell.start(false, false);\n } catch (err) {\n // Non-SSR: reflect synchronous errors into snapshot via manual update\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = (err as Error) ?? null;\n holder.set(cur);\n inst._enqueueRun?.();\n return;\n }\n\n // If the resource completed synchronously, subscribers were not notified.\n // Force a re-render so the component can observe the value.\n if (!cell.pending) {\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n holder.set(cur);\n inst._enqueueRun?.();\n }\n });\n }\n }\n\n const cell = h.cell!;\n cell.setLoader(fn);\n\n // Detect dependency changes and refresh immediately\n const depsChanged =\n !cell.deps ||\n cell.deps.length !== deps.length ||\n cell.deps.some((d, i) => d !== deps[i]);\n\n if (depsChanged) {\n cell.deps = deps.slice();\n cell.generation++;\n cell.pending = true;\n cell.error = null;\n try {\n if (inst.ssr) {\n cell.start(true, false);\n if (!cell.pending) {\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n }\n } else {\n globalScheduler.enqueue(() => {\n cell.start(false, false);\n if (!cell.pending) {\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n holder.set(cur);\n inst._enqueueRun?.();\n }\n });\n }\n } catch (err) {\n if (err instanceof SSRDataMissingError) throw err;\n cell.error = err as Error;\n cell.pending = false;\n const cur = holder();\n cur.snapshot.value = cell.value;\n cur.snapshot.pending = cell.pending;\n cur.snapshot.error = cell.error;\n // Do not call holder.set() here; this is still render.\n }\n }\n\n // Return the stable snapshot object owned by the cell\n return h.snapshot;\n}\n\nexport function on(\n target: EventTarget,\n event: string,\n handler: EventListener\n): void {\n const ownerIsRoot = getCurrentComponentInstance()?.isRoot ?? false;\n // Register the listener to be attached on mount. If the owner is not the\n // root app instance, fail loudly to prevent silent no-op behavior.\n registerMountOperation(() => {\n if (!ownerIsRoot) {\n throw new Error('[Askr] on() may only be used in root components');\n }\n target.addEventListener(event, handler);\n // Return cleanup function\n return () => {\n target.removeEventListener(event, handler);\n };\n });\n}\n\nexport function timer(intervalMs: number, fn: () => void): void {\n const ownerIsRoot = getCurrentComponentInstance()?.isRoot ?? false;\n // Register the timer to be started on mount. Fail loudly when used outside\n // of the root component to avoid silent no-ops.\n registerMountOperation(() => {\n if (!ownerIsRoot) {\n throw new Error('[Askr] timer() may only be used in root components');\n }\n const id = setInterval(fn, intervalMs);\n // Return cleanup function\n return () => {\n clearInterval(id);\n };\n });\n}\n\nexport function stream<T>(\n _source: unknown,\n _options?: Record<string, unknown>\n): { value: T | null; pending: boolean; error: Error | null } {\n // Stub implementation: no-op.\n return { value: null, pending: true, error: null };\n}\n\nexport function task(\n fn: () => void | (() => void) | Promise<void | (() => void)>\n): void {\n const ownerIsRoot = getCurrentComponentInstance()?.isRoot ?? false;\n // Register the task to run on mount. Fail loudly when used outside the root\n // component so callers get immediate feedback rather than silent no-op.\n registerMountOperation(async () => {\n if (!ownerIsRoot) {\n throw new Error('[Askr] task() may only be used in root components');\n }\n // Execute the task (may be async) and return its cleanup\n return await fn();\n });\n}\n\n/**\n * Capture the result of a synchronous expression at call time and return a\n * thunk that returns the captured value later. This is a low-level helper for\n * cases where async continuations need to observe a snapshot of values at the\n * moment scheduling occurred.\n *\n * Usage (public API):\n * const snapshot = capture(() => someState());\n * Promise.resolve().then(() => { use(snapshot()); });\n */\nexport function capture<T>(fn: () => T): () => T {\n const value = fn();\n return () => value;\n}\n"],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { isDevelopmentEnvironment, isRuntimeEnvFlagEnabled } from "../common/env.js";
|
|
2
2
|
//#region src/runtime/perf-metrics.ts
|
|
3
3
|
const BENCH_BUILD_ENABLED = isRuntimeEnvFlagEnabled("ASKR_BENCH");
|
|
4
|
+
let cachedPerfStore;
|
|
4
5
|
function createInitialPerfMetrics() {
|
|
5
6
|
return {
|
|
6
7
|
selectorInvalidations: 0,
|
|
@@ -31,15 +32,20 @@ function shouldCollectPerfMetrics() {
|
|
|
31
32
|
}
|
|
32
33
|
}
|
|
33
34
|
function getPerfStore() {
|
|
35
|
+
if (cachedPerfStore) return cachedPerfStore;
|
|
34
36
|
if (!shouldCollectPerfMetrics()) return null;
|
|
35
37
|
try {
|
|
36
38
|
const g = globalThis;
|
|
37
39
|
if (!g.__ASKR_PERF__) g.__ASKR_PERF__ = createInitialPerfMetrics();
|
|
38
|
-
|
|
40
|
+
cachedPerfStore = g.__ASKR_PERF__;
|
|
41
|
+
return cachedPerfStore;
|
|
39
42
|
} catch {
|
|
40
43
|
return null;
|
|
41
44
|
}
|
|
42
45
|
}
|
|
46
|
+
function getPerfMetricsStore() {
|
|
47
|
+
return getPerfStore();
|
|
48
|
+
}
|
|
43
49
|
function incrementPerfMetric(key, delta = 1) {
|
|
44
50
|
const store = getPerfStore();
|
|
45
51
|
if (!store) return;
|
|
@@ -59,6 +65,6 @@ function recordSchedulerFlushTaskCount(taskCount) {
|
|
|
59
65
|
store.schedulerTaskExecutions += taskCount;
|
|
60
66
|
}
|
|
61
67
|
//#endregion
|
|
62
|
-
export { addPerfDuration, incrementPerfMetric, recordSchedulerFlushTaskCount };
|
|
68
|
+
export { addPerfDuration, getPerfMetricsStore, incrementPerfMetric, recordSchedulerFlushTaskCount };
|
|
63
69
|
|
|
64
70
|
//# sourceMappingURL=perf-metrics.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"perf-metrics.js","names":[],"sources":["../../src/runtime/perf-metrics.ts"],"sourcesContent":["import {\n isDevelopmentEnvironment,\n isRuntimeEnvFlagEnabled,\n} from '../common/env';\n\ntype PerfMetrics = {\n selectorInvalidations: number;\n selectorCandidateReads: number;\n reactivePropReevaluations: number;\n skippedDomPropWrites: number;\n classListPatchOps: number;\n delegatedAncestorHops: number;\n hydrationBoundaryActivations: number;\n ssrTagCacheHits: number;\n lastSchedulerTaskCountPerFlush: number;\n maxSchedulerTaskCountPerFlush: number;\n schedulerFlushCount: number;\n schedulerTaskExecutions: number;\n ssgWorkerCount: number;\n ssgRenderTimeMs: number;\n ssgWorkerRenderTimeMs: number;\n ssgWriteTimeMs: number;\n};\n\ntype PerfMetricsKey = keyof PerfMetrics;\n\ntype AskrPerfGlobal = typeof globalThis & {\n __ASKR_BENCH__?: boolean;\n __ASKR_PERF__?: PerfMetrics;\n};\n\nconst BENCH_BUILD_ENABLED = isRuntimeEnvFlagEnabled('ASKR_BENCH');\n\nfunction createInitialPerfMetrics(): PerfMetrics {\n return {\n selectorInvalidations: 0,\n selectorCandidateReads: 0,\n reactivePropReevaluations: 0,\n skippedDomPropWrites: 0,\n classListPatchOps: 0,\n delegatedAncestorHops: 0,\n hydrationBoundaryActivations: 0,\n ssrTagCacheHits: 0,\n lastSchedulerTaskCountPerFlush: 0,\n maxSchedulerTaskCountPerFlush: 0,\n schedulerFlushCount: 0,\n schedulerTaskExecutions: 0,\n ssgWorkerCount: 0,\n ssgRenderTimeMs: 0,\n ssgWorkerRenderTimeMs: 0,\n ssgWriteTimeMs: 0,\n };\n}\n\nfunction shouldCollectPerfMetrics(): boolean {\n if (isDevelopmentEnvironment()) {\n return true;\n }\n if (!BENCH_BUILD_ENABLED) {\n return false;\n }\n try {\n return !!(globalThis as AskrPerfGlobal).__ASKR_BENCH__;\n } catch {\n return false;\n }\n}\n\nfunction getPerfStore(): PerfMetrics | null {\n if (!shouldCollectPerfMetrics()) {\n return null;\n }\n\n try {\n const g = globalThis as AskrPerfGlobal;\n if (!g.__ASKR_PERF__) {\n g.__ASKR_PERF__ = createInitialPerfMetrics();\n }\n
|
|
1
|
+
{"version":3,"file":"perf-metrics.js","names":[],"sources":["../../src/runtime/perf-metrics.ts"],"sourcesContent":["import {\n isDevelopmentEnvironment,\n isRuntimeEnvFlagEnabled,\n} from '../common/env';\n\ntype PerfMetrics = {\n selectorInvalidations: number;\n selectorCandidateReads: number;\n reactivePropReevaluations: number;\n skippedDomPropWrites: number;\n classListPatchOps: number;\n delegatedAncestorHops: number;\n hydrationBoundaryActivations: number;\n ssrTagCacheHits: number;\n lastSchedulerTaskCountPerFlush: number;\n maxSchedulerTaskCountPerFlush: number;\n schedulerFlushCount: number;\n schedulerTaskExecutions: number;\n ssgWorkerCount: number;\n ssgRenderTimeMs: number;\n ssgWorkerRenderTimeMs: number;\n ssgWriteTimeMs: number;\n};\n\ntype PerfMetricsKey = keyof PerfMetrics;\n\ntype AskrPerfGlobal = typeof globalThis & {\n __ASKR_BENCH__?: boolean;\n __ASKR_PERF__?: PerfMetrics;\n};\n\nconst BENCH_BUILD_ENABLED = isRuntimeEnvFlagEnabled('ASKR_BENCH');\nlet cachedPerfStore: PerfMetrics | undefined;\n\nfunction createInitialPerfMetrics(): PerfMetrics {\n return {\n selectorInvalidations: 0,\n selectorCandidateReads: 0,\n reactivePropReevaluations: 0,\n skippedDomPropWrites: 0,\n classListPatchOps: 0,\n delegatedAncestorHops: 0,\n hydrationBoundaryActivations: 0,\n ssrTagCacheHits: 0,\n lastSchedulerTaskCountPerFlush: 0,\n maxSchedulerTaskCountPerFlush: 0,\n schedulerFlushCount: 0,\n schedulerTaskExecutions: 0,\n ssgWorkerCount: 0,\n ssgRenderTimeMs: 0,\n ssgWorkerRenderTimeMs: 0,\n ssgWriteTimeMs: 0,\n };\n}\n\nfunction shouldCollectPerfMetrics(): boolean {\n if (isDevelopmentEnvironment()) {\n return true;\n }\n if (!BENCH_BUILD_ENABLED) {\n return false;\n }\n try {\n return !!(globalThis as AskrPerfGlobal).__ASKR_BENCH__;\n } catch {\n return false;\n }\n}\n\nfunction getPerfStore(): PerfMetrics | null {\n if (cachedPerfStore) {\n return cachedPerfStore;\n }\n\n if (!shouldCollectPerfMetrics()) {\n return null;\n }\n\n try {\n const g = globalThis as AskrPerfGlobal;\n if (!g.__ASKR_PERF__) {\n g.__ASKR_PERF__ = createInitialPerfMetrics();\n }\n cachedPerfStore = g.__ASKR_PERF__;\n return cachedPerfStore;\n } catch {\n return null;\n }\n}\n\nexport function getPerfMetricsStore(): PerfMetrics | null {\n return getPerfStore();\n}\n\nexport function incrementPerfMetric(\n key:\n | 'selectorInvalidations'\n | 'selectorCandidateReads'\n | 'reactivePropReevaluations'\n | 'skippedDomPropWrites'\n | 'classListPatchOps'\n | 'delegatedAncestorHops'\n | 'hydrationBoundaryActivations'\n | 'ssrTagCacheHits'\n | 'ssgWorkerCount',\n delta = 1\n): void {\n const store = getPerfStore();\n if (!store) return;\n store[key] += delta;\n}\n\nexport function addPerfDuration(\n key: 'ssgRenderTimeMs' | 'ssgWorkerRenderTimeMs' | 'ssgWriteTimeMs',\n deltaMs: number\n): void {\n const store = getPerfStore();\n if (!store) return;\n store[key] += deltaMs;\n}\n\nexport function recordSchedulerFlushTaskCount(taskCount: number): void {\n const store = getPerfStore();\n if (!store) return;\n store.lastSchedulerTaskCountPerFlush = taskCount;\n store.maxSchedulerTaskCountPerFlush = Math.max(\n store.maxSchedulerTaskCountPerFlush,\n taskCount\n );\n store.schedulerFlushCount += 1;\n store.schedulerTaskExecutions += taskCount;\n}\n\nexport function getPerfMetrics(): Readonly<PerfMetrics> | undefined {\n const store = getPerfStore();\n return store ? { ...store } : undefined;\n}\n\nexport function resetPerfMetrics(): void {\n const store = getPerfStore();\n if (!store) return;\n const next = createInitialPerfMetrics();\n (Object.keys(next) as PerfMetricsKey[]).forEach((key) => {\n store[key] = next[key];\n });\n}\n"],"mappings":";;AA+BA,MAAM,sBAAsB,wBAAwB,aAAa;AACjE,IAAI;AAEJ,SAAS,2BAAwC;CAC/C,OAAO;EACL,uBAAuB;EACvB,wBAAwB;EACxB,2BAA2B;EAC3B,sBAAsB;EACtB,mBAAmB;EACnB,uBAAuB;EACvB,8BAA8B;EAC9B,iBAAiB;EACjB,gCAAgC;EAChC,+BAA+B;EAC/B,qBAAqB;EACrB,yBAAyB;EACzB,gBAAgB;EAChB,iBAAiB;EACjB,uBAAuB;EACvB,gBAAgB;EACjB;;AAGH,SAAS,2BAAoC;CAC3C,IAAI,0BAA0B,EAC5B,OAAO;CAET,IAAI,CAAC,qBACH,OAAO;CAET,IAAI;EACF,OAAO,CAAC,CAAE,WAA8B;SAClC;EACN,OAAO;;;AAIX,SAAS,eAAmC;CAC1C,IAAI,iBACF,OAAO;CAGT,IAAI,CAAC,0BAA0B,EAC7B,OAAO;CAGT,IAAI;EACF,MAAM,IAAI;EACV,IAAI,CAAC,EAAE,eACL,EAAE,gBAAgB,0BAA0B;EAE9C,kBAAkB,EAAE;EACpB,OAAO;SACD;EACN,OAAO;;;AAIX,SAAgB,sBAA0C;CACxD,OAAO,cAAc;;AAGvB,SAAgB,oBACd,KAUA,QAAQ,GACF;CACN,MAAM,QAAQ,cAAc;CAC5B,IAAI,CAAC,OAAO;CACZ,MAAM,QAAQ;;AAGhB,SAAgB,gBACd,KACA,SACM;CACN,MAAM,QAAQ,cAAc;CAC5B,IAAI,CAAC,OAAO;CACZ,MAAM,QAAQ;;AAGhB,SAAgB,8BAA8B,WAAyB;CACrE,MAAM,QAAQ,cAAc;CAC5B,IAAI,CAAC,OAAO;CACZ,MAAM,iCAAiC;CACvC,MAAM,gCAAgC,KAAK,IACzC,MAAM,+BACN,UACD;CACD,MAAM,uBAAuB;CAC7B,MAAM,2BAA2B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"readable.js","names":[],"sources":["../../src/runtime/readable.ts"],"sourcesContent":["import { globalScheduler } from './scheduler';\nimport { getCurrentInstance, type ComponentInstance } from './component';\n\nexport interface DerivedSubscriber {\n _markDirty(): void;\n _pendingDependencySources?: Set<ReadableSource<unknown>>;\n}\n\nexport interface ReadableSource<T = unknown> {\n (): T;\n _hasBeenRead?: boolean;\n _readers?: Map<ComponentInstance, number>;\n _derivedSubscribers?: Set<DerivedSubscriber>;\n}\n\nexport function markReadableUsage(source: unknown): void {\n if (\n source &&\n typeof source === 'function' &&\n ('_hasBeenRead' in source || '_readers' in source)\n ) {\n (source as ReadableSource<unknown>)._hasBeenRead = true;\n }\n}\n\ntype RendererBridge = {\n markReactivePropsDirtySource?: (source: ReadableSource<unknown>) => void;\n};\n\nlet currentDerivedSubscriber: DerivedSubscriber | null = null;\nlet suppressComponentReadTrackingDepth = 0;\nlet currentFineGrainedReadSources: Set<ReadableSource<unknown>> | null = null;\n\nexport function recordReadableRead(source: ReadableSource<unknown>): void {\n if (currentFineGrainedReadSources) {\n currentFineGrainedReadSources.add(source);\n return;\n }\n\n if (currentDerivedSubscriber) {\n if (!currentDerivedSubscriber._pendingDependencySources) {\n currentDerivedSubscriber._pendingDependencySources = new Set();\n }\n currentDerivedSubscriber._pendingDependencySources.add(source);\n }\n\n if (suppressComponentReadTrackingDepth > 0) {\n return;\n }\n\n const inst = getCurrentInstance();\n if (!inst || inst._currentRenderToken === undefined) {\n return;\n }\n\n if (!inst._pendingReadSources) {\n inst._pendingReadSources = new Set();\n }\n inst._pendingReadSources.add(source);\n}\n\nexport function withFineGrainedReadTracking<T>(\n pendingSources: Set<ReadableSource<unknown>>,\n fn: () => T\n): T {\n const previousPendingSources = currentFineGrainedReadSources;\n currentFineGrainedReadSources = pendingSources;\n\n try {\n return fn();\n } finally {\n currentFineGrainedReadSources = previousPendingSources;\n }\n}\n\nexport function finalizeReadableSubscriptions(\n instance: ComponentInstance\n): void {\n const newSet = instance._pendingReadSources;\n const oldSet = instance._lastReadSources;\n const token = instance._currentRenderToken;\n\n if (token === undefined) {\n return;\n }\n\n if (oldSet) {\n for (const source of oldSet) {\n if (!newSet?.has(source)) {\n source._readers?.delete(instance);\n }\n }\n }\n\n instance.lastRenderToken = token;\n\n if (newSet) {\n for (const source of newSet) {\n let readers = source._readers;\n if (!readers) {\n readers = new Map();\n source._readers = readers;\n }\n readers.set(instance, instance.lastRenderToken ?? 0);\n }\n }\n\n instance._lastReadSources = newSet ?? new Set();\n instance._pendingReadSources = undefined;\n instance._currentRenderToken = undefined;\n}\n\nexport function cleanupReadableSubscriptions(\n instance: ComponentInstance\n): void {\n const sources = instance._lastReadSources;\n if (!sources || sources.size === 0) {\n return;\n }\n\n for (const source of sources) {\n source._readers?.delete(instance);\n }\n instance._lastReadSources = new Set();\n}\n\nexport function withDerivedReadTracking<T>(\n subscriber: DerivedSubscriber,\n fn: () => T\n): T {\n const prevDerivedSubscriber = currentDerivedSubscriber;\n currentDerivedSubscriber = subscriber;\n suppressComponentReadTrackingDepth += 1;\n\n try {\n return fn();\n } finally {\n suppressComponentReadTrackingDepth -= 1;\n currentDerivedSubscriber = prevDerivedSubscriber;\n }\n}\n\nexport function syncDerivedDependencySubscriptions(\n subscriber: DerivedSubscriber,\n prevSources: Set<ReadableSource<unknown>>,\n nextSources: Set<ReadableSource<unknown>>\n): void {\n for (const source of prevSources) {\n if (!nextSources.has(source)) {\n source._derivedSubscribers?.delete(subscriber);\n }\n }\n\n for (const source of nextSources) {\n let subscribers = source._derivedSubscribers;\n if (!subscribers) {\n subscribers = new Set();\n source._derivedSubscribers = subscribers;\n }\n subscribers.add(subscriber);\n }\n}\n\nexport function clearDerivedDependencySubscriptions(\n subscriber: DerivedSubscriber,\n sources: Set<ReadableSource<unknown>>\n): void {\n for (const source of sources) {\n source._derivedSubscribers?.delete(subscriber);\n }\n sources.clear();\n}\n\nexport function markReadableDerivedSubscribersDirty(\n source: ReadableSource<unknown>\n): void {\n const subscribers = source._derivedSubscribers;\n if (!subscribers || subscribers.size === 0) {\n return;\n }\n\n for (const subscriber of subscribers) {\n subscriber._markDirty();\n }\n}\n\nexport function markReactivePropsDirtySource(\n source: ReadableSource<unknown>\n): void {\n try {\n (\n globalThis as {\n __ASKR_RENDERER?: RendererBridge;\n }\n ).__ASKR_RENDERER?.markReactivePropsDirtySource?.(source);\n } catch {\n // Keep readable notifications side-effect safe.\n }\n}\n\nexport function notifyReadableReaders(\n source: ReadableSource<unknown>,\n skipInstance?: ComponentInstance | null\n): boolean {\n const readers = source._readers;\n let didScheduleUpdate = false;\n\n if (!readers || readers.size === 0) {\n return false;\n }\n\n for (const [instance, token] of readers) {\n if (skipInstance && instance === skipInstance) {\n continue;\n }\n if (instance.lastRenderToken !== token) {\n continue;\n }\n if (instance.hasPendingUpdate) {\n continue;\n }\n\n instance.hasPendingUpdate = true;\n const task = instance._pendingFlushTask;\n if (task) {\n globalScheduler.enqueue(task);\n } else {\n globalScheduler.enqueue(() => {\n instance.hasPendingUpdate = false;\n instance.notifyUpdate?.();\n });\n }\n didScheduleUpdate = true;\n }\n\n return didScheduleUpdate;\n}\n"],"mappings":";;;AAeA,SAAgB,kBAAkB,QAAuB;
|
|
1
|
+
{"version":3,"file":"readable.js","names":[],"sources":["../../src/runtime/readable.ts"],"sourcesContent":["import { globalScheduler } from './scheduler';\nimport { getCurrentInstance, type ComponentInstance } from './component';\n\nexport interface DerivedSubscriber {\n _markDirty(): void;\n _pendingDependencySources?: Set<ReadableSource<unknown>>;\n}\n\nexport interface ReadableSource<T = unknown> {\n (): T;\n _hasBeenRead?: boolean;\n _readers?: Map<ComponentInstance, number>;\n _derivedSubscribers?: Set<DerivedSubscriber>;\n}\n\nexport function markReadableUsage(source: unknown): void {\n if (\n source &&\n typeof source === 'function' &&\n ('_hasBeenRead' in source || '_readers' in source)\n ) {\n (source as ReadableSource<unknown>)._hasBeenRead = true;\n }\n}\n\ntype RendererBridge = {\n markReactivePropsDirtySource?: (source: ReadableSource<unknown>) => void;\n};\n\nlet currentDerivedSubscriber: DerivedSubscriber | null = null;\nlet suppressComponentReadTrackingDepth = 0;\nlet currentFineGrainedReadSources: Set<ReadableSource<unknown>> | null = null;\n\nexport function recordReadableRead(source: ReadableSource<unknown>): void {\n if (currentFineGrainedReadSources) {\n currentFineGrainedReadSources.add(source);\n return;\n }\n\n if (currentDerivedSubscriber) {\n if (!currentDerivedSubscriber._pendingDependencySources) {\n currentDerivedSubscriber._pendingDependencySources = new Set();\n }\n currentDerivedSubscriber._pendingDependencySources.add(source);\n }\n\n if (suppressComponentReadTrackingDepth > 0) {\n return;\n }\n\n const inst = getCurrentInstance();\n if (!inst || inst._currentRenderToken === undefined) {\n return;\n }\n\n if (!inst._pendingReadSources) {\n inst._pendingReadSources = new Set();\n }\n inst._pendingReadSources.add(source);\n}\n\nexport function withFineGrainedReadTracking<T>(\n pendingSources: Set<ReadableSource<unknown>>,\n fn: () => T\n): T {\n const previousPendingSources = currentFineGrainedReadSources;\n currentFineGrainedReadSources = pendingSources;\n\n try {\n return fn();\n } finally {\n currentFineGrainedReadSources = previousPendingSources;\n }\n}\n\nexport function finalizeReadableSubscriptions(\n instance: ComponentInstance\n): void {\n const newSet = instance._pendingReadSources;\n const oldSet = instance._lastReadSources;\n const token = instance._currentRenderToken;\n\n if (token === undefined) {\n return;\n }\n\n if (oldSet) {\n for (const source of oldSet) {\n if (!newSet?.has(source)) {\n source._readers?.delete(instance);\n }\n }\n }\n\n instance.lastRenderToken = token;\n\n if (newSet) {\n for (const source of newSet) {\n let readers = source._readers;\n if (!readers) {\n readers = new Map();\n source._readers = readers;\n }\n readers.set(instance, instance.lastRenderToken ?? 0);\n }\n }\n\n instance._lastReadSources = newSet ?? new Set();\n instance._pendingReadSources = undefined;\n instance._currentRenderToken = undefined;\n}\n\nexport function cleanupReadableSubscriptions(\n instance: ComponentInstance\n): void {\n const sources = instance._lastReadSources;\n if (!sources || sources.size === 0) {\n return;\n }\n\n for (const source of sources) {\n source._readers?.delete(instance);\n }\n instance._lastReadSources = new Set();\n}\n\nexport function withDerivedReadTracking<T>(\n subscriber: DerivedSubscriber,\n fn: () => T\n): T {\n const prevDerivedSubscriber = currentDerivedSubscriber;\n currentDerivedSubscriber = subscriber;\n suppressComponentReadTrackingDepth += 1;\n\n try {\n return fn();\n } finally {\n suppressComponentReadTrackingDepth -= 1;\n currentDerivedSubscriber = prevDerivedSubscriber;\n }\n}\n\nexport function syncDerivedDependencySubscriptions(\n subscriber: DerivedSubscriber,\n prevSources: Set<ReadableSource<unknown>>,\n nextSources: Set<ReadableSource<unknown>>\n): void {\n for (const source of prevSources) {\n if (!nextSources.has(source)) {\n source._derivedSubscribers?.delete(subscriber);\n }\n }\n\n for (const source of nextSources) {\n let subscribers = source._derivedSubscribers;\n if (!subscribers) {\n subscribers = new Set();\n source._derivedSubscribers = subscribers;\n }\n subscribers.add(subscriber);\n }\n}\n\nexport function clearDerivedDependencySubscriptions(\n subscriber: DerivedSubscriber,\n sources: Set<ReadableSource<unknown>>\n): void {\n for (const source of sources) {\n source._derivedSubscribers?.delete(subscriber);\n }\n sources.clear();\n}\n\nexport function markReadableDerivedSubscribersDirty(\n source: ReadableSource<unknown>\n): void {\n const subscribers = source._derivedSubscribers;\n if (!subscribers || subscribers.size === 0) {\n return;\n }\n\n for (const subscriber of subscribers) {\n subscriber._markDirty();\n }\n}\n\nexport function markReactivePropsDirtySource(\n source: ReadableSource<unknown>\n): void {\n try {\n (\n globalThis as {\n __ASKR_RENDERER?: RendererBridge;\n }\n ).__ASKR_RENDERER?.markReactivePropsDirtySource?.(source);\n } catch {\n // Keep readable notifications side-effect safe.\n }\n}\n\nexport function notifyReadableReaders(\n source: ReadableSource<unknown>,\n skipInstance?: ComponentInstance | null\n): boolean {\n const readers = source._readers;\n let didScheduleUpdate = false;\n\n if (!readers || readers.size === 0) {\n return false;\n }\n\n for (const [instance, token] of readers) {\n if (skipInstance && instance === skipInstance) {\n continue;\n }\n if (instance.lastRenderToken !== token) {\n continue;\n }\n if (instance.hasPendingUpdate) {\n continue;\n }\n\n instance.hasPendingUpdate = true;\n const task = instance._pendingFlushTask;\n if (task) {\n globalScheduler.enqueue(task);\n } else {\n globalScheduler.enqueue(() => {\n instance.hasPendingUpdate = false;\n instance.notifyUpdate?.();\n });\n }\n didScheduleUpdate = true;\n }\n\n return didScheduleUpdate;\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { logger } from "../dev/logger.js";
|
|
2
2
|
import { withAsyncResourceContext } from "./context.js";
|
|
3
|
+
import { brandSnapshotSource } from "./snapshot-source.js";
|
|
3
4
|
import { getSSRBridge } from "./ssr-bridge.js";
|
|
4
5
|
//#region src/runtime/resource-cell.ts
|
|
5
6
|
/**
|
|
@@ -22,12 +23,12 @@ var ResourceCell = class {
|
|
|
22
23
|
this.fn = fn;
|
|
23
24
|
this.deps = deps ? deps.slice() : null;
|
|
24
25
|
this.resourceFrame = resourceFrame;
|
|
25
|
-
this.snapshot = {
|
|
26
|
+
this.snapshot = brandSnapshotSource({
|
|
26
27
|
value: null,
|
|
27
28
|
pending: true,
|
|
28
29
|
error: null,
|
|
29
30
|
refresh: () => this.refresh()
|
|
30
|
-
};
|
|
31
|
+
});
|
|
31
32
|
}
|
|
32
33
|
setLoader(fn) {
|
|
33
34
|
this.fn = fn;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resource-cell.js","names":[],"sources":["../../src/runtime/resource-cell.ts"],"sourcesContent":["import { withAsyncResourceContext, type ContextFrame } from './context';\nimport { logger } from '../dev/logger';\nimport { getSSRBridge } from './ssr-bridge';\n\n/**\n * Pure, component-agnostic ResourceCell state machine.\n * - Holds value/pending/error/generation/controller\n * - Exposes a stable `snapshot` object: { value, pending, error, refresh }\n * - Uses `withAsyncResourceContext` to bind the synchronous execution step\n * to a captured frame. Continuations after await do not see the frame.\n */\nexport class ResourceCell<U> {\n value: U | null = null;\n pending = true;\n error: Error | null = null;\n generation = 0;\n controller: AbortController | null = null;\n deps: unknown[] | null = null;\n resourceFrame: ContextFrame | null = null;\n\n // Optional debug label set by caller (component name) to improve logs\n ownerName?: string;\n\n private subscribers = new Set<() => void>();\n\n readonly snapshot: {\n value: U | null;\n pending: boolean;\n error: Error | null;\n refresh: () => void;\n };\n\n private fn: (opts: { signal: AbortSignal }) => Promise<U> | U;\n\n constructor(\n fn: (opts: { signal: AbortSignal }) => Promise<U> | U,\n deps: unknown[] | null,\n resourceFrame: ContextFrame | null\n ) {\n this.fn = fn;\n this.deps = deps ? deps.slice() : null;\n this.resourceFrame = resourceFrame;\n this.snapshot = {\n value: null,\n pending: true,\n error: null,\n refresh: () => this.refresh(),\n };\n }\n\n setLoader(fn: (opts: { signal: AbortSignal }) => Promise<U> | U): void {\n this.fn = fn;\n }\n\n subscribe(cb: () => void): () => void {\n this.subscribers.add(cb);\n return () => this.subscribers.delete(cb);\n }\n\n private notifySubscribers() {\n this.snapshot.value = this.value;\n this.snapshot.pending = this.pending;\n this.snapshot.error = this.error;\n for (const cb of this.subscribers) cb();\n }\n\n start(ssr = false, notify = true) {\n const generation = this.generation;\n\n this.controller?.abort();\n const controller = new AbortController();\n this.controller = controller;\n this.pending = true;\n this.error = null;\n if (notify) this.notifySubscribers();\n\n let result: Promise<U> | U;\n try {\n // Execute only the synchronous step inside the frozen resource frame.\n result = withAsyncResourceContext(this.resourceFrame, () =>\n this.fn({ signal: controller.signal })\n );\n } catch (err) {\n this.pending = false;\n this.error = err as Error;\n if (notify) this.notifySubscribers();\n return;\n }\n\n if (!(result instanceof Promise)) {\n this.value = result as U;\n this.pending = false;\n this.error = null;\n if (notify) this.notifySubscribers();\n return;\n }\n\n if (ssr) {\n // During SSR async results are disallowed\n getSSRBridge().throwSSRDataMissing();\n }\n\n (result as Promise<U>)\n .then((val) => {\n if (this.generation !== generation) return;\n if (this.controller !== controller) return;\n this.value = val;\n this.pending = false;\n this.error = null;\n this.notifySubscribers();\n })\n .catch((err) => {\n if (this.generation !== generation) return;\n if (this.controller !== controller) return;\n\n const isAbortError =\n controller.signal.aborted ||\n (err instanceof Error && err.name === 'AbortError') ||\n (typeof DOMException !== 'undefined' &&\n err instanceof DOMException &&\n err.name === 'AbortError');\n\n if (isAbortError) {\n return;\n }\n\n this.pending = false;\n this.error = err as Error;\n try {\n if (this.ownerName) {\n logger.error(\n `[Askr] Async resource error in ${this.ownerName}:`,\n err\n );\n } else {\n logger.error('[Askr] Async resource error:', err);\n }\n } catch {\n /* ignore logging errors */\n }\n this.notifySubscribers();\n });\n }\n\n refresh() {\n this.generation++;\n this.controller?.abort();\n this.start();\n }\n\n abort() {\n this.controller?.abort();\n }\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"resource-cell.js","names":[],"sources":["../../src/runtime/resource-cell.ts"],"sourcesContent":["import { withAsyncResourceContext, type ContextFrame } from './context';\nimport { logger } from '../dev/logger';\nimport { getSSRBridge } from './ssr-bridge';\nimport {\n brandSnapshotSource,\n type SnapshotSourceBrand,\n} from './snapshot-source';\n\n/**\n * Pure, component-agnostic ResourceCell state machine.\n * - Holds value/pending/error/generation/controller\n * - Exposes a stable `snapshot` object: { value, pending, error, refresh }\n * - Uses `withAsyncResourceContext` to bind the synchronous execution step\n * to a captured frame. Continuations after await do not see the frame.\n */\nexport class ResourceCell<U> {\n value: U | null = null;\n pending = true;\n error: Error | null = null;\n generation = 0;\n controller: AbortController | null = null;\n deps: unknown[] | null = null;\n resourceFrame: ContextFrame | null = null;\n\n // Optional debug label set by caller (component name) to improve logs\n ownerName?: string;\n\n private subscribers = new Set<() => void>();\n\n readonly snapshot: {\n value: U | null;\n pending: boolean;\n error: Error | null;\n refresh: () => void;\n } & SnapshotSourceBrand;\n\n private fn: (opts: { signal: AbortSignal }) => Promise<U> | U;\n\n constructor(\n fn: (opts: { signal: AbortSignal }) => Promise<U> | U,\n deps: unknown[] | null,\n resourceFrame: ContextFrame | null\n ) {\n this.fn = fn;\n this.deps = deps ? deps.slice() : null;\n this.resourceFrame = resourceFrame;\n this.snapshot = brandSnapshotSource({\n value: null,\n pending: true,\n error: null,\n refresh: () => this.refresh(),\n });\n }\n\n setLoader(fn: (opts: { signal: AbortSignal }) => Promise<U> | U): void {\n this.fn = fn;\n }\n\n subscribe(cb: () => void): () => void {\n this.subscribers.add(cb);\n return () => this.subscribers.delete(cb);\n }\n\n private notifySubscribers() {\n this.snapshot.value = this.value;\n this.snapshot.pending = this.pending;\n this.snapshot.error = this.error;\n for (const cb of this.subscribers) cb();\n }\n\n start(ssr = false, notify = true) {\n const generation = this.generation;\n\n this.controller?.abort();\n const controller = new AbortController();\n this.controller = controller;\n this.pending = true;\n this.error = null;\n if (notify) this.notifySubscribers();\n\n let result: Promise<U> | U;\n try {\n // Execute only the synchronous step inside the frozen resource frame.\n result = withAsyncResourceContext(this.resourceFrame, () =>\n this.fn({ signal: controller.signal })\n );\n } catch (err) {\n this.pending = false;\n this.error = err as Error;\n if (notify) this.notifySubscribers();\n return;\n }\n\n if (!(result instanceof Promise)) {\n this.value = result as U;\n this.pending = false;\n this.error = null;\n if (notify) this.notifySubscribers();\n return;\n }\n\n if (ssr) {\n // During SSR async results are disallowed\n getSSRBridge().throwSSRDataMissing();\n }\n\n (result as Promise<U>)\n .then((val) => {\n if (this.generation !== generation) return;\n if (this.controller !== controller) return;\n this.value = val;\n this.pending = false;\n this.error = null;\n this.notifySubscribers();\n })\n .catch((err) => {\n if (this.generation !== generation) return;\n if (this.controller !== controller) return;\n\n const isAbortError =\n controller.signal.aborted ||\n (err instanceof Error && err.name === 'AbortError') ||\n (typeof DOMException !== 'undefined' &&\n err instanceof DOMException &&\n err.name === 'AbortError');\n\n if (isAbortError) {\n return;\n }\n\n this.pending = false;\n this.error = err as Error;\n try {\n if (this.ownerName) {\n logger.error(\n `[Askr] Async resource error in ${this.ownerName}:`,\n err\n );\n } else {\n logger.error('[Askr] Async resource error:', err);\n }\n } catch {\n /* ignore logging errors */\n }\n this.notifySubscribers();\n });\n }\n\n refresh() {\n this.generation++;\n this.controller?.abort();\n this.start();\n }\n\n abort() {\n this.controller?.abort();\n }\n}\n"],"mappings":";;;;;;;;;;;;AAeA,IAAa,eAAb,MAA6B;CAuB3B,YACE,IACA,MACA,eACA;eA1BgB;iBACR;eACY;oBACT;oBACwB;cACZ;uBACY;qCAKf,IAAI,KAAiB;EAgBzC,KAAK,KAAK;EACV,KAAK,OAAO,OAAO,KAAK,OAAO,GAAG;EAClC,KAAK,gBAAgB;EACrB,KAAK,WAAW,oBAAoB;GAClC,OAAO;GACP,SAAS;GACT,OAAO;GACP,eAAe,KAAK,SAAS;GAC9B,CAAC;;CAGJ,UAAU,IAA6D;EACrE,KAAK,KAAK;;CAGZ,UAAU,IAA4B;EACpC,KAAK,YAAY,IAAI,GAAG;EACxB,aAAa,KAAK,YAAY,OAAO,GAAG;;CAG1C,oBAA4B;EAC1B,KAAK,SAAS,QAAQ,KAAK;EAC3B,KAAK,SAAS,UAAU,KAAK;EAC7B,KAAK,SAAS,QAAQ,KAAK;EAC3B,KAAK,MAAM,MAAM,KAAK,aAAa,IAAI;;CAGzC,MAAM,MAAM,OAAO,SAAS,MAAM;EAChC,MAAM,aAAa,KAAK;EAExB,KAAK,YAAY,OAAO;EACxB,MAAM,aAAa,IAAI,iBAAiB;EACxC,KAAK,aAAa;EAClB,KAAK,UAAU;EACf,KAAK,QAAQ;EACb,IAAI,QAAQ,KAAK,mBAAmB;EAEpC,IAAI;EACJ,IAAI;GAEF,SAAS,yBAAyB,KAAK,qBACrC,KAAK,GAAG,EAAE,QAAQ,WAAW,QAAQ,CAAC,CACvC;WACM,KAAK;GACZ,KAAK,UAAU;GACf,KAAK,QAAQ;GACb,IAAI,QAAQ,KAAK,mBAAmB;GACpC;;EAGF,IAAI,EAAE,kBAAkB,UAAU;GAChC,KAAK,QAAQ;GACb,KAAK,UAAU;GACf,KAAK,QAAQ;GACb,IAAI,QAAQ,KAAK,mBAAmB;GACpC;;EAGF,IAAI,KAEF,cAAc,CAAC,qBAAqB;EAGtC,OACG,MAAM,QAAQ;GACb,IAAI,KAAK,eAAe,YAAY;GACpC,IAAI,KAAK,eAAe,YAAY;GACpC,KAAK,QAAQ;GACb,KAAK,UAAU;GACf,KAAK,QAAQ;GACb,KAAK,mBAAmB;IACxB,CACD,OAAO,QAAQ;GACd,IAAI,KAAK,eAAe,YAAY;GACpC,IAAI,KAAK,eAAe,YAAY;GASpC,IANE,WAAW,OAAO,WACjB,eAAe,SAAS,IAAI,SAAS,gBACrC,OAAO,iBAAiB,eACvB,eAAe,gBACf,IAAI,SAAS,cAGf;GAGF,KAAK,UAAU;GACf,KAAK,QAAQ;GACb,IAAI;IACF,IAAI,KAAK,WACP,OAAO,MACL,kCAAkC,KAAK,UAAU,IACjD,IACD;SAED,OAAO,MAAM,gCAAgC,IAAI;WAE7C;GAGR,KAAK,mBAAmB;IACxB;;CAGN,UAAU;EACR,KAAK;EACL,KAAK,YAAY,OAAO;EACxB,KAAK,OAAO;;CAGd,QAAQ;EACN,KAAK,YAAY,OAAO"}
|
|
@@ -29,7 +29,6 @@ declare class Scheduler {
|
|
|
29
29
|
enqueue(task: Task): void;
|
|
30
30
|
enqueueInLane(lane: SchedulerLane, task: Task): void;
|
|
31
31
|
flush(): void;
|
|
32
|
-
private finalizeFlush;
|
|
33
32
|
runWithSyncProgress<T>(fn: () => T): T;
|
|
34
33
|
waitForFlush(targetVersion?: number, timeoutMs?: number): Promise<void>;
|
|
35
34
|
getState(): {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.d.ts","names":[],"sources":["../../src/runtime/scheduler.ts"],"mappings":";;;;;;;AAmBA;;;;KADK,IAAA;AAAA,KACO,aAAA;AAAA,cA8BC,SAAA;EAAA,QACH,KAAA;EAAA,QAOA,OAAA;EAAA,QACA,SAAA;EAAA,QACA,KAAA;EAAA,QACA,cAAA;EAAA,QAGA,YAAA;EAAA,QAGA,aAAA;EAAA,QAGA,iBAAA;EAAA,QAGA,OAAA;EAAA,QAQA,SAAA;EAAA,QAEA,eAAA;EAAA,QAUA,mBAAA;EAAA,QASA,WAAA;EAAA,QAmBA,iBAAA;
|
|
1
|
+
{"version":3,"file":"scheduler.d.ts","names":[],"sources":["../../src/runtime/scheduler.ts"],"mappings":";;;;;;;AAmBA;;;;KADK,IAAA;AAAA,KACO,aAAA;AAAA,cA8BC,SAAA;EAAA,QACH,KAAA;EAAA,QAOA,OAAA;EAAA,QACA,SAAA;EAAA,QACA,KAAA;EAAA,QACA,cAAA;EAAA,QAGA,YAAA;EAAA,QAGA,aAAA;EAAA,QAGA,iBAAA;EAAA,QAGA,OAAA;EAAA,QAQA,SAAA;EAAA,QAEA,eAAA;EAAA,QAUA,mBAAA;EAAA,QASA,WAAA;EAAA,QAmBA,iBAAA;EA4BR,OAAA,CAAQ,IAAA,EAAM,IAAA;EAId,aAAA,CAAc,IAAA,EAAM,aAAA,EAAe,IAAA,EAAM,IAAA;EAuBzC,KAAA,CAAA;EAuFA,mBAAA,GAAA,CAAuB,EAAA,QAAU,CAAA,GAAI,CAAA;EAyCrC,YAAA,CAAa,aAAA,WAAwB,SAAA,YAAmB,OAAA;EAgCxD,QAAA,CAAA;;;;;;;;;;;;;;;;EAsBA,eAAA,CAAA;EAIA,aAAA,CAAA;EAMA,iBAAA,GAAA,CAAqB,EAAA,QAAU,CAAA,EAAG,SAAA,sBAAwC,CAAA;EAmB1E,YAAA,CAAa,CAAA;EAOb,WAAA,CAAA;EAIA,WAAA,CAAA;EAKA,qBAAA,CAAA;EAAA,QAgCQ,cAAA;AAAA;AAAA,cAmBG,eAAA,EAAe,SAAA;AAAA,iBAEZ,oBAAA,CAAA;AAAA,iBAIA,oBAAA,CAAqB,OAAA,EAAS,aAAA,GAAgB,aAAA"}
|
|
@@ -86,7 +86,7 @@ var Scheduler = class {
|
|
|
86
86
|
queue.head = 0;
|
|
87
87
|
}
|
|
88
88
|
scheduleFlushKick() {
|
|
89
|
-
if (this.running || this.kickScheduled || this.inHandler || isBulkCommitActive() || !this.hasPendingTasks()) return;
|
|
89
|
+
if (this.running || this.kickScheduled || this.inHandler || this.allowSyncProgress || isBulkCommitActive() || !this.hasPendingTasks()) return;
|
|
90
90
|
this.kickScheduled = true;
|
|
91
91
|
queueMicrotask(() => {
|
|
92
92
|
this.kickScheduled = false;
|
|
@@ -141,7 +141,7 @@ var Scheduler = class {
|
|
|
141
141
|
executedInLane++;
|
|
142
142
|
didRunTask = true;
|
|
143
143
|
} catch (err) {
|
|
144
|
-
if (this.executionDepth > 0) this.executionDepth
|
|
144
|
+
if (this.executionDepth > 0) this.executionDepth--;
|
|
145
145
|
fatal = err;
|
|
146
146
|
break;
|
|
147
147
|
}
|
|
@@ -155,28 +155,19 @@ var Scheduler = class {
|
|
|
155
155
|
this.running = false;
|
|
156
156
|
this.depth = 0;
|
|
157
157
|
this.executionDepth = 0;
|
|
158
|
-
for (const lane of SCHEDULER_LANES)
|
|
158
|
+
for (const lane of SCHEDULER_LANES) {
|
|
159
|
+
const queue = this.lanes[lane];
|
|
160
|
+
if (queue.head > 0) this.compactLane(queue);
|
|
161
|
+
}
|
|
159
162
|
this.flushVersion++;
|
|
160
163
|
this.resolveWaiters();
|
|
161
164
|
recordSchedulerFlushTaskCount(executedTaskCount);
|
|
162
165
|
}
|
|
163
166
|
if (fatal) throw fatal;
|
|
164
167
|
}
|
|
165
|
-
finalizeFlush() {}
|
|
166
168
|
runWithSyncProgress(fn) {
|
|
167
169
|
const prev = this.allowSyncProgress;
|
|
168
170
|
this.allowSyncProgress = true;
|
|
169
|
-
const g = globalThis;
|
|
170
|
-
const origQueueMicrotask = g.queueMicrotask;
|
|
171
|
-
const origSetTimeout = g.setTimeout;
|
|
172
|
-
if (isDevelopmentEnvironment()) {
|
|
173
|
-
g.queueMicrotask = () => {
|
|
174
|
-
throw new Error("[Scheduler] queueMicrotask not allowed during runWithSyncProgress");
|
|
175
|
-
};
|
|
176
|
-
g.setTimeout = () => {
|
|
177
|
-
throw new Error("[Scheduler] setTimeout not allowed during runWithSyncProgress");
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
171
|
const startVersion = this.flushVersion;
|
|
181
172
|
try {
|
|
182
173
|
const res = fn();
|
|
@@ -186,10 +177,6 @@ var Scheduler = class {
|
|
|
186
177
|
}
|
|
187
178
|
return res;
|
|
188
179
|
} finally {
|
|
189
|
-
if (isDevelopmentEnvironment()) {
|
|
190
|
-
g.queueMicrotask = origQueueMicrotask;
|
|
191
|
-
g.setTimeout = origSetTimeout;
|
|
192
|
-
}
|
|
193
180
|
try {
|
|
194
181
|
if (this.flushVersion === startVersion) {
|
|
195
182
|
this.flushVersion++;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scheduler.js","names":[],"sources":["../../src/runtime/scheduler.ts"],"sourcesContent":["/**\n * Serialized update scheduler — safer design (no inline execution, explicit flush)\n *\n * Key ideas:\n * - Never execute a task inline from `enqueue`.\n * - `flush()` is explicit and non-reentrant.\n * - `runWithSyncProgress()` allows enqueues temporarily but does not run tasks\n * inline; it runs `fn` and then does an explicit `flush()`.\n * - `waitForFlush()` is race-free with a monotonic `flushVersion`.\n */\n\nimport { isDevelopmentEnvironment } from '../common/env';\nimport { assertSchedulingPrecondition, invariant } from '../dev/invariant';\nimport { logger } from '../dev/logger';\nimport { recordSchedulerFlushTaskCount } from './perf-metrics';\n\nconst MAX_FLUSH_DEPTH = 50;\n\ntype Task = () => void;\nexport type SchedulerLane = 'derived' | 'component' | 'reactive' | 'post';\n\nconst SCHEDULER_LANES: SchedulerLane[] = [\n 'derived',\n 'component',\n 'reactive',\n 'post',\n];\n\ninterface LaneQueue {\n tasks: Task[];\n head: number;\n}\n\nfunction isBulkCommitActive(): boolean {\n try {\n const fb = (\n globalThis as {\n __ASKR_FASTLANE?: { isBulkCommitActive?: () => boolean };\n }\n ).__ASKR_FASTLANE;\n return typeof fb?.isBulkCommitActive === 'function'\n ? !!fb.isBulkCommitActive()\n : false;\n } catch (e) {\n void e;\n return false;\n }\n}\n\nexport class Scheduler {\n private lanes: Record<SchedulerLane, LaneQueue> = {\n derived: { tasks: [], head: 0 },\n component: { tasks: [], head: 0 },\n reactive: { tasks: [], head: 0 },\n post: { tasks: [], head: 0 },\n };\n\n private running = false;\n private inHandler = false;\n private depth = 0;\n private executionDepth = 0; // for compat with existing diagnostics\n\n // Monotonic flush version increments at end of each flush\n private flushVersion = 0;\n\n // Best-effort microtask kick scheduling\n private kickScheduled = false;\n\n // Escape hatch flag for runWithSyncProgress\n private allowSyncProgress = false;\n\n // Waiters waiting for flushVersion >= target\n private waiters: Array<{\n target: number;\n resolve: () => void;\n reject: (err: unknown) => void;\n timer?: ReturnType<typeof setTimeout>;\n }> = [];\n\n // Keep a lightweight taskCount for compatibility/diagnostics\n private taskCount = 0;\n\n private hasPendingTasks(): boolean {\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n if (queue.head < queue.tasks.length) {\n return true;\n }\n }\n return false;\n }\n\n private getPendingTaskCount(): number {\n let total = 0;\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n total += queue.tasks.length - queue.head;\n }\n return total;\n }\n\n private compactLane(queue: LaneQueue): void {\n if (queue.head >= queue.tasks.length) {\n queue.tasks.length = 0;\n queue.head = 0;\n return;\n }\n\n if (queue.head <= 0) {\n return;\n }\n\n const remaining = queue.tasks.length - queue.head;\n for (let i = 0; i < remaining; i++) {\n queue.tasks[i] = queue.tasks[queue.head + i];\n }\n queue.tasks.length = remaining;\n queue.head = 0;\n }\n\n private scheduleFlushKick(): void {\n if (\n this.running ||\n this.kickScheduled ||\n this.inHandler ||\n isBulkCommitActive() ||\n !this.hasPendingTasks()\n ) {\n return;\n }\n\n this.kickScheduled = true;\n queueMicrotask(() => {\n this.kickScheduled = false;\n if (this.running || isBulkCommitActive() || !this.hasPendingTasks()) {\n return;\n }\n try {\n this.flush();\n } catch (err) {\n setTimeout(() => {\n throw err;\n });\n }\n });\n }\n\n enqueue(task: Task): void {\n this.enqueueInLane('component', task);\n }\n\n enqueueInLane(lane: SchedulerLane, task: Task): void {\n assertSchedulingPrecondition(\n typeof task === 'function',\n 'enqueue() requires a function'\n );\n\n // Strict rule: during bulk commit, only allow enqueues if runWithSyncProgress enabled\n if (isBulkCommitActive() && !this.allowSyncProgress) {\n if (isDevelopmentEnvironment()) {\n throw new Error(\n '[Scheduler] enqueue() during bulk commit (not allowed)'\n );\n }\n return;\n }\n\n // Enqueue task and account counts\n this.lanes[lane].tasks.push(task);\n this.taskCount++;\n\n this.scheduleFlushKick();\n }\n\n flush(): void {\n invariant(\n !this.running,\n '[Scheduler] flush() called while already running'\n );\n\n // Dev-only guard: disallow flush during bulk commit unless allowed\n if (isDevelopmentEnvironment()) {\n if (isBulkCommitActive() && !this.allowSyncProgress) {\n throw new Error(\n '[Scheduler] flush() started during bulk commit (not allowed)'\n );\n }\n }\n\n this.running = true;\n this.depth = 0;\n let fatal: unknown = null;\n let executedTaskCount = 0;\n const checkFlushDepth = isDevelopmentEnvironment();\n\n try {\n while (fatal === null) {\n let didRunTask = false;\n\n for (const lane of SCHEDULER_LANES) {\n const laneQueue = this.lanes[lane];\n let executedInLane = 0;\n\n while (laneQueue.head < laneQueue.tasks.length) {\n this.depth++;\n if (checkFlushDepth && this.depth > MAX_FLUSH_DEPTH) {\n throw new Error(\n `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`\n );\n }\n\n const task = laneQueue.tasks[laneQueue.head++];\n try {\n this.executionDepth++;\n task();\n this.executionDepth--;\n executedTaskCount++;\n executedInLane++;\n didRunTask = true;\n } catch (err) {\n // ensure executionDepth stays balanced\n if (this.executionDepth > 0) this.executionDepth = 0;\n fatal = err;\n break;\n }\n }\n\n if (executedInLane > 0) {\n this.taskCount = Math.max(0, this.taskCount - executedInLane);\n }\n\n if (fatal !== null) {\n break;\n }\n }\n\n if (!didRunTask) {\n break;\n }\n }\n } finally {\n this.running = false;\n this.depth = 0;\n this.executionDepth = 0;\n\n for (const lane of SCHEDULER_LANES) {\n this.compactLane(this.lanes[lane]);\n }\n\n // Advance flush epoch and resolve waiters\n this.flushVersion++;\n this.resolveWaiters();\n recordSchedulerFlushTaskCount(executedTaskCount);\n }\n\n if (fatal) throw fatal;\n }\n\n private finalizeFlush(): void {\n // Not used - finalize happens in finally block above\n }\n\n runWithSyncProgress<T>(fn: () => T): T {\n const prev = this.allowSyncProgress;\n this.allowSyncProgress = true;\n\n const g = globalThis as {\n queueMicrotask?: (...args: unknown[]) => void;\n setTimeout?: (...args: unknown[]) => unknown;\n };\n const origQueueMicrotask = g.queueMicrotask;\n const origSetTimeout = g.setTimeout;\n\n if (isDevelopmentEnvironment()) {\n g.queueMicrotask = () => {\n throw new Error(\n '[Scheduler] queueMicrotask not allowed during runWithSyncProgress'\n );\n };\n g.setTimeout = () => {\n throw new Error(\n '[Scheduler] setTimeout not allowed during runWithSyncProgress'\n );\n };\n }\n\n // Snapshot flushVersion so we can ensure we always complete an epoch\n const startVersion = this.flushVersion;\n\n try {\n const res = fn();\n\n // Flush deterministically if tasks were enqueued (and we're not already running)\n if (!this.running && this.hasPendingTasks()) {\n this.flush();\n }\n\n if (isDevelopmentEnvironment()) {\n if (this.hasPendingTasks()) {\n throw new Error(\n '[Scheduler] tasks remain after runWithSyncProgress flush'\n );\n }\n }\n\n return res;\n } finally {\n // Restore guarded globals\n if (isDevelopmentEnvironment()) {\n g.queueMicrotask = origQueueMicrotask;\n g.setTimeout = origSetTimeout;\n }\n\n // If no flush happened during the protected window, complete an epoch so\n // observers (tests) see progress even when fast-lane did synchronous work\n // without enqueuing tasks.\n try {\n if (this.flushVersion === startVersion) {\n this.flushVersion++;\n this.resolveWaiters();\n }\n } catch (e) {\n void e;\n }\n\n this.allowSyncProgress = prev;\n }\n }\n\n waitForFlush(targetVersion?: number, timeoutMs = 2000): Promise<void> {\n const target =\n typeof targetVersion === 'number' ? targetVersion : this.flushVersion + 1;\n if (this.flushVersion >= target) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const ns =\n (\n globalThis as unknown as Record<string, unknown> & {\n __ASKR__?: Record<string, unknown>;\n }\n ).__ASKR__ || {};\n const diag = {\n flushVersion: this.flushVersion,\n queueLen: this.getPendingTaskCount(),\n running: this.running,\n inHandler: this.inHandler,\n bulk: isBulkCommitActive(),\n namespace: ns,\n };\n reject(\n new Error(\n `waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`\n )\n );\n }, timeoutMs);\n\n this.waiters.push({ target, resolve, reject, timer });\n });\n }\n\n getState() {\n // Provide the compatibility shape expected by diagnostics/tests\n return {\n queueLength: this.getPendingTaskCount(),\n running: this.running,\n depth: this.depth,\n executionDepth: this.executionDepth,\n taskCount: this.taskCount,\n flushVersion: this.flushVersion,\n laneQueues: {\n derived: this.lanes.derived.tasks.length - this.lanes.derived.head,\n component:\n this.lanes.component.tasks.length - this.lanes.component.head,\n reactive: this.lanes.reactive.tasks.length - this.lanes.reactive.head,\n post: this.lanes.post.tasks.length - this.lanes.post.head,\n },\n // New fields for optional inspection\n inHandler: this.inHandler,\n allowSyncProgress: this.allowSyncProgress,\n };\n }\n\n getFlushVersion(): number {\n return this.flushVersion;\n }\n\n flushIfQueued(): void {\n if (!this.running && this.hasPendingTasks()) {\n this.flush();\n }\n }\n\n runInHandlerScope<T>(fn: () => T, flushMode: 'defer' | 'sync' = 'defer'): T {\n const prevInHandler = this.inHandler;\n this.inHandler = true;\n\n try {\n return fn();\n } finally {\n this.inHandler = prevInHandler;\n\n if (!this.inHandler) {\n if (flushMode === 'sync') {\n this.flushIfQueued();\n } else {\n this.scheduleFlushKick();\n }\n }\n }\n }\n\n setInHandler(v: boolean) {\n this.inHandler = v;\n if (!v) {\n this.scheduleFlushKick();\n }\n }\n\n isInHandler(): boolean {\n return this.inHandler;\n }\n\n isExecuting(): boolean {\n return this.running || this.executionDepth > 0;\n }\n\n // Clear pending synchronous tasks (used by fastlane enter/exit)\n clearPendingSyncTasks(): number {\n const remaining = this.getPendingTaskCount();\n if (remaining <= 0) return 0;\n\n if (this.running) {\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n queue.tasks.length = queue.head;\n }\n this.taskCount = Math.max(0, this.taskCount - remaining);\n queueMicrotask(() => {\n try {\n this.flushVersion++;\n this.resolveWaiters();\n } catch (e) {\n void e;\n }\n });\n return remaining;\n }\n\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n queue.tasks.length = 0;\n queue.head = 0;\n }\n this.taskCount = Math.max(0, this.taskCount - remaining);\n this.flushVersion++;\n this.resolveWaiters();\n return remaining;\n }\n\n private resolveWaiters() {\n if (this.waiters.length === 0) return;\n const ready: Array<() => void> = [];\n const remaining: typeof this.waiters = [];\n\n for (const w of this.waiters) {\n if (this.flushVersion >= w.target) {\n if (w.timer) clearTimeout(w.timer);\n ready.push(w.resolve);\n } else {\n remaining.push(w);\n }\n }\n\n this.waiters = remaining;\n for (const r of ready) r();\n }\n}\n\nexport const globalScheduler = new Scheduler();\n\nexport function isSchedulerExecuting(): boolean {\n return globalScheduler.isExecuting();\n}\n\nexport function scheduleEventHandler(handler: EventListener): EventListener {\n return (event: Event) => {\n try {\n globalScheduler.runInHandlerScope(() => {\n handler.call(null, event);\n });\n } catch (error) {\n logger.error('[Askr] Event handler error:', error);\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scheduler.js","names":[],"sources":["../../src/runtime/scheduler.ts"],"sourcesContent":["/**\n * Serialized update scheduler — safer design (no inline execution, explicit flush)\n *\n * Key ideas:\n * - Never execute a task inline from `enqueue`.\n * - `flush()` is explicit and non-reentrant.\n * - `runWithSyncProgress()` allows enqueues temporarily but does not run tasks\n * inline; it runs `fn` and then does an explicit `flush()`.\n * - `waitForFlush()` is race-free with a monotonic `flushVersion`.\n */\n\nimport { isDevelopmentEnvironment } from '../common/env';\nimport { assertSchedulingPrecondition, invariant } from '../dev/invariant';\nimport { logger } from '../dev/logger';\nimport { recordSchedulerFlushTaskCount } from './perf-metrics';\n\nconst MAX_FLUSH_DEPTH = 50;\n\ntype Task = () => void;\nexport type SchedulerLane = 'derived' | 'component' | 'reactive' | 'post';\n\nconst SCHEDULER_LANES: SchedulerLane[] = [\n 'derived',\n 'component',\n 'reactive',\n 'post',\n];\n\ninterface LaneQueue {\n tasks: Task[];\n head: number;\n}\n\nfunction isBulkCommitActive(): boolean {\n try {\n const fb = (\n globalThis as {\n __ASKR_FASTLANE?: { isBulkCommitActive?: () => boolean };\n }\n ).__ASKR_FASTLANE;\n return typeof fb?.isBulkCommitActive === 'function'\n ? !!fb.isBulkCommitActive()\n : false;\n } catch (e) {\n void e;\n return false;\n }\n}\n\nexport class Scheduler {\n private lanes: Record<SchedulerLane, LaneQueue> = {\n derived: { tasks: [], head: 0 },\n component: { tasks: [], head: 0 },\n reactive: { tasks: [], head: 0 },\n post: { tasks: [], head: 0 },\n };\n\n private running = false;\n private inHandler = false;\n private depth = 0;\n private executionDepth = 0; // for compat with existing diagnostics\n\n // Monotonic flush version increments at end of each flush\n private flushVersion = 0;\n\n // Best-effort microtask kick scheduling\n private kickScheduled = false;\n\n // Escape hatch flag for runWithSyncProgress\n private allowSyncProgress = false;\n\n // Waiters waiting for flushVersion >= target\n private waiters: Array<{\n target: number;\n resolve: () => void;\n reject: (err: unknown) => void;\n timer?: ReturnType<typeof setTimeout>;\n }> = [];\n\n // Keep a lightweight taskCount for compatibility/diagnostics\n private taskCount = 0;\n\n private hasPendingTasks(): boolean {\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n if (queue.head < queue.tasks.length) {\n return true;\n }\n }\n return false;\n }\n\n private getPendingTaskCount(): number {\n let total = 0;\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n total += queue.tasks.length - queue.head;\n }\n return total;\n }\n\n private compactLane(queue: LaneQueue): void {\n if (queue.head >= queue.tasks.length) {\n queue.tasks.length = 0;\n queue.head = 0;\n return;\n }\n\n if (queue.head <= 0) {\n return;\n }\n\n const remaining = queue.tasks.length - queue.head;\n for (let i = 0; i < remaining; i++) {\n queue.tasks[i] = queue.tasks[queue.head + i];\n }\n queue.tasks.length = remaining;\n queue.head = 0;\n }\n\n private scheduleFlushKick(): void {\n if (\n this.running ||\n this.kickScheduled ||\n this.inHandler ||\n this.allowSyncProgress ||\n isBulkCommitActive() ||\n !this.hasPendingTasks()\n ) {\n return;\n }\n\n this.kickScheduled = true;\n queueMicrotask(() => {\n this.kickScheduled = false;\n if (this.running || isBulkCommitActive() || !this.hasPendingTasks()) {\n return;\n }\n try {\n this.flush();\n } catch (err) {\n setTimeout(() => {\n throw err;\n });\n }\n });\n }\n\n enqueue(task: Task): void {\n this.enqueueInLane('component', task);\n }\n\n enqueueInLane(lane: SchedulerLane, task: Task): void {\n assertSchedulingPrecondition(\n typeof task === 'function',\n 'enqueue() requires a function'\n );\n\n // Strict rule: during bulk commit, only allow enqueues if runWithSyncProgress enabled\n if (isBulkCommitActive() && !this.allowSyncProgress) {\n if (isDevelopmentEnvironment()) {\n throw new Error(\n '[Scheduler] enqueue() during bulk commit (not allowed)'\n );\n }\n return;\n }\n\n // Enqueue task and account counts\n this.lanes[lane].tasks.push(task);\n this.taskCount++;\n\n this.scheduleFlushKick();\n }\n\n flush(): void {\n invariant(\n !this.running,\n '[Scheduler] flush() called while already running'\n );\n\n // Dev-only guard: disallow flush during bulk commit unless allowed\n if (isDevelopmentEnvironment()) {\n if (isBulkCommitActive() && !this.allowSyncProgress) {\n throw new Error(\n '[Scheduler] flush() started during bulk commit (not allowed)'\n );\n }\n }\n\n this.running = true;\n this.depth = 0;\n let fatal: unknown = null;\n let executedTaskCount = 0;\n const checkFlushDepth = isDevelopmentEnvironment();\n\n try {\n while (fatal === null) {\n let didRunTask = false;\n\n for (const lane of SCHEDULER_LANES) {\n const laneQueue = this.lanes[lane];\n let executedInLane = 0;\n\n while (laneQueue.head < laneQueue.tasks.length) {\n this.depth++;\n if (checkFlushDepth && this.depth > MAX_FLUSH_DEPTH) {\n throw new Error(\n `[Scheduler] exceeded MAX_FLUSH_DEPTH (${MAX_FLUSH_DEPTH}). Likely infinite update loop.`\n );\n }\n\n const task = laneQueue.tasks[laneQueue.head++];\n try {\n this.executionDepth++;\n task();\n this.executionDepth--;\n executedTaskCount++;\n executedInLane++;\n didRunTask = true;\n } catch (err) {\n // ensure executionDepth stays balanced for the task that threw\n if (this.executionDepth > 0) this.executionDepth--;\n fatal = err;\n break;\n }\n }\n\n if (executedInLane > 0) {\n this.taskCount = Math.max(0, this.taskCount - executedInLane);\n }\n\n if (fatal !== null) {\n break;\n }\n }\n\n if (!didRunTask) {\n break;\n }\n }\n } finally {\n this.running = false;\n this.depth = 0;\n this.executionDepth = 0;\n\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n if (queue.head > 0) {\n this.compactLane(queue);\n }\n }\n\n // Advance flush epoch and resolve waiters\n this.flushVersion++;\n this.resolveWaiters();\n recordSchedulerFlushTaskCount(executedTaskCount);\n }\n\n if (fatal) throw fatal;\n }\n\n runWithSyncProgress<T>(fn: () => T): T {\n const prev = this.allowSyncProgress;\n this.allowSyncProgress = true;\n\n // Snapshot flushVersion so we can ensure we always complete an epoch\n const startVersion = this.flushVersion;\n\n try {\n const res = fn();\n\n // Flush deterministically if tasks were enqueued (and we're not already running)\n if (!this.running && this.hasPendingTasks()) {\n this.flush();\n }\n\n if (isDevelopmentEnvironment()) {\n if (this.hasPendingTasks()) {\n throw new Error(\n '[Scheduler] tasks remain after runWithSyncProgress flush'\n );\n }\n }\n\n return res;\n } finally {\n // If no flush happened during the protected window, complete an epoch so\n // observers (tests) see progress even when fast-lane did synchronous work\n // without enqueuing tasks.\n try {\n if (this.flushVersion === startVersion) {\n this.flushVersion++;\n this.resolveWaiters();\n }\n } catch (e) {\n void e;\n }\n\n this.allowSyncProgress = prev;\n }\n }\n\n waitForFlush(targetVersion?: number, timeoutMs = 2000): Promise<void> {\n const target =\n typeof targetVersion === 'number' ? targetVersion : this.flushVersion + 1;\n if (this.flushVersion >= target) return Promise.resolve();\n\n return new Promise((resolve, reject) => {\n const timer = setTimeout(() => {\n const ns =\n (\n globalThis as unknown as Record<string, unknown> & {\n __ASKR__?: Record<string, unknown>;\n }\n ).__ASKR__ || {};\n const diag = {\n flushVersion: this.flushVersion,\n queueLen: this.getPendingTaskCount(),\n running: this.running,\n inHandler: this.inHandler,\n bulk: isBulkCommitActive(),\n namespace: ns,\n };\n reject(\n new Error(\n `waitForFlush timeout ${timeoutMs}ms: ${JSON.stringify(diag)}`\n )\n );\n }, timeoutMs);\n\n this.waiters.push({ target, resolve, reject, timer });\n });\n }\n\n getState() {\n // Provide the compatibility shape expected by diagnostics/tests\n return {\n queueLength: this.getPendingTaskCount(),\n running: this.running,\n depth: this.depth,\n executionDepth: this.executionDepth,\n taskCount: this.taskCount,\n flushVersion: this.flushVersion,\n laneQueues: {\n derived: this.lanes.derived.tasks.length - this.lanes.derived.head,\n component:\n this.lanes.component.tasks.length - this.lanes.component.head,\n reactive: this.lanes.reactive.tasks.length - this.lanes.reactive.head,\n post: this.lanes.post.tasks.length - this.lanes.post.head,\n },\n // New fields for optional inspection\n inHandler: this.inHandler,\n allowSyncProgress: this.allowSyncProgress,\n };\n }\n\n getFlushVersion(): number {\n return this.flushVersion;\n }\n\n flushIfQueued(): void {\n if (!this.running && this.hasPendingTasks()) {\n this.flush();\n }\n }\n\n runInHandlerScope<T>(fn: () => T, flushMode: 'defer' | 'sync' = 'defer'): T {\n const prevInHandler = this.inHandler;\n this.inHandler = true;\n\n try {\n return fn();\n } finally {\n this.inHandler = prevInHandler;\n\n if (!this.inHandler) {\n if (flushMode === 'sync') {\n this.flushIfQueued();\n } else {\n this.scheduleFlushKick();\n }\n }\n }\n }\n\n setInHandler(v: boolean) {\n this.inHandler = v;\n if (!v) {\n this.scheduleFlushKick();\n }\n }\n\n isInHandler(): boolean {\n return this.inHandler;\n }\n\n isExecuting(): boolean {\n return this.running || this.executionDepth > 0;\n }\n\n // Clear pending synchronous tasks (used by fastlane enter/exit)\n clearPendingSyncTasks(): number {\n const remaining = this.getPendingTaskCount();\n if (remaining <= 0) return 0;\n\n if (this.running) {\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n queue.tasks.length = queue.head;\n }\n this.taskCount = Math.max(0, this.taskCount - remaining);\n queueMicrotask(() => {\n try {\n this.flushVersion++;\n this.resolveWaiters();\n } catch (e) {\n void e;\n }\n });\n return remaining;\n }\n\n for (const lane of SCHEDULER_LANES) {\n const queue = this.lanes[lane];\n queue.tasks.length = 0;\n queue.head = 0;\n }\n this.taskCount = Math.max(0, this.taskCount - remaining);\n this.flushVersion++;\n this.resolveWaiters();\n return remaining;\n }\n\n private resolveWaiters() {\n if (this.waiters.length === 0) return;\n const ready: Array<() => void> = [];\n const remaining: typeof this.waiters = [];\n\n for (const w of this.waiters) {\n if (this.flushVersion >= w.target) {\n if (w.timer) clearTimeout(w.timer);\n ready.push(w.resolve);\n } else {\n remaining.push(w);\n }\n }\n\n this.waiters = remaining;\n for (const r of ready) r();\n }\n}\n\nexport const globalScheduler = new Scheduler();\n\nexport function isSchedulerExecuting(): boolean {\n return globalScheduler.isExecuting();\n}\n\nexport function scheduleEventHandler(handler: EventListener): EventListener {\n return (event: Event) => {\n try {\n globalScheduler.runInHandlerScope(() => {\n handler.call(null, event);\n });\n } catch (error) {\n logger.error('[Askr] Event handler error:', error);\n }\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selector.d.ts","names":[],"sources":["../../src/runtime/selector.ts"],"mappings":";UA6BiB,QAAA;EAAA,CACd,SAAA,EAAW,CAAA;AAAA;AAAA,KAOT,cAAA;EACH,cAAA,CAAe,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA;AAAA;AAAA,
|
|
1
|
+
{"version":3,"file":"selector.d.ts","names":[],"sources":["../../src/runtime/selector.ts"],"mappings":";UA6BiB,QAAA;EAAA,CACd,SAAA,EAAW,CAAA;AAAA;AAAA,KAOT,cAAA;EACH,cAAA,CAAe,CAAA,EAAG,CAAA,EAAG,CAAA,EAAG,CAAA;AAAA;AAAA,iBAqcV,QAAA,GAAA,CACd,MAAA,QAAc,CAAA,EACd,MAAA,GAAQ,cAAA,CAAe,CAAA,IACtB,QAAA,CAAS,CAAA"}
|