@rangojs/router 0.10.1 → 0.11.0

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 (38) hide show
  1. package/dist/types/browser/partial-update.d.ts +1 -0
  2. package/dist/types/client-urls/navigation.d.ts +11 -0
  3. package/dist/types/client-urls/revalidate-chain.d.ts +33 -0
  4. package/dist/types/client-urls/types.d.ts +41 -14
  5. package/dist/types/client.d.ts +2 -0
  6. package/dist/types/index.d.ts +1 -1
  7. package/dist/types/index.rsc.d.ts +1 -1
  8. package/dist/types/router/is-action.d.ts +34 -0
  9. package/dist/types/testing/index.d.ts +3 -1
  10. package/dist/types/testing/run-client-revalidate.d.ts +43 -0
  11. package/dist/types/testing/to-url.d.ts +2 -0
  12. package/dist/types/types/handler-context.d.ts +12 -5
  13. package/dist/types/types/index.d.ts +1 -1
  14. package/dist/types/vite/plugins/expose-action-id.d.ts +14 -0
  15. package/dist/vite/index.js +14 -2
  16. package/package.json +1 -1
  17. package/skills/client-urls/SKILL.md +26 -4
  18. package/skills/loader/SKILL.md +1 -0
  19. package/skills/testing/SKILL.md +1 -0
  20. package/skills/typesafety/route-types.md +1 -0
  21. package/src/browser/partial-update.ts +11 -2
  22. package/src/browser/server-action-bridge.ts +9 -2
  23. package/src/client-urls/navigation.ts +40 -42
  24. package/src/client-urls/revalidate-chain.ts +83 -0
  25. package/src/client-urls/types.ts +41 -13
  26. package/src/client.tsx +5 -0
  27. package/src/index.rsc.ts +1 -0
  28. package/src/index.ts +1 -0
  29. package/src/router/is-action.ts +100 -0
  30. package/src/router/revalidation.ts +5 -48
  31. package/src/rsc/server-action.ts +2 -4
  32. package/src/testing/index.ts +4 -1
  33. package/src/testing/run-client-revalidate.ts +108 -0
  34. package/src/testing/run-transition-when.ts +1 -3
  35. package/src/testing/to-url.ts +5 -0
  36. package/src/types/handler-context.ts +13 -5
  37. package/src/types/index.ts +1 -0
  38. package/src/vite/plugins/expose-action-id.ts +29 -1
@@ -541,8 +541,10 @@ export type RevalidateParams<TParams = GenericParams, TEnv = any> = Parameters<
541
541
  /**
542
542
  * A reference to a server action, used by `isAction()` in a revalidate predicate.
543
543
  *
544
- * Either a directly imported action (`import { addToCart }`) or a namespace
545
- * import of an action module (`import * as CartActions`). Matching resolves the
544
+ * Either a directly imported action (`import { addToCart }`), a namespace
545
+ * import of an action module (`import * as CartActions`), an object
546
+ * literal of actions (`{ addToCart, removeFromCart }`), or a grouped
547
+ * namespace (`{ Cart: CartActions, Order: OrderActions }`). Matching resolves the
546
548
  * action's build-injected id (`path#export`) — the same identity the router uses
547
549
  * for `actionId` — so a renamed or moved action breaks at compile time instead
548
550
  * of silently failing to match.
@@ -551,6 +553,9 @@ export type ActionRef =
551
553
  | ((...args: never[]) => unknown)
552
554
  | Record<string, unknown>;
553
555
 
556
+ /** The `isAction()` matcher passed to server and client `revalidate()` predicates. */
557
+ export type IsActionFn = (...actions: ActionRef[]) => boolean;
558
+
554
559
  /**
555
560
  * Revalidation function called during client-side navigation to decide whether
556
561
  * a segment (layout, route, parallel slot, or loader) should be re-rendered.
@@ -643,8 +648,10 @@ export type ShouldRevalidateFn<TParams = GenericParams, TEnv = any> = (args: {
643
648
  /**
644
649
  * Typed, rename-safe action matching. Returns `true` when the action that
645
650
  * triggered this revalidation is one of the given references — or, for a
646
- * namespace import (`import * as CartActions`), any export of that module —
647
- * and `false` otherwise (including plain navigation with no action).
651
+ * namespace import (`import * as CartActions`), object literal
652
+ * (`{ addToCart, removeFromCart }`), or grouped namespaces
653
+ * (`{ Cart: CartActions }`), any of those exports — and `false`
654
+ * otherwise (including plain navigation with no action).
648
655
  *
649
656
  * Called with NO arguments it answers "is this request an action at all?":
650
657
  * `true` for any action, `false` on plain navigation. Use the bare form when
@@ -668,9 +675,10 @@ export type ShouldRevalidateFn<TParams = GenericParams, TEnv = any> = (args: {
668
675
  * revalidate((ctx) => ctx.isAction(addToCart) || undefined); // one action
669
676
  * revalidate((ctx) => ctx.isAction(addToCart, removeFromCart) || undefined); // several
670
677
  * revalidate((ctx) => ctx.isAction(CartActions) || undefined); // any in the module
678
+ * revalidate((ctx) => ctx.isAction({ addToCart, removeFromCart }) || undefined); // object form
671
679
  * ```
672
680
  */
