@askrjs/askr 0.0.94 → 0.0.96

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/CHANGELOG.md +2 -0
  2. package/README.md +3 -1
  3. package/dist/actions/index.d.ts +1 -1
  4. package/dist/{activity-CJIQyo5X.d.ts → activity-D_5x2HXQ.d.ts} +1 -1
  5. package/dist/{activity-BshyLB3_.js → activity-d4t3CdzK.js} +1 -1
  6. package/dist/benchmark.js +5 -5
  7. package/dist/boot/index.d.ts +2 -2
  8. package/dist/boot/index.js +1 -1
  9. package/dist/{boot-BsMB_ySP.js → boot-C1wT3Vfs.js} +11 -9
  10. package/dist/{cleanup-DefafSjG.js → cleanup-D7uN-Z1s.js} +1 -1
  11. package/dist/{component-cleanup-hrqSikl2.js → component-cleanup-DYG80yhG.js} +2 -0
  12. package/dist/{component-internal-DOVVMjG7.js → component-internal-D0yE2Ch3.js} +72 -8
  13. package/dist/components/index.d.ts +5 -2
  14. package/dist/components/index.js +4 -1
  15. package/dist/control/index.d.ts +1 -1
  16. package/dist/control/index.js +1 -1
  17. package/dist/{control-DqOzkgc7.js → control-C0klPV7Q.js} +1 -1
  18. package/dist/{csp-nonce-BBxq1zq4.js → csp-nonce-BVybpV1v.js} +1 -1
  19. package/dist/data/index.d.ts +1 -1
  20. package/dist/data/index.js +1 -1
  21. package/dist/{data-DYqokZzc.js → data-Bm8L6JOm.js} +1 -1
  22. package/dist/{deferred-BS0Xpstg.js → deferred-p8hyanxJ.js} +2 -2
  23. package/dist/{for-internal-jrliHZmF.js → for-internal-DkBwTG4S.js} +2 -2
  24. package/dist/foundations/index.d.ts +1 -1
  25. package/dist/foundations/index.js +1 -1
  26. package/dist/foundations/state/index.d.ts +1 -1
  27. package/dist/foundations/structures/index.d.ts +1 -1
  28. package/dist/foundations/structures/index.js +1 -1
  29. package/dist/fx/index.d.ts +1 -1
  30. package/dist/{index-KQ7Vl4-8.d.ts → index-CTIBOqUB.d.ts} +4 -0
  31. package/dist/{index-C29SEGIv.d.ts → index-S2IqSJib.d.ts} +1 -1
  32. package/dist/index.d.ts +1 -1
  33. package/dist/index.js +5 -5
  34. package/dist/{lifecycle-operations-Qxq6ABrC.js → lifecycle-operations-CBzG5PyB.js} +1 -1
  35. package/dist/{navigate-CECCMPAr.js → navigate-DChM5m8m.js} +6 -6
  36. package/dist/{navigate-BL_FIIJ6.d.ts → navigate-eGm4omPj.d.ts} +1 -1
  37. package/dist/{renderer-CLvq458M.js → renderer-tXX_xj86.js} +5 -32
  38. package/dist/{resolution-D8PgnTzh.js → resolution-BQyo7qNn.js} +2 -2
  39. package/dist/{resource-operation-CLQXCn1p.js → resource-operation-B62UQKHV.js} +1 -1
  40. package/dist/resources/index.d.ts +2 -2
  41. package/dist/resources/index.js +4 -4
  42. package/dist/router/index.d.ts +3 -3
  43. package/dist/router/index.js +5 -5
  44. package/dist/ssg/index.d.ts +1 -1
  45. package/dist/ssg/index.js +163 -122
  46. package/dist/ssr/index.d.ts +1 -1
  47. package/dist/ssr/index.js +1 -1
  48. package/dist/{ssr-8_vHDWqV.js → ssr-DYcK20IX.js} +5 -5
  49. package/dist/{structures-umvVUD-M.js → structures-CPycoyZj.js} +45 -10
  50. package/dist/testing/index.d.ts +1 -1
  51. package/dist/testing/index.js +2 -2
  52. package/dist/{verify-hydration-jf8X2V3x.js → verify-hydration-m-wVbYOD.js} +1 -1
  53. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
- import { Z as getSignal } from "../index-KQ7Vl4-8.js";
2
- import { i as onRouteChange, n as RouteChangeOptions, t as RouteChangeCleanup } from "../activity-CJIQyo5X.js";
1
+ import { Z as getSignal } from "../index-CTIBOqUB.js";
2
+ import { i as onRouteChange, n as RouteChangeOptions, t as RouteChangeCleanup } from "../activity-D_5x2HXQ.js";
3
3
  //#region src/runtime/resource-operation.d.ts
4
4
  /** Reactive result of a {@link resource}: current value, loading state, and controls. */
