@reckona/mreact-router 0.0.154 → 0.0.156

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reckona/mreact-router",
3
- "version": "0.0.154",
3
+ "version": "0.0.156",
4
4
  "description": "File-system app router, SSR, actions, and deployment adapters for mreact.",
5
5
  "keywords": [
6
6
  "jsx",
@@ -105,20 +105,20 @@
105
105
  },
106
106
  "dependencies": {
107
107
  "typescript": "^6.0.3",
108
- "@reckona/mreact": "0.0.154",
109
- "@reckona/mreact-compat": "0.0.154",
110
- "@reckona/mreact-compiler": "0.0.154",
111
- "@reckona/mreact-devtools": "0.0.154",
112
- "@reckona/mreact-query": "0.0.154",
113
- "@reckona/mreact-reactive-core": "0.0.154",
114
- "@reckona/mreact-reactive-dom": "0.0.154",
115
- "@reckona/mreact-shared": "0.0.154",
116
- "@reckona/mreact-server": "0.0.154"
108
+ "@reckona/mreact-compat": "0.0.156",
109
+ "@reckona/mreact-compiler": "0.0.156",
110
+ "@reckona/mreact-devtools": "0.0.156",
111
+ "@reckona/mreact-reactive-core": "0.0.156",
112
+ "@reckona/mreact-query": "0.0.156",
113
+ "@reckona/mreact": "0.0.156",
114
+ "@reckona/mreact-reactive-dom": "0.0.156",
115
+ "@reckona/mreact-server": "0.0.156",
116
+ "@reckona/mreact-shared": "0.0.156"
117
117
  },
118
118
  "peerDependencies": {
119
119
  "vite": ">=8 <9"
120
120
  },
121
121
  "optionalDependencies": {
122
- "@reckona/mreact-router-native": "0.0.154"
122
+ "@reckona/mreact-router-native": "0.0.156"
123
123
  }
124
124
  }
@@ -5,9 +5,11 @@ import {
5
5
  __MREACT_QUERY_STATE_SCRIPT_ID,
6
6
  createQueryClient,
7
7
  dehydrate,
8
+ installQueryAsyncStorage,
8
9
  isQueryClientScopeUnavailableError,
9
10
  runWithQueryClient,
10
11
  type DehydratedQueryClient,
12
+ type QueryAsyncStorage,
11
13
  type QueryClient,
12
14
  } from "@reckona/mreact-query";