673
- isAction: (...actions: ActionRef[]) => boolean;
681
+ isAction: IsActionFn;
674
682
  /** URL where the action was executed (the page the user was on when they triggered the action). */
675
683
  actionUrl?: URL;
676
684
  /** Return value from the action execution. Can be used to conditionally revalidate based on the action's outcome. */
@@ -38,6 +38,7 @@ export type {
38
38
  RevalidateParams,
39
39
  ShouldRevalidateFn,
40
40
  ActionRef,
41
+ IsActionFn,
41
42
  RouteKeys,
42
43
  ExtractRouteParams,
43
44
  HandlersForRouteMap,
@@ -92,6 +92,27 @@ function isUseServerModule(filePath: string): boolean {
92
92
  }
93
93
  }
94
94
 
95
+ /**
96
+ * Per-reference own `bind` injected next to `$$id`. React's client.browser
97
+ * build carries no server-reference metadata across `.bind()` (the
98
+ * edge/node/server builds install an own `bind` on each reference that
99
+ * does), so `isAction(boundStub)` would silently miss in the browser only.
100
+ * Installing the same per-reference own `bind` here — scoped to the stubs
101
+ * this plugin already wraps, guarded to never override an existing own
102
+ * `bind` — closes that without mutating the global Function.prototype
103
+ * (which would re-wrap once per Vite environment/HMR pass and break
104
+ * native-function detection for co-loaded code). The helper re-installs
105
+ * itself on the bound result so chained binds keep the metadata too.
106
+ */
107
+ export const ACTION_BIND_HELPER_NAME: string = "__rangoActionBind";
108
+ export const ACTION_BIND_HELPER_SOURCE: string = `var ${ACTION_BIND_HELPER_NAME} = function () {
109
+ var bound = Function.prototype.bind.apply(this, arguments);
110
+ if (typeof this.$id === "string") bound.$id = this.$id;
111
+ if (typeof this.$$id === "string") bound.$$id = this.$$id;
112
+ bound.bind = ${ACTION_BIND_HELPER_NAME};
113
+ return bound;
114
+ };`;
115
+
95
116
  function applyServerReferenceWrapping(
96
117
  code: string,
97
118
  s: MagicString,
@@ -126,10 +147,17 @@ function applyServerReferenceWrapping(
126
147
  }
127
148
  }
128
149
 
129
- const replacement = `(function(fn) { fn.$$id = ${finalIdArg}; return fn; })(${fnCall}(${idArg}${rest}))`;
150
+ const replacement =
151
+ `(function(fn) { fn.$$id = ${finalIdArg}; ` +
152
+ `if (!Object.prototype.hasOwnProperty.call(fn, "bind")) fn.bind = ${ACTION_BIND_HELPER_NAME}; ` +
153
+ `return fn; })(${fnCall}(${idArg}${rest}))`;
130
154
  s.overwrite(start, end, replacement);
131
155
  }
132
156
 
157
+ if (hasChanges) {
158
+ s.prepend(`${ACTION_BIND_HELPER_SOURCE}\n`);
159
+ }
160
+
133
161
  return hasChanges;
134
162
  }
135
163