5
5
  interface ResourceResult<T> {
@@ -1,9 +1,9 @@
1
1
  import { t as enqueueRuntimeLane } from "../access-d_DCdwqn.js";
2
2
  import { f as getCurrentComponentInstance, g as getSignal, i as claimHookIndex } from "../component-scope-4I3yEMkb.js";
3
- import { a as registerCommitOperation } from "../component-internal-DOVVMjG7.js";
4
- import { t as resource } from "../resource-operation-CLQXCn1p.js";
5
- import { a as task, i as routeActive, n as documentVisible, o as timer, r as on, s as windowFocused, t as capture } from "../lifecycle-operations-Qxq6ABrC.js";
6
- import { n as onRouteChange } from "../activity-BshyLB3_.js";
3
+ import { a as registerCommitOperation } from "../component-internal-D0yE2Ch3.js";
4
+ import { t as resource } from "../resource-operation-B62UQKHV.js";
5
+ import { a as task, i as routeActive, n as documentVisible, o as timer, r as on, s as windowFocused, t as capture } from "../lifecycle-operations-CBzG5PyB.js";
6
+ import { n as onRouteChange } from "../activity-d4t3CdzK.js";
7
7
  //#region src/runtime/stream-operation.ts
8
8
  function toError(value) {
9
9
  return value instanceof Error ? value : new Error(String(value));
@@ -1,7 +1,7 @@
1
- import { $ as AccessDecision, At as RouteRegistry, Ct as RouteParams, Dt as RouteRecord, Et as RouteQuery, Ft as RouteSearch, It as RouteSearchValue, Lt as RouteSnapshot, Mt as RouteRenderResult, Nt as RouteRequestOptions, Ot as RouteRef, Pt as RouteRequestResult, Q as AccessAllowDecision, St as RouteOptions, Tt as RoutePolicy, _t as RouteManifest, at as LayoutScopeRecord, bt as RouteMetaSource, ct as ParsedSegment, dt as RouteAuthResolver, et as AccessDenyDecision, ft as RouteComponent, gt as RouteHandler, ht as RouteDestination, it as GroupHelperOptions, jt as RouteRegistryOptions, kt as RouteRefSearch, lt as Route, mt as RouteDefinition, nt as AccessRedirectDecision, ot as PageHelperOptions, pt as RouteContext, rn as RenderableChild, rt as AccessRedirectStatus, st as PageScopeRecord, tt as AccessDenyStatus, ut as RouteAuthOptions, vt as RouteMatch, wt as RoutePathParams, xt as RouteMode, yt as RouteMeta } from "../index-KQ7Vl4-8.js";
1
+ import { $ as AccessDecision, At as RouteRegistry, Ct as RouteParams, Dt as RouteRecord, Et as RouteQuery, Ft as RouteSearch, It as RouteSearchValue, Lt as RouteSnapshot, Mt as RouteRenderResult, Nt as RouteRequestOptions, Ot as RouteRef, Pt as RouteRequestResult, Q as AccessAllowDecision, St as RouteOptions, Tt as RoutePolicy, _t as RouteManifest, at as LayoutScopeRecord, bt as RouteMetaSource, ct as ParsedSegment, dt as RouteAuthResolver, et as AccessDenyDecision, ft as RouteComponent, gt as RouteHandler, ht as RouteDestination, it as GroupHelperOptions, jt as RouteRegistryOptions, kt as RouteRefSearch, lt as Route, mt as RouteDefinition, nt as AccessRedirectDecision, ot as PageHelperOptions, pt as RouteContext, rn as RenderableChild, rt as AccessRedirectStatus, st as PageScopeRecord, tt as AccessDenyStatus, ut as RouteAuthOptions, vt as RouteMatch, wt as RoutePathParams, xt as RouteMode, yt as RouteMeta } from "../index-CTIBOqUB.js";
2
2
  import { r as JSXElement, s as Props } from "../types-DkVJhVs9.js";
3
- import { a as ScrollRestorationOptions, i as NavigationScrollBehavior, n as NavigateOptions, r as HistoryScrollBehavior, t as navigate } from "../navigate-BL_FIIJ6.js";
4
- import { i as onRouteChange, n as RouteChangeOptions, r as currentRoute, t as RouteChangeCleanup } from "../activity-CJIQyo5X.js";
3
+ import { a as ScrollRestorationOptions, i as NavigationScrollBehavior, n as NavigateOptions, r as HistoryScrollBehavior, t as navigate } from "../navigate-eGm4omPj.js";
4
+ import { i as onRouteChange, n as RouteChangeOptions, r as currentRoute, t as RouteChangeCleanup } from "../activity-D_5x2HXQ.js";
5
5
  import { AuthContext, AuthContext as AuthContext$1, AuthRequirement, AuthRequirement as AuthRequirement$1 } from "@askrjs/auth";
6
6
  import { ObjectSchema } from "@askrjs/schema";
7
7
  //#region src/router/route-query.d.ts
@@ -1,11 +1,11 @@
1
1
  import { n as createRouteRegistry } from "../manifest-D6rfj_jC.js";
2
- import { a as isSafeHref } from "../csp-nonce-BBxq1zq4.js";
2
+ import { a as isSafeHref } from "../csp-nonce-BVybpV1v.js";
3
3
  import { jsx } from "../jsx-runtime.js";
4
4
  import { o as addRouteBasePath, y as getActiveRouteBasePath } from "../route-matching-Bo6UqG-5.js";
5
- import { a as group, c as route, i as fallback, n as resolveDeferredValues, o as index, r as routeData, s as page, t as Resolve } from "../deferred-BS0Xpstg.js";
6
- import { S as RouteDataLoadError, a as resolveRouteRequest, b as lazy, f as defer, g as Outlet, h as currentAuth, p as isDeferred, x as lazyRouteData } from "../resolution-D8PgnTzh.js";
7
- import { n as onRouteChange, t as currentRoute } from "../activity-BshyLB3_.js";
8
- import { a as serializeRouteMeta, c as getCurrentHref, d as parseTargetUrl, h as syncRegisteredRouteSnapshot, i as resolveRouteMeta, l as getRegisteredAppsSnapshot, m as syncAppRegistrationLocation, n as navigate, p as setCurrentRouteLocation, r as reconcileRouteMeta, s as saveScrollPosition, u as getWindowHref } from "../navigate-CECCMPAr.js";
5
+ import { a as group, c as route, i as fallback, n as resolveDeferredValues, o as index, r as routeData, s as page, t as Resolve } from "../deferred-p8hyanxJ.js";
6
+ import { S as RouteDataLoadError, a as resolveRouteRequest, b as lazy, f as defer, g as Outlet, h as currentAuth, p as isDeferred, x as lazyRouteData } from "../resolution-BQyo7qNn.js";
7
+ import { n as onRouteChange, t as currentRoute } from "../activity-d4t3CdzK.js";
8
+ import { a as serializeRouteMeta, c as getCurrentHref, d as parseTargetUrl, h as syncRegisteredRouteSnapshot, i as resolveRouteMeta, l as getRegisteredAppsSnapshot, m as syncAppRegistrationLocation, n as navigate, p as setCurrentRouteLocation, r as reconcileRouteMeta, s as saveScrollPosition, u as getWindowHref } from "../navigate-DChM5m8m.js";
9
9
  import { o as mergeProps } from "../compose-ref-1Fhs7mbF.js";
10
10
  import { t as applyInteractionPolicy } from "../interactions-B4XaFxt1.js";
11
11
  //#region src/router/route-query.ts
@@ -1,4 +1,4 @@
1
- import { At as RouteRegistry, St as RouteOptions, Tt as RoutePolicy, an as DocumentRenderArgs, gt as RouteHandler, on as DocumentRenderContext, sn as DocumentRenderer, un as SSRStyleRegistrationValidation, wt as RoutePathParams } from "../index-KQ7Vl4-8.js";
1
+ import { At as RouteRegistry, St as RouteOptions, Tt as RoutePolicy, an as DocumentRenderArgs, gt as RouteHandler, on as DocumentRenderContext, sn as DocumentRenderer, un as SSRStyleRegistrationValidation, wt as RoutePathParams } from "../index-CTIBOqUB.js";
2
2
  import { AuthRequirement } from "@askrjs/auth";
3
3
  import "node:fs/promises";
4
4
  //#region src/ssg/types.d.ts
package/dist/ssg/index.js CHANGED
@@ -1,15 +1,32 @@
1
1
  import "../access-d_DCdwqn.js";
2
2
  import { n as createRouteRegistry } from "../manifest-D6rfj_jC.js";
3
3
  import { o as addRouteBasePath } from "../route-matching-Bo6UqG-5.js";
4
- import { c as route, n as resolveDeferredValues } from "../deferred-BS0Xpstg.js";
5
- import { S as RouteDataLoadError, c as prepareRouteHydrationData, t as bindResolvedRouteData, v as getRenderHandler, y as _preloadRouteHandler } from "../resolution-D8PgnTzh.js";
4
+ import { c as route, n as resolveDeferredValues } from "../deferred-p8hyanxJ.js";
5
+ import { S as RouteDataLoadError, c as prepareRouteHydrationData, t as bindResolvedRouteData, v as getRenderHandler, y as _preloadRouteHandler } from "../resolution-BQyo7qNn.js";
6
6
  import { n as renderDocument } from "../ssr-DSHd_Q9K.js";
7
- import { n as renderToString } from "../ssr-8_vHDWqV.js";
7
+ import { n as renderToString } from "../ssr-DYcK20IX.js";
8
8
  import * as fs from "node:fs/promises";
9
9
  import * as pathModule from "node:path";
10
10
  import * as os from "node:os";
11
11
  import * as fsSync from "node:fs";
12
12
  //#region src/ssg/route-utils.ts
13
+ function getPortableOutputPathKey(filePath) {
14
+ return filePath.normalize("NFC").replaceAll("\\", "/").toLowerCase();
15
+ }
16
+ function describeRoute(descriptor) {
17
+ if (descriptor.route.path === descriptor.path) return `"${descriptor.path}"`;
18
+ return `"${descriptor.route.path}" (resolved as "${descriptor.path}")`;
19
+ }
20
+ /** Reject routes that could overwrite one another on any supported filesystem. */
21
+ function validateOutputPathCollisions(descriptors) {
22
+ const seen = /* @__PURE__ */ new Map();
23
+ for (const descriptor of descriptors) {
24
+ const key = getPortableOutputPathKey(descriptor.filePath);
25
+ const existing = seen.get(key);
26
+ if (existing) throw new Error(`SSG output path collision: routes ${describeRoute(existing)} and ${describeRoute(descriptor)} both map to "${descriptor.filePath}" after case-insensitive normalization`);
27
+ seen.set(key, descriptor);
28
+ }
29
+ }
13
30
  function assertSafeRoutePath(pathStr) {
14
31
  if (!pathStr.startsWith("/") || pathStr.startsWith("//") || pathStr.includes("\\") || pathStr.includes("\0") || pathStr.split("/").some((segment) => segment === "." || segment === "..")) throw new Error(`SSG route path must be an absolute URL path without dot segments or backslashes: ${pathStr}`);
15
32
  }
@@ -663,6 +680,24 @@ function resolveParallelism(requested) {
663
680
  const availableParallelism = os.availableParallelism;
664
681
  return Math.max(1, availableParallelism?.() ?? os.cpus().length ?? 1);
665
682
  }
683
+ const outputDirectoryQueues = /* @__PURE__ */ new Map();
684
+ async function withOutputDirectoryLock(outputDir, operation) {
685
+ const key = pathModule.resolve(outputDir).normalize("NFC").toLowerCase();
686
+ const previous = outputDirectoryQueues.get(key) ?? Promise.resolve();
687
+ let release;
688
+ const current = new Promise((resolve) => {
689
+ release = resolve;
690
+ });
691
+ const tail = previous.then(() => current, () => current);
692
+ outputDirectoryQueues.set(key, tail);
693
+ await previous.catch(() => void 0);
694
+ try {
695
+ return await operation();
696
+ } finally {
697
+ release();
698
+ if (outputDirectoryQueues.get(key) === tail) outputDirectoryQueues.delete(key);
699
+ }
700
+ }
666
701
  async function createStagingDirectory(outputDir) {
667
702
  const parent = pathModule.dirname(outputDir);
668
703
  const base = pathModule.basename(outputDir);
@@ -753,136 +788,142 @@ function createStaticGen(options) {
753
788
  * Returns detailed results
754
789
  */
755
790
  async generate(generateOptions = {}) {
756
- const splitRoutes = await splitStaticRoutes(configuredRoutes);
757
- const routes = splitRoutes.routes;
758
- validateRoutes(routes);
759
- const changedKeys = dedupeStrings(generateOptions.changedKeys);
760
- const changedRoutes = dedupeStrings(generateOptions.changedRoutes);
761
- const requestedMode = generateOptions.mode ?? "full";
762
- const previousManifest = !generateOptions.forceFull && requestedMode === "incremental" ? readIncrementalManifest(options.outputDir, seed) : null;
763
- const effectiveMode = generateOptions.forceFull || requestedMode !== "incremental" || previousManifest === null ? "full" : "incremental";
764
- const dataMap = resolveSsgData(routes, { dataOverrides: options.dataOverrides });
765
- const descriptors = routes.map(resolveRouteDescriptor);
766
- const routeResultsById = /* @__PURE__ */ new Map();
767
- for (const runtimeRoute of splitRoutes.runtimeOnly) routeResultsById.set(runtimeRoute.routeId, runtimeRoute.result);
768
- const eligibleDescriptors = descriptors;
769
- const previousEntries = new Map((previousManifest?.routes ?? []).map((entry) => [entry.routeId, entry]));
770
- const currentRouteIds = new Set(eligibleDescriptors.map((descriptor) => descriptor.routeId));
771
- const selected = [];
772
- const routesToRender = [];
773
- for (let index = 0; index < eligibleDescriptors.length; index += 1) {
774
- const descriptor = eligibleDescriptors[index];
775
- const entry = selectRouteForGeneration(descriptor, previousEntries.get(descriptor.routeId) ?? null, effectiveMode, changedKeys, changedRoutes);
776
- selected.push(entry);
777
- if (entry.reason !== "unchanged") routesToRender.push(entry);
778
- }
779
- const renderStartTime = performance.now();
780
- const renderedResults = routesToRender.length > 0 ? await batchRenderRoutes(getRoutesToRender(routesToRender), {
781
- seed,
782
- dataMap,
783
- concurrency: resolvedConcurrency,
784
- document: options.document,
785
- styleRegistrationValidation: options.styleRegistrationValidation
786
- }) : [];
787
- performance.now() - renderStartTime;
788
- const renderedByRouteId = /* @__PURE__ */ new Map();
789
- for (let index = 0; index < renderedResults.length; index += 1) renderedByRouteId.set(routesToRender[index].descriptor.routeId, renderedResults[index]);
790
- let cacheHits = 0;
791
- const nextManifestRoutes = [];
792
- for (const entry of selected) {
793
- const { descriptor, previous, reason } = entry;
794
- const resolvedData = resolveSsgRouteData(dataMap, descriptor.route.path, descriptor.path);
795
- const baseData = resolvedData.hasData ? resolvedData.data : void 0;
796
- const resourceCount = resolvedData.hasData && baseData ? Object.keys(baseData).length : 0;
797
- if (reason === "unchanged") {
798
- routeResultsById.set(descriptor.routeId, {
791
+ return withOutputDirectoryLock(options.outputDir, async () => {
792
+ const splitRoutes = await splitStaticRoutes(configuredRoutes);
793
+ const routes = splitRoutes.routes;
794
+ validateRoutes(routes);
795
+ const changedKeys = dedupeStrings(generateOptions.changedKeys);
796
+ const changedRoutes = dedupeStrings(generateOptions.changedRoutes);
797
+ const requestedMode = generateOptions.mode ?? "full";
798
+ const previousManifest = !generateOptions.forceFull && requestedMode === "incremental" ? readIncrementalManifest(options.outputDir, seed) : null;
799
+ const effectiveMode = generateOptions.forceFull || requestedMode !== "incremental" || previousManifest === null ? "full" : "incremental";
800
+ const dataMap = resolveSsgData(routes, { dataOverrides: options.dataOverrides });
801
+ const descriptors = routes.map(resolveRouteDescriptor);
802
+ validateOutputPathCollisions(descriptors);
803
+ const routeResultsById = /* @__PURE__ */ new Map();
804
+ for (const runtimeRoute of splitRoutes.runtimeOnly) routeResultsById.set(runtimeRoute.routeId, runtimeRoute.result);
805
+ const eligibleDescriptors = descriptors;
806
+ const previousEntries = new Map((previousManifest?.routes ?? []).map((entry) => [entry.routeId, entry]));
807
+ const currentRouteIds = new Set(eligibleDescriptors.map((descriptor) => descriptor.routeId));
808
+ const selected = [];
809
+ const routesToRender = [];
810
+ for (let index = 0; index < eligibleDescriptors.length; index += 1) {
811
+ const descriptor = eligibleDescriptors[index];
812
+ const entry = selectRouteForGeneration(descriptor, previousEntries.get(descriptor.routeId) ?? null, effectiveMode, changedKeys, changedRoutes);
813
+ selected.push(entry);
814
+ if (entry.reason !== "unchanged") routesToRender.push(entry);
815
+ }
816
+ const renderStartTime = performance.now();
817
+ const renderedResults = routesToRender.length > 0 ? await batchRenderRoutes(getRoutesToRender(routesToRender), {
818
+ seed,
819
+ dataMap,
820
+ concurrency: resolvedConcurrency,
821
+ document: options.document,
822
+ styleRegistrationValidation: options.styleRegistrationValidation
823
+ }) : [];
824
+ performance.now() - renderStartTime;
825
+ const renderedByRouteId = /* @__PURE__ */ new Map();
826
+ for (let index = 0; index < renderedResults.length; index += 1) renderedByRouteId.set(routesToRender[index].descriptor.routeId, renderedResults[index]);
827
+ let cacheHits = 0;
828
+ const nextManifestRoutes = [];
829
+ for (const entry of selected) {
830
+ const { descriptor, previous, reason } = entry;
831
+ const resolvedData = resolveSsgRouteData(dataMap, descriptor.route.path, descriptor.path);
832
+ const baseData = resolvedData.hasData ? resolvedData.data : void 0;
833
+ const resourceCount = resolvedData.hasData && baseData ? Object.keys(baseData).length : 0;
834
+ if (reason === "unchanged") {
835
+ routeResultsById.set(descriptor.routeId, {
836
+ path: descriptor.path,
837
+ filePath: descriptor.filePath,
838
+ html: "",
839
+ fileSize: previous?.htmlHash !== null && previous !== null ? getExistingOutputFileSize(options.outputDir, descriptor.filePath) : 0,
840
+ renderDuration: 0,
841
+ resourceCount,
842
+ status: "skipped",
843
+ reason: "unchanged",
844
+ written: false
845
+ });
846
+ if (previous) nextManifestRoutes.push({
847
+ ...previous,
848
+ path: descriptor.path,
849
+ filePath: descriptor.filePath,
850
+ invalidationKeys: descriptor.invalidationKeys.slice()
851
+ });
852
+ continue;
853
+ }
854
+ const rendered = renderedByRouteId.get(descriptor.routeId);
855
+ if (!rendered) throw new Error(`Missing rendered result for route "${descriptor.path}"`);
856
+ const nextResult = {
857
+ ...rendered,
799
858
  path: descriptor.path,
800
859
  filePath: descriptor.filePath,
801
- html: "",
802
- fileSize: previous?.htmlHash !== null && previous !== null ? getExistingOutputFileSize(options.outputDir, descriptor.filePath) : 0,
803
- renderDuration: 0,
804
860
  resourceCount,
805
- status: "skipped",
806
- reason: "unchanged",
861
+ reason,
807
862
  written: false
808
- });
809
- if (previous) nextManifestRoutes.push({
810
- ...previous,
811
- path: descriptor.path,
812
- filePath: descriptor.filePath,
813
- invalidationKeys: descriptor.invalidationKeys.slice()
814
- });
815
- continue;
816
- }
817
- const rendered = renderedByRouteId.get(descriptor.routeId);
818
- if (!rendered) throw new Error(`Missing rendered result for route "${descriptor.path}"`);
819
- const nextResult = {
820
- ...rendered,
821
- path: descriptor.path,
822
- filePath: descriptor.filePath,
823
- resourceCount,
824
- reason,
825
- written: false
826
- };
827
- if (nextResult.status === "success") {
828
- const htmlDigest = hashHtml(nextResult.html);
829
- const shouldWrite = effectiveMode === "full" || !previous || previous.htmlHash !== htmlDigest || !outputFileExists(options.outputDir, descriptor.filePath);
830
- nextResult.written = shouldWrite;
831
- if (!shouldWrite) cacheHits += 1;
832
- nextManifestRoutes.push({
863
+ };
864
+ if (nextResult.status === "success") {
865
+ const htmlDigest = hashHtml(nextResult.html);
866
+ const shouldWrite = effectiveMode === "full" || !previous || previous.htmlHash !== htmlDigest || !outputFileExists(options.outputDir, descriptor.filePath);
867
+ nextResult.written = shouldWrite;
868
+ if (!shouldWrite) cacheHits += 1;
869
+ nextManifestRoutes.push({
870
+ routeId: descriptor.routeId,
871
+ path: descriptor.path,
872
+ filePath: descriptor.filePath,
873
+ invalidationKeys: descriptor.invalidationKeys.slice(),
874
+ htmlHash: htmlDigest,
875
+ lastStatus: "success"
876
+ });
877
+ } else nextManifestRoutes.push({
833
878
  routeId: descriptor.routeId,
834
879
  path: descriptor.path,
835
880
  filePath: descriptor.filePath,
836
881
  invalidationKeys: descriptor.invalidationKeys.slice(),
837
- htmlHash: htmlDigest,
838
- lastStatus: "success"
882
+ htmlHash: previous?.htmlHash ?? null,
883
+ lastStatus: "error"
839
884
  });
840
- } else nextManifestRoutes.push({
841
- routeId: descriptor.routeId,
842
- path: descriptor.path,
843
- filePath: descriptor.filePath,
844
- invalidationKeys: descriptor.invalidationKeys.slice(),
845
- htmlHash: previous?.htmlHash ?? null,
846
- lastStatus: "error"
847
- });
848
- routeResultsById.set(descriptor.routeId, nextResult);
849
- }
850
- const removedResults = collectRemovedRouteResults(previousManifest, currentRouteIds);
851
- const routeResults = [];
852
- for (const routeId of splitRoutes.outputRouteIds) {
853
- const routeResult = routeResultsById.get(routeId);
854
- if (routeResult) routeResults.push(routeResult);
855
- }
856
- routeResults.push(...removedResults);
857
- result = generateSSGResult(routeResults, {
858
- mode: effectiveMode,
859
- cacheHits,
860
- invalidatedKeys: changedKeys,
861
- invalidatedRoutes: changedRoutes
862
- });
863
- if (effectiveMode === "full" && result.failed > 0) return result;
864
- const targetOutputDir = effectiveMode === "full" ? await createStagingDirectory(options.outputDir) : options.outputDir;
865
- try {
866
- const writeStartTime = performance.now();
867
- await writeStaticFiles(routeResults, targetOutputDir, { concurrency: resolvedConcurrency });
868
- performance.now() - writeStartTime;
869
- await writeMetadata(resultToMetadata(result), targetOutputDir);
870
- await writeIncrementalManifest({
871
- schemaVersion: 1,
872
- seed,
885
+ routeResultsById.set(descriptor.routeId, nextResult);
886
+ }
887
+ const removedResults = collectRemovedRouteResults(previousManifest, currentRouteIds);
888
+ const routeResults = [];
889
+ for (const routeId of splitRoutes.outputRouteIds) {
890
+ const routeResult = routeResultsById.get(routeId);
891
+ if (routeResult) routeResults.push(routeResult);
892
+ }
893
+ routeResults.push(...removedResults);
894
+ result = generateSSGResult(routeResults, {
873
895
  mode: effectiveMode,
874
- routes: nextManifestRoutes
875
- }, targetOutputDir);
876
- await copyStaticAssets(options.assets, targetOutputDir);
877
- if (effectiveMode === "full") await replaceOutputDirectory(targetOutputDir, options.outputDir);
878
- } catch (error) {
879
- if (effectiveMode === "full") await fs.rm(targetOutputDir, {
880
- recursive: true,
881
- force: true
896
+ cacheHits,
897
+ invalidatedKeys: changedKeys,
898
+ invalidatedRoutes: changedRoutes
882
899
  });
883
- throw error;
884
- }
885
- return result;
900
+ if (effectiveMode === "full" && result.failed > 0) {
901
+ for (const routeResult of result.routes) if (routeResult.status === "success") routeResult.written = false;
902
+ return result;
903
+ }
904
+ const targetOutputDir = effectiveMode === "full" ? await createStagingDirectory(options.outputDir) : options.outputDir;
905
+ try {
906
+ const writeStartTime = performance.now();
907
+ await writeStaticFiles(routeResults, targetOutputDir, { concurrency: resolvedConcurrency });
908
+ performance.now() - writeStartTime;
909
+ await writeMetadata(resultToMetadata(result), targetOutputDir);
910
+ await writeIncrementalManifest({
911
+ schemaVersion: 1,
912
+ seed,
913
+ mode: effectiveMode,
914
+ routes: nextManifestRoutes
915
+ }, targetOutputDir);
916
+ await copyStaticAssets(options.assets, targetOutputDir);
917
+ if (effectiveMode === "full") await replaceOutputDirectory(targetOutputDir, options.outputDir);
918
+ } catch (error) {
919
+ if (effectiveMode === "full") await fs.rm(targetOutputDir, {
920
+ recursive: true,
921
+ force: true
922
+ });
923
+ throw error;
924
+ }
925
+ return result;
926
+ });
886
927
  },
887
928
  /**
888
929
  * Get effective config in a serializable form for diagnostics.
@@ -1,4 +1,4 @@
1
- import { At as RouteRegistry, Dt as RouteRecord, E as SSRPortalState, Pt as RouteRequestResult, Rt as CoreTelemetry, T as DeferredBoundaryRegistration, Yt as QueryPrefetchContext, an as DocumentRenderArgs, c as ServerQueryRegistry, cn as SSRData, et as AccessDenyDecision, gt as RouteHandler, ln as SSRStyleRegistration, lt as Route, nt as AccessRedirectDecision, on as DocumentRenderContext, sn as DocumentRenderer, un as SSRStyleRegistrationValidation, ut as RouteAuthOptions, zt as DataRuntime } from "../index-KQ7Vl4-8.js";
1
+ import { At as RouteRegistry, Dt as RouteRecord, E as SSRPortalState, Pt as RouteRequestResult, Rt as CoreTelemetry, T as DeferredBoundaryRegistration, Yt as QueryPrefetchContext, an as DocumentRenderArgs, c as ServerQueryRegistry, cn as SSRData, et as AccessDenyDecision, gt as RouteHandler, ln as SSRStyleRegistration, lt as Route, nt as AccessRedirectDecision, on as DocumentRenderContext, sn as DocumentRenderer, un as SSRStyleRegistrationValidation, ut as RouteAuthOptions, zt as DataRuntime } from "../index-CTIBOqUB.js";
2
2
  import { r as JSXElement, s as Props } from "../types-DkVJhVs9.js";
3
3
  import { AuthContext } from "@askrjs/auth";
4
4
  //#region src/common/page-render-envelope.d.ts
package/dist/ssr/index.js CHANGED
@@ -1,3 +1,3 @@
1
1
  import { C as SSRDataMissingError } from "../render-context-B7ohlh6t.js";
2
- import { a as renderResolvedToStringSync, c as createRenderContext, d as withRenderContextAsync, i as renderRouteRequestToString, l as getRenderContext, n as renderToString, o as renderToStringSync, r as renderRouteRequest, s as resolveRequest, t as renderToStream, u as withRenderContext } from "../ssr-8_vHDWqV.js";
2
+ import { a as renderResolvedToStringSync, c as createRenderContext, d as withRenderContextAsync, i as renderRouteRequestToString, l as getRenderContext, n as renderToString, o as renderToStringSync, r as renderRouteRequest, s as resolveRequest, t as renderToStream, u as withRenderContext } from "../ssr-DYcK20IX.js";
3
3
  export { SSRDataMissingError, createRenderContext, getRenderContext, renderResolvedToStringSync, renderRouteRequest, renderRouteRequestToString, renderToStream, renderToString, renderToStringSync, resolveRequest, withRenderContext, withRenderContextAsync };
@@ -3,18 +3,18 @@ import { t as __ERROR_BOUNDARY__ } from "./vnode-D24p6IW2.js";
3
3
  import { y as logger } from "./access-d_DCdwqn.js";
4
4
  import { f as getCurrentComponentInstance, w as setCurrentComponentInstance } from "./component-scope-4I3yEMkb.js";
5
5
  import { i as isFragmentType, n as Fragment, t as ELEMENT_TYPE } from "./jsx-DRS-qjiN.js";
6
- import { g as markVNodeTreeWithContextFrame, m as getVNodeContextFrame, n as createComponentInstance, p as getExecutionContextFrame, u as callWithContext } from "./component-internal-DOVVMjG7.js";
7
- import { t as cleanupComponent } from "./component-cleanup-hrqSikl2.js";
6
+ import { f as callWithContext, g as getVNodeContextFrame, h as getExecutionContextFrame, n as createComponentInstance, v as markVNodeTreeWithContextFrame } from "./component-internal-D0yE2Ch3.js";
7
+ import { t as cleanupComponent } from "./component-cleanup-DYG80yhG.js";
8
8
  import { p as isPromiseLike } from "./lifecycle-batch-BH0fax4F.js";
9
- import { _ as setControlBoundaryContextFrame, g as evaluateShowState, h as evaluateCaseState, i as evaluateForState, o as rollbackForStateTransaction, r as commitForStateTransaction } from "./for-internal-jrliHZmF.js";
9
+ import { _ as setControlBoundaryContextFrame, g as evaluateShowState, h as evaluateCaseState, i as evaluateForState, o as rollbackForStateTransaction, r as commitForStateTransaction } from "./for-internal-DkBwTG4S.js";
10
10
  import { C as SSRDataMissingError, _ as isEmptyPageRenderEnvelope, f as stopRenderPhase$1, h as createPageRenderEnvelope, t as configureRenderContextProvider, u as startRenderPhase$1, v as isPageRenderEnvelope, x as withHydrationRenderUrl } from "./render-context-B7ohlh6t.js";
11
11
  import { n as getPublicAttributeName, t as sanitizeCssValue } from "./css-BuPaTOfe.js";
12
- import { o as isUnsafeUrlAttribute, r as validateCspNonce, t as CspNonceScope } from "./csp-nonce-BBxq1zq4.js";
12
+ import { o as isUnsafeUrlAttribute, r as validateCspNonce, t as CspNonceScope } from "./csp-nonce-BVybpV1v.js";
13
13
  import { n as SSR_PORTAL_HOST, t as SSR_PORTAL_ANCHOR } from "./portal-w3oQ6oQU.js";
14
14
  import { t as createDataRuntime } from "./data-runtime-JxoRgrrt.js";
15
15
  import { c as withTelemetry, i as dehydrateDataRuntime, t as createQueryPrefetchContext } from "./query-registry-CimB1YeW.js";
16
16
  import { a as resolveRouteFromRoutes, c as removeRouteBasePath, s as normalizeRouteBasePath, t as _resolveRouteMatchFromRoutes } from "./route-matching-Bo6UqG-5.js";
17
- import { a as resolveRouteRequest, d as DEFERRED_BOUNDARY, l as validateRouteHydrationData, o as getRouteHydrationMetadata, p as isDeferred } from "./resolution-D8PgnTzh.js";
17
+ import { a as resolveRouteRequest, d as DEFERRED_BOUNDARY, l as validateRouteHydrationData, o as getRouteHydrationMetadata, p as isDeferred } from "./resolution-BQyo7qNn.js";
18
18
  import { n as disposeRegisteredDefaultPortalScope, r as getDefaultPortalHost } from "./default-portal-runtime-DvMvWJVp.js";
19
19
  import { n as renderDocument, t as SSR_RENDER_DATA_ATTR } from "./ssr-DSHd_Q9K.js";
20
20
  //#region src/ssr/escape.ts
@@ -2,7 +2,7 @@ import { o as getRuntimeRenderer } from "./access-d_DCdwqn.js";
2
2
  import { f as getCurrentComponentInstance, m as getCurrentPortalScope } from "./component-scope-4I3yEMkb.js";
3
3
  import { n as Fragment, t as ELEMENT_TYPE } from "./jsx-DRS-qjiN.js";
4
4
  import { l as notifyReadableReaders, o as markReactivePropsDirtySource, s as markReadableDerivedSubscribersDirty, u as recordReadableRead } from "./readable-BsdGWsDE.js";
5
- import { t as cleanupComponent } from "./component-cleanup-hrqSikl2.js";
5
+ import { t as cleanupComponent } from "./component-cleanup-DYG80yhG.js";
6
6
  import { c as getCurrentLifecycleCommitBatch, u as registerLifecycleTransaction } from "./lifecycle-batch-BH0fax4F.js";
7
7
  import { n as getActiveRenderContext } from "./render-context-B7ohlh6t.js";
8
8
  import { i as createSSRPortalHostToken, n as SSR_PORTAL_HOST, r as createSSRPortalAnchorToken, t as SSR_PORTAL_ANCHOR } from "./portal-w3oQ6oQU.js";
@@ -110,6 +110,16 @@ function Presence({ present, children }) {
110
110
  //#endregion
111
111
  //#region src/runtime/portal.ts
112
112
  const DEFAULT_SSR_PORTAL_KEY = {};
113
+ function setPortalErrorParent(host, owner) {
114
+ if (!host) return;
115
+ if (owner && owner.instance._ownershipGeneration === owner.generation && owner.instance.notifyUpdate !== null) {
116
+ host._portalErrorParent = owner.instance;
117
+ host._portalErrorParentGeneration = owner.generation;
118
+ return;
119
+ }
120
+ host._portalErrorParent = null;
121
+ host._portalErrorParentGeneration = void 0;
122
+ }
113
123
  function getSSRPortalSlot(key) {
114
124
  const context = getActiveRenderContext();
115
125
  if (context?.mode !== "ssr") return null;
@@ -160,6 +170,7 @@ function createSSRPortalAnchor() {
160
170
  }
161
171
  function createPortalSlot() {
162
172
  let currentValue;
173
+ let currentOwner = null;
163
174
  const source = (() => {
164
175
  recordReadableRead(source);
165
176
  return currentValue;
@@ -168,12 +179,17 @@ function createPortalSlot() {
168
179
  read() {
169
180
  return source();
170
181
  },
171
- write(value) {
172
- if (Object.is(currentValue, value)) return;
182
+ write(value, owner) {
183
+ const ownerChanged = currentOwner?.instance !== owner?.instance || currentOwner?.generation !== owner?.generation;
184
+ currentOwner = owner;
185
+ if (Object.is(currentValue, value) && !ownerChanged) return;
173
186
  currentValue = value;
174
187
  markReadableDerivedSubscribersDirty(source);
175
188
  markReactivePropsDirtySource(source);
176
189
  notifyReadableReaders(source);
190
+ },
191
+ getOwner() {
192
+ return currentOwner;
177
193
  }
178
194
  };
179
195
  }
@@ -185,11 +201,17 @@ function definePortal() {
185
201
  function PortalHost() {
186
202
  const serverHost = createSSRPortalHost(ssrPortalKey, false);
187
203
  if (serverHost) return serverHost;
204
+ const host = getCurrentComponentInstance();
205
+ if (host?.fn === PortalHost) setPortalErrorParent(host, slot.getOwner());
188
206
  return slot.read();
189
207
  }
190
208
  PortalHost.render = function PortalRender(props) {
191
209
  if (writeSSRPortal(ssrPortalKey, props.children)) return null;
192
- slot.write(props.children);
210
+ const owner = getCurrentComponentInstance();
211
+ slot.write(props.children, owner ? {
212
+ instance: owner,
213
+ generation: owner._ownershipGeneration
214
+ } : null);
193
215
  return null;
194
216
  };
195
217
  return PortalHost;
@@ -232,7 +254,7 @@ function clearTrackedBatchPortalWrite(transaction) {
232
254
  batch = batch.parent;
233
255
  }
234
256
  }
235
- function createExplicitHostSource(readCount) {
257
+ function createPortalStateSource(readCount) {
236
258
  let source = (() => {
237
259
  recordReadableRead(source);
238
260
  return readCount();
@@ -247,6 +269,14 @@ function notifyExplicitDefaultPortalHostReaders(state) {
247
269
  markReactivePropsDirtySource(state.explicitHostSource);
248
270
  notifyReadableReaders(state.explicitHostSource);
249
271
  }
272
+ function setDefaultPortalOwner(state, owner) {
273
+ if (state.owner?.instance === owner?.instance && state.owner?.generation === owner?.generation) return;
274
+ state.owner = owner;
275
+ state.ownerVersion += 1;
276
+ markReadableDerivedSubscribersDirty(state.ownerSource);
277
+ markReactivePropsDirtySource(state.ownerSource);
278
+ notifyReadableReaders(state.ownerSource);
279
+ }
250
280
  function createDefaultPortalState() {
251
281
  const state = {
252
282
  portal: definePortal(),
@@ -255,9 +285,12 @@ function createDefaultPortalState() {
255
285
  cleanupOwners: /* @__PURE__ */ new WeakSet(),
256
286
  explicitHostOwners: /* @__PURE__ */ new Map(),
257
287
  explicitHostCleanupOwners: /* @__PURE__ */ new WeakSet(),
258
- explicitHostSource: (() => 0)
288
+ explicitHostSource: (() => 0),
289
+ ownerVersion: 0,
290
+ ownerSource: (() => 0)
259
291
  };
260
- state.explicitHostSource = createExplicitHostSource(() => state.explicitHostOwners.size);
292
+ state.explicitHostSource = createPortalStateSource(() => state.explicitHostOwners.size);
293
+ state.ownerSource = createPortalStateSource(() => state.ownerVersion);
261
294
  return state;
262
295
  }
263
296
  function disposeDefaultPortalScope(scope) {
@@ -295,7 +328,7 @@ function detachDefaultPortalHostOutput(state) {
295
328
  }
296
329
  function applyDefaultPortalWrite(state, children, owner) {
297
330
  state.portal.render({ children });
298
- state.owner = owner;
331
+ setDefaultPortalOwner(state, owner);
299
332
  if (isEmptyPortalValue(children)) detachDefaultPortalHostOutput(state);
300
333
  }
301
334
  function isStaleDefaultPortalScope(scope) {
@@ -405,7 +438,7 @@ function applyPendingDefaultPortalValue(scope) {
405
438
  if (!_hasPendingDefaultPortalValue) return;
406
439
  const state = getDefaultPortalState(scope);
407
440
  state.portal.render({ children: _pendingDefaultPortalValue });
408
- state.owner = null;
441
+ setDefaultPortalOwner(state, null);
409
442
  _hasPendingDefaultPortalValue = false;
410
443
  _pendingDefaultPortalValue = void 0;
411
444
  }
@@ -460,7 +493,7 @@ function clearDefaultPortalForInstance(instance) {
460
493
  _hasPendingDefaultPortalValue = false;
461
494
  _pendingDefaultPortalValue = void 0;
462
495
  state.portal.render({ children: void 0 });
463
- state.owner = null;
496
+ setDefaultPortalOwner(state, null);
464
497
  detachDefaultPortalHostOutput(state);
465
498
  }
466
499
  /**
@@ -482,6 +515,8 @@ const DefaultPortal = (() => {
482
515
  if (!(props?.__askrAutoDefaultPortal === true)) registerExplicitDefaultPortalHost(scope, state, owner);
483
516
  else if (readExplicitDefaultPortalHosts(state) > 0) return null;
484
517
  applyPendingDefaultPortalValue(scope);
518
+ state.ownerSource();
519
+ setPortalErrorParent(owner, state.owner);
485
520
  const value = state.portal();
486
521
  return value === void 0 ? null : value;
487
522
  }
@@ -1,4 +1,4 @@
1
- import { At as RouteRegistry, Gt as Query, Ut as Mutation, Zt as QueryStaleReason, nn as ComponentFunction, ut as RouteAuthOptions, vt as RouteMatch, zt as DataRuntime } from "../index-KQ7Vl4-8.js";
1
+ import { At as RouteRegistry, Gt as Query, Ut as Mutation, Zt as QueryStaleReason, nn as ComponentFunction, ut as RouteAuthOptions, vt as RouteMatch, zt as DataRuntime } from "../index-CTIBOqUB.js";
2
2
  //#region src/testing/render.d.ts
3
3
  /** Options for {@link render} and {@link mount}. */
4
4
  interface RenderOptions {
@@ -1,9 +1,9 @@
1
1
  import { r as flushRuntimeScheduler } from "../access-d_DCdwqn.js";
2
2
  import { u as recordReadableRead } from "../readable-BsdGWsDE.js";
3
3
  import { l as addInvalidationListener, t as createDataRuntime } from "../data-runtime-JxoRgrrt.js";
4
- import { a as notifySource, i as normalizeAsyncDataError, r as createReadableSource } from "../data-DYqokZzc.js";
4
+ import { a as notifySource, i as normalizeAsyncDataError, r as createReadableSource } from "../data-Bm8L6JOm.js";
5
5
  import { r as computeRouteActivityMatches } from "../route-matching-Bo6UqG-5.js";
6
- import { a as cleanupApp, r as createSPA, t as createIsland } from "../boot-BsMB_ySP.js";
6
+ import { a as cleanupApp, r as createSPA, t as createIsland } from "../boot-C1wT3Vfs.js";
7
7
  //#region src/testing/render.ts
8
8
  const renderStates = /* @__PURE__ */ new WeakMap();
9
9
  const eventConstructorNames = {
@@ -1,6 +1,6 @@
1
1
  import { g as getHydrationRenderUrl, m as withHydrationVerificationRender } from "./render-context-B7ohlh6t.js";
2
2
  import { t as SSR_RENDER_DATA_ATTR } from "./ssr-DSHd_Q9K.js";
3
- import { a as renderResolvedToStringSync } from "./ssr-8_vHDWqV.js";
3
+ import { a as renderResolvedToStringSync } from "./ssr-DYcK20IX.js";
4
4
  //#region src/ssr/verify-hydration.ts
5
5
  function normalizeHydrationHtml(html) {
6
6
  const template = document.createElement("template");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askrjs/askr",
3
- "version": "0.0.94",
3
+ "version": "0.0.96",
4
4
  "description": "Actor-backed deterministic UI framework",
5
5
  "keywords": [
6
6
  "askr",