13
15
  import type {
@@ -489,18 +491,92 @@ function injectCloudflareQueryStateScript(html: string, state: DehydratedQueryCl
489
491
  : `${html}${script}`;
490
492
  }
491
493
 
492
- function runWithCloudflareQueryClient<T>(queryClient: QueryClient, fn: () => T): T {
494
+ async function runWithCloudflareQueryClient<T>(
495
+ queryClient: QueryClient,
496
+ fn: () => T,
497
+ ): Promise<Awaited<T>> {
493
498
  try {
494
- return runWithQueryClient(queryClient, fn);
499
+ return await runWithQueryClient(queryClient, fn);
495
500
  } catch (error) {
496
501
  if (isQueryClientScopeUnavailableError(error)) {
497
- return fn();
502
+ installQueryAsyncStorage(cloudflareQueryClientStorage);
503
+ return await runWithSerializedCloudflareQueryClient(queryClient, fn);
498
504
  }
499
505
 
500
506
  throw error;
501
507
  }
502
508
  }
503
509
 
510
+ const cloudflareQueryClientStorage = createCloudflareQueryClientStorage();
511
+ let cloudflareQueryClientFallbackQueue: Promise<void> = Promise.resolve();
512
+
513
+ async function runWithSerializedCloudflareQueryClient<T>(
514
+ queryClient: QueryClient,
515
+ fn: () => T,
516
+ ): Promise<Awaited<T>> {
517
+ const previous = cloudflareQueryClientFallbackQueue;
518
+ let releaseCurrent!: () => void;
519
+ const current = new Promise<void>((resolve) => {
520
+ releaseCurrent = resolve;
521
+ });
522
+ cloudflareQueryClientFallbackQueue = previous.then(() => current, () => current);
523
+
524
+ await previous;
525
+
526
+ try {
527
+ return await runWithQueryClient(queryClient, fn);
528
+ } finally {
529
+ releaseCurrent();
530
+ }
531
+ }
532
+
533
+ function createCloudflareQueryClientStorage(): QueryAsyncStorage<QueryClient> {
534
+ const stores: QueryClient[] = [];
535
+
536
+ return {
537
+ getStore() {
538
+ return stores.at(-1);
539
+ },
540
+ run<TResult>(store: QueryClient, callback: () => TResult): TResult {
541
+ stores.push(store);
542
+ let popSynchronously = true;
543
+
544
+ try {
545
+ const result = callback();
546
+
547
+ if (isPromiseLike(result)) {
548
+ popSynchronously = false;
549
+ return Promise.resolve(result).finally(() => {
550
+ removeCloudflareQueryClientStore(stores, store);
551
+ }) as TResult;
552
+ }
553
+
554
+ return result;
555
+ } finally {
556
+ if (popSynchronously) {
557
+ removeCloudflareQueryClientStore(stores, store);
558
+ }
559
+ }
560
+ },
561
+ };
562
+ }
563
+
564
+ function removeCloudflareQueryClientStore(stores: QueryClient[], store: QueryClient): void {
565
+ const index = stores.lastIndexOf(store);
566
+
567
+ if (index >= 0) {
568
+ stores.splice(index, 1);
569
+ }
570
+ }
571
+
572
+ function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
573
+ return (
574
+ (typeof value === "object" || typeof value === "function") &&
575
+ value !== null &&
576
+ typeof (value as { then?: unknown }).then === "function"
577
+ );
578
+ }
579
+
504
580
  async function dispatchCloudflareServerRoute<Env>(
505
581
  module: CloudflareServerRouteModule<Env>,
506
582
  request: Request,
@@ -342,6 +342,28 @@ export function resolveCompatVendorEntryFiles(resolveDir?: string): Map<string,
342
342
  return files;
343
343
  }
344
344
 
345
+ const compatVendorPlaceholderImportPattern =
346
+ /(["'])mreact-compat-vendor:([\w-]+)\1/gu;
347
+
348
+ export function rewriteCompatVendorPlaceholderImportsForRunner(
349
+ code: string,
350
+ resolveDir?: string,
351
+ ): string {
352
+ if (!code.includes(COMPAT_VENDOR_PLACEHOLDER_PREFIX)) {
353
+ return code;
354
+ }
355
+ const entryFiles = resolveCompatVendorEntryFiles(resolveDir);
356
+
357
+ return code.replace(
358
+ compatVendorPlaceholderImportPattern,
359
+ (source, quote: string, entry: string) => {
360
+ const file = entryFiles.get(entry);
361
+
362
+ return file === undefined ? source : `${quote}${pathToFileURL(file).href}${quote}`;
363
+ },
364
+ );
365
+ }
366
+
345
367
  // Marks every compat-family import as external with a deterministic
346
368
  // placeholder specifier; writeServerModuleArtifactFiles later rewrites the
347
369
  // placeholders to relative paths into the emitted shared vendor chunks.
package/src/render.ts CHANGED
@@ -64,6 +64,7 @@ import {
64
64
  importAppRouterFileModule,
65
65
  importAppRouterSourceModule,
66
66
  fileImportMetaUrlPlugin,
67
+ rewriteCompatVendorPlaceholderImportsForRunner,
67
68
  } from "./module-runner.js";
68
69
  import { bytesResponse, htmlResponse } from "./http.js";
69
70
  import { isNotFoundError, isRedirectError, rewriteLocation } from "./navigation.js";
@@ -2952,7 +2953,8 @@ async function loadServerModule(
2952
2953
  serverModuleCacheVersion,
2953
2954
  });
2954
2955
  }
2955
- const moduleCode = prebuiltCode ?? code;
2956
+ const moduleCode =
2957
+ prebuiltCode === undefined ? code : rewriteCompatVendorPlaceholderImportsForRunner(prebuiltCode);
2956
2958
  const cacheKey =
2957
2959
  serverModuleCacheVersion === undefined
2958
2960
  ? undefined
@@ -3604,7 +3606,8 @@ async function loadServerStreamModule(
3604
3606
  serverModuleCacheVersion,
3605
3607
  });
3606
3608
  }
3607
- const moduleCode = prebuiltCode ?? code;
3609
+ const moduleCode =
3610
+ prebuiltCode === undefined ? code : rewriteCompatVendorPlaceholderImportsForRunner(prebuiltCode);
3608
3611
  const cacheKey =
3609
3612
  serverModuleCacheVersion === undefined
3610
3613
  ? undefined