@stackframe/stack-shared 2.8.20 → 2.8.22

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 (61) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/dist/config/schema.d.mts +67 -15
  3. package/dist/config/schema.d.ts +67 -15
  4. package/dist/config/schema.js +8 -4
  5. package/dist/config/schema.js.map +1 -1
  6. package/dist/esm/config/schema.js +8 -4
  7. package/dist/esm/config/schema.js.map +1 -1
  8. package/dist/esm/helpers/email-themes.js +45 -0
  9. package/dist/esm/helpers/email-themes.js.map +1 -0
  10. package/dist/esm/interface/admin-interface.js +71 -2
  11. package/dist/esm/interface/admin-interface.js.map +1 -1
  12. package/dist/esm/interface/client-interface.js +1 -1
  13. package/dist/esm/interface/client-interface.js.map +1 -1
  14. package/dist/esm/known-errors.js +13 -8
  15. package/dist/esm/known-errors.js.map +1 -1
  16. package/dist/esm/schema-fields.js +9 -1
  17. package/dist/esm/schema-fields.js.map +1 -1
  18. package/dist/esm/utils/errors.js +3 -0
  19. package/dist/esm/utils/errors.js.map +1 -1
  20. package/dist/esm/utils/esbuild.js +54 -45
  21. package/dist/esm/utils/esbuild.js.map +1 -1
  22. package/dist/esm/utils/json.js +4 -4
  23. package/dist/esm/utils/json.js.map +1 -1
  24. package/dist/esm/utils/react.js +45 -4
  25. package/dist/esm/utils/react.js.map +1 -1
  26. package/dist/helpers/email-themes.d.mts +13 -0
  27. package/dist/helpers/email-themes.d.ts +13 -0
  28. package/dist/helpers/email-themes.js +71 -0
  29. package/dist/helpers/email-themes.js.map +1 -0
  30. package/dist/interface/admin-interface.d.mts +40 -2
  31. package/dist/interface/admin-interface.d.ts +40 -2
  32. package/dist/interface/admin-interface.js +71 -2
  33. package/dist/interface/admin-interface.js.map +1 -1
  34. package/dist/interface/client-interface.js +1 -1
  35. package/dist/interface/client-interface.js.map +1 -1
  36. package/dist/interface/crud/projects.d.mts +7 -7
  37. package/dist/interface/crud/projects.d.ts +7 -7
  38. package/dist/known-errors.d.mts +2 -2
  39. package/dist/known-errors.d.ts +2 -2
  40. package/dist/known-errors.js +13 -8
  41. package/dist/known-errors.js.map +1 -1
  42. package/dist/schema-fields.d.mts +6 -2
  43. package/dist/schema-fields.d.ts +6 -2
  44. package/dist/schema-fields.js +10 -1
  45. package/dist/schema-fields.js.map +1 -1
  46. package/dist/utils/errors.js +3 -0
  47. package/dist/utils/errors.js.map +1 -1
  48. package/dist/utils/esbuild.js +54 -45
  49. package/dist/utils/esbuild.js.map +1 -1
  50. package/dist/utils/json.d.mts +2 -2
  51. package/dist/utils/json.d.ts +2 -2
  52. package/dist/utils/json.js +5 -5
  53. package/dist/utils/json.js.map +1 -1
  54. package/dist/utils/react.d.mts +30 -5
  55. package/dist/utils/react.d.ts +30 -5
  56. package/dist/utils/react.js +49 -5
  57. package/dist/utils/react.js.map +1 -1
  58. package/dist/utils/types.d.mts +3 -2
  59. package/dist/utils/types.d.ts +3 -2
  60. package/dist/utils/types.js.map +1 -1
  61. package/package.json +1 -1
@@ -1,5 +1,10 @@
1
- import React, { MutableRefObject } from 'react';
1
+ import React, { SetStateAction } from 'react';
2
2
 
3
+ declare function componentWrapper<C extends React.ComponentType<any> | keyof React.JSX.IntrinsicElements, ExtraProps extends {} = {}>(displayName: string, render: React.ForwardRefRenderFunction<RefFromComponent<C>, React.ComponentPropsWithRef<C> & ExtraProps>): React.FC<React.ComponentPropsWithRef<C> & ExtraProps & {
4
+ ref?: React.Ref<RefFromComponent<C>> | undefined;
5
+ }>;
6
+ type RefFromComponent<C extends React.ComponentType<any> | keyof React.JSX.IntrinsicElements> = NonNullable<RefFromComponentDistCond<React.ComponentPropsWithRef<C>["ref"]>>;
7
+ type RefFromComponentDistCond<A> = A extends React.RefObject<infer T> ? T : never;
3
8
  declare function forwardRefIfNeeded<T, P = {}>(render: React.ForwardRefRenderFunction<T, P>): React.FC<P & {
4
9
  ref?: React.Ref<T>;
5
10
  }>;
@@ -10,11 +15,31 @@ declare function getNodeText(node: React.ReactNode): string;
10
15
  * You can use this to translate older query- or AsyncResult-based code to new the Suspense system, for example: `if (query.isLoading) suspend();`
11
16
  */
12
17
  declare function suspend(): never;
13
- type InstantStateRef<T> = Readonly<MutableRefObject<T>>;
18
+ declare function mapRef<T, R>(ref: ReadonlyRef<T>, mapper: (value: T) => R): ReadonlyRef<R>;
19
+ type ReadonlyRef<T> = {
20
+ readonly current: T;
21
+ };
22
+ type RefState<T> = ReadonlyRef<T> & {
23
+ set: (updater: SetStateAction<T>) => void;
24
+ };
14
25
  /**
15
- * Like useState, but its value is immediately available.
26
+ * Like useState, but its value is immediately available on refState.current after being set.
27
+ *
28
+ * Like useRef, but setting the value will cause a rerender.
29
+ *
30
+ * Note that useRefState returns a new object every time a rerender happens due to a value change, which is intentional
31
+ * as it allows you to specify it in a dependency array like this:
32
+ *
33
+ * ```tsx
34
+ * useEffect(() => {
35
+ * // do something with refState.current
36
+ * }, [refState]); // instead of refState.current
37
+ * ```
38
+ *
39
+ * If you don't want this, you can wrap the result in a useMemo call.
16
40
  */
17
- declare function useInstantState<T>(initialValue: T): [InstantStateRef<T>, (value: T) => void];
41
+ declare function useRefState<T>(initialValue: T): RefState<T>;
42
+ declare function mapRefState<T, R>(refState: RefState<T>, mapper: (value: T) => R, reverseMapper: (oldT: T, newR: R) => T): RefState<R>;
18
43
  declare class NoSuspenseBoundaryError extends Error {
19
44
  digest: string;
20
45
  reason: string;
@@ -27,4 +52,4 @@ declare class NoSuspenseBoundaryError extends Error {
27
52
  */
28
53
  declare function suspendIfSsr(caller?: string): void;
29
54
 
30
- export { type InstantStateRef, NoSuspenseBoundaryError, forwardRefIfNeeded, getNodeText, suspend, suspendIfSsr, useInstantState };
55
+ export { NoSuspenseBoundaryError, type ReadonlyRef, type RefState, componentWrapper, forwardRefIfNeeded, getNodeText, mapRef, mapRefState, suspend, suspendIfSsr, useRefState };
@@ -1,5 +1,10 @@
1
- import React, { MutableRefObject } from 'react';
1
+ import React, { SetStateAction } from 'react';
2
2
 
3
+ declare function componentWrapper<C extends React.ComponentType<any> | keyof React.JSX.IntrinsicElements, ExtraProps extends {} = {}>(displayName: string, render: React.ForwardRefRenderFunction<RefFromComponent<C>, React.ComponentPropsWithRef<C> & ExtraProps>): React.FC<React.ComponentPropsWithRef<C> & ExtraProps & {
4
+ ref?: React.Ref<RefFromComponent<C>> | undefined;
5
+ }>;
6
+ type RefFromComponent<C extends React.ComponentType<any> | keyof React.JSX.IntrinsicElements> = NonNullable<RefFromComponentDistCond<React.ComponentPropsWithRef<C>["ref"]>>;
7
+ type RefFromComponentDistCond<A> = A extends React.RefObject<infer T> ? T : never;
3
8
  declare function forwardRefIfNeeded<T, P = {}>(render: React.ForwardRefRenderFunction<T, P>): React.FC<P & {
4
9
  ref?: React.Ref<T>;
5
10
  }>;
@@ -10,11 +15,31 @@ declare function getNodeText(node: React.ReactNode): string;
10
15
  * You can use this to translate older query- or AsyncResult-based code to new the Suspense system, for example: `if (query.isLoading) suspend();`
11
16
  */
12
17
  declare function suspend(): never;
13
- type InstantStateRef<T> = Readonly<MutableRefObject<T>>;
18
+ declare function mapRef<T, R>(ref: ReadonlyRef<T>, mapper: (value: T) => R): ReadonlyRef<R>;
19
+ type ReadonlyRef<T> = {
20
+ readonly current: T;
21
+ };
22
+ type RefState<T> = ReadonlyRef<T> & {
23
+ set: (updater: SetStateAction<T>) => void;
24
+ };
14
25
  /**
15
- * Like useState, but its value is immediately available.
26
+ * Like useState, but its value is immediately available on refState.current after being set.
27
+ *
28
+ * Like useRef, but setting the value will cause a rerender.
29
+ *
30
+ * Note that useRefState returns a new object every time a rerender happens due to a value change, which is intentional
31
+ * as it allows you to specify it in a dependency array like this:
32
+ *
33
+ * ```tsx
34
+ * useEffect(() => {
35
+ * // do something with refState.current
36
+ * }, [refState]); // instead of refState.current
37
+ * ```
38
+ *
39
+ * If you don't want this, you can wrap the result in a useMemo call.
16
40
  */
17
- declare function useInstantState<T>(initialValue: T): [InstantStateRef<T>, (value: T) => void];
41
+ declare function useRefState<T>(initialValue: T): RefState<T>;
42
+ declare function mapRefState<T, R>(refState: RefState<T>, mapper: (value: T) => R, reverseMapper: (oldT: T, newR: R) => T): RefState<R>;
18
43
  declare class NoSuspenseBoundaryError extends Error {
19
44
  digest: string;
20
45
  reason: string;
@@ -27,4 +52,4 @@ declare class NoSuspenseBoundaryError extends Error {
27
52
  */
28
53
  declare function suspendIfSsr(caller?: string): void;
29
54
 
30
- export { type InstantStateRef, NoSuspenseBoundaryError, forwardRefIfNeeded, getNodeText, suspend, suspendIfSsr, useInstantState };
55
+ export { NoSuspenseBoundaryError, type ReadonlyRef, type RefState, componentWrapper, forwardRefIfNeeded, getNodeText, mapRef, mapRefState, suspend, suspendIfSsr, useRefState };
@@ -31,17 +31,25 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var react_exports = {};
32
32
  __export(react_exports, {
33
33
  NoSuspenseBoundaryError: () => NoSuspenseBoundaryError,
34
+ componentWrapper: () => componentWrapper,
34
35
  forwardRefIfNeeded: () => forwardRefIfNeeded,
35
36
  getNodeText: () => getNodeText,
37
+ mapRef: () => mapRef,
38
+ mapRefState: () => mapRefState,
36
39
  suspend: () => suspend,
37
40
  suspendIfSsr: () => suspendIfSsr,
38
- useInstantState: () => useInstantState
41
+ useRefState: () => useRefState
39
42
  });
40
43
  module.exports = __toCommonJS(react_exports);
41
44
  var import_react = __toESM(require("react"));
42
45
  var import_env = require("./env.js");
43
46
  var import_promises = require("./promises.js");
44
47
  var import_strings = require("./strings.js");
48
+ function componentWrapper(displayName, render) {
49
+ const Component = forwardRefIfNeeded(render);
50
+ Component.displayName = displayName;
51
+ return Component;
52
+ }
45
53
  function forwardRefIfNeeded(render) {
46
54
  const version = import_react.default.version;
47
55
  const major = parseInt(version.split(".")[0]);
@@ -70,14 +78,47 @@ function suspend() {
70
78
  import_react.default.use((0, import_promises.neverResolve)());
71
79
  throw new Error("Somehow a Promise that never resolves was resolved?");
72
80
  }
73
- function useInstantState(initialValue) {
81
+ function mapRef(ref, mapper) {
82
+ let last = null;
83
+ return {
84
+ get current() {
85
+ const input = ref.current;
86
+ if (last === null || input !== last[0]) {
87
+ last = [input, mapper(input)];
88
+ }
89
+ return last[1];
90
+ }
91
+ };
92
+ }
93
+ function useRefState(initialValue) {
74
94
  const [, setState] = import_react.default.useState(initialValue);
75
95
  const ref = import_react.default.useRef(initialValue);
76
- const setValue = import_react.default.useCallback((value) => {
96
+ const setValue = import_react.default.useCallback((updater) => {
97
+ const value = typeof updater === "function" ? updater(ref.current) : updater;
77
98
  setState(value);
78
99
  ref.current = value;
79
100
  }, []);
80
- return [ref, setValue];
101
+ const res = import_react.default.useMemo(() => ({
102
+ current: ref.current,
103
+ set: setValue
104
+ }), [ref.current, setValue]);
105
+ return res;
106
+ }
107
+ function mapRefState(refState, mapper, reverseMapper) {
108
+ let last = null;
109
+ return {
110
+ get current() {
111
+ const input = refState.current;
112
+ if (last === null || input !== last[0]) {
113
+ last = [input, mapper(input)];
114
+ }
115
+ return last[1];
116
+ },
117
+ set(updater) {
118
+ const value = typeof updater === "function" ? updater(this.current) : updater;
119
+ refState.set(reverseMapper(refState.current, value));
120
+ }
121
+ };
81
122
  }
82
123
  var NoSuspenseBoundaryError = class extends Error {
83
124
  constructor(options) {
@@ -119,10 +160,13 @@ function suspendIfSsr(caller) {
119
160
  // Annotate the CommonJS export names for ESM import in node:
120
161
  0 && (module.exports = {
121
162
  NoSuspenseBoundaryError,
163
+ componentWrapper,
122
164
  forwardRefIfNeeded,
123
165
  getNodeText,
166
+ mapRef,
167
+ mapRefState,
124
168
  suspend,
125
169
  suspendIfSsr,
126
- useInstantState
170
+ useRefState
127
171
  });
128
172
  //# sourceMappingURL=react.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/react.tsx"],"sourcesContent":["import React, { MutableRefObject } from \"react\";\nimport { isBrowserLike } from \"./env\";\nimport { neverResolve } from \"./promises\";\nimport { deindent } from \"./strings\";\n\nexport function forwardRefIfNeeded<T, P = {}>(render: React.ForwardRefRenderFunction<T, P>): React.FC<P & { ref?: React.Ref<T> }> {\n // TODO: when we drop support for react 18, remove this\n\n const version = React.version;\n const major = parseInt(version.split(\".\")[0]);\n if (major < 19) {\n return React.forwardRef<T, P>(render as any) as any;\n } else {\n return ((props: P) => render(props, (props as any).ref)) as any;\n }\n}\nundefined?.test(\"forwardRefIfNeeded\", ({ expect }) => {\n // Mock React.version and React.forwardRef\n const originalVersion = React.version;\n const originalForwardRef = React.forwardRef;\n\n try {\n // Test with React version < 19\n Object.defineProperty(React, 'version', { value: '18.2.0', writable: true });\n\n // Create a render function\n const renderFn = (props: any, ref: any) => null;\n\n // Call forwardRefIfNeeded\n const result = forwardRefIfNeeded(renderFn);\n\n // Verify the function returns something\n expect(result).toBeDefined();\n\n // Test with React version >= 19\n Object.defineProperty(React, 'version', { value: '19.0.0', writable: true });\n\n // Call forwardRefIfNeeded again with React 19\n const result19 = forwardRefIfNeeded(renderFn);\n\n // Verify the function returns something\n expect(result19).toBeDefined();\n } finally {\n // Restore original values\n Object.defineProperty(React, 'version', { value: originalVersion });\n React.forwardRef = originalForwardRef;\n }\n});\n\nexport function getNodeText(node: React.ReactNode): string {\n if ([\"number\", \"string\"].includes(typeof node)) {\n return `${node}`;\n }\n if (!node) {\n return \"\";\n }\n if (Array.isArray(node)) {\n return node.map(getNodeText).join(\"\");\n }\n if (typeof node === \"object\" && \"props\" in node) {\n return getNodeText(node.props.children);\n }\n throw new Error(`Unknown node type: ${typeof node}`);\n}\nundefined?.test(\"getNodeText\", ({ expect }) => {\n // Test with string\n expect(getNodeText(\"hello\")).toBe(\"hello\");\n\n // Test with number\n expect(getNodeText(42)).toBe(\"42\");\n\n // Test with null/undefined\n expect(getNodeText(null)).toBe(\"\");\n expect(getNodeText(undefined)).toBe(\"\");\n\n // Test with array\n expect(getNodeText([\"hello\", \" \", \"world\"])).toBe(\"hello world\");\n expect(getNodeText([1, 2, 3])).toBe(\"123\");\n\n // Test with mixed array\n expect(getNodeText([\"hello\", 42, null])).toBe(\"hello42\");\n\n // Test with React element (mocked)\n const mockElement = {\n props: {\n children: \"child text\"\n }\n } as React.ReactElement;\n expect(getNodeText(mockElement)).toBe(\"child text\");\n\n // Test with nested React elements\n const nestedElement = {\n props: {\n children: {\n props: {\n children: \"nested text\"\n }\n } as React.ReactElement\n }\n } as React.ReactElement;\n expect(getNodeText(nestedElement)).toBe(\"nested text\");\n\n // Test with array of React elements\n const arrayOfElements = [\n { props: { children: \"first\" } } as React.ReactElement,\n { props: { children: \"second\" } } as React.ReactElement\n ];\n expect(getNodeText(arrayOfElements)).toBe(\"firstsecond\");\n});\n\n/**\n * Suspends the currently rendered component indefinitely. Will not unsuspend unless the component rerenders.\n *\n * You can use this to translate older query- or AsyncResult-based code to new the Suspense system, for example: `if (query.isLoading) suspend();`\n */\nexport function suspend(): never {\n React.use(neverResolve());\n throw new Error(\"Somehow a Promise that never resolves was resolved?\");\n}\n\nexport type InstantStateRef<T> = Readonly<MutableRefObject<T>>;\n\n/**\n * Like useState, but its value is immediately available.\n */\nexport function useInstantState<T>(initialValue: T): [InstantStateRef<T>, (value: T) => void] {\n const [, setState] = React.useState(initialValue);\n const ref = React.useRef(initialValue);\n const setValue = React.useCallback((value: T) => {\n setState(value);\n ref.current = value;\n }, []);\n return [ref, setValue];\n}\n\nexport class NoSuspenseBoundaryError extends Error {\n digest: string;\n reason: string;\n\n constructor(options: { caller?: string }) {\n super(deindent`\n ${options.caller ?? \"This code path\"} attempted to display a loading indicator, but didn't find a Suspense boundary above it. Please read the error message below carefully.\n \n The fix depends on which of the 3 scenarios caused it:\n \n 1. You are missing a loading.tsx file in your app directory. Fix it by adding a loading.tsx file in your app directory.\n\n 2. The component is rendered in the root (outermost) layout.tsx or template.tsx file. Next.js does not wrap those files in a Suspense boundary, even if there is a loading.tsx file in the same folder. To fix it, wrap your layout inside a route group like this:\n\n - app\n - - layout.tsx // contains <html> and <body>, alongside providers and other components that don't need ${options.caller ?? \"this code path\"}\n - - loading.tsx // required for suspense\n - - (main)\n - - - layout.tsx // contains the main layout of your app, like a sidebar or a header, and can use ${options.caller ?? \"this code path\"}\n - - - route.tsx // your actual main page\n - - - the rest of your app\n\n For more information on this approach, see Next's documentation on route groups: https://nextjs.org/docs/app/building-your-application/routing/route-groups\n \n 3. You caught this error with try-catch or a custom error boundary. Fix this by rethrowing the error or not catching it in the first place.\n\n See: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout\n\n More information on SSR and Suspense boundaries: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content\n `);\n\n this.name = \"NoSuspenseBoundaryError\";\n this.reason = options.caller ?? \"suspendIfSsr()\";\n\n // set the digest so nextjs doesn't log the error\n // https://github.com/vercel/next.js/blob/d01d6d9c35a8c2725b3d74c1402ab76d4779a6cf/packages/next/src/shared/lib/lazy-dynamic/bailout-to-csr.ts#L14\n this.digest = \"BAILOUT_TO_CLIENT_SIDE_RENDERING\";\n }\n}\nundefined?.test(\"NoSuspenseBoundaryError\", ({ expect }) => {\n // Test with default options\n const defaultError = new NoSuspenseBoundaryError({});\n expect(defaultError.name).toBe(\"NoSuspenseBoundaryError\");\n expect(defaultError.reason).toBe(\"suspendIfSsr()\");\n expect(defaultError.digest).toBe(\"BAILOUT_TO_CLIENT_SIDE_RENDERING\");\n expect(defaultError.message).toContain(\"This code path attempted to display a loading indicator\");\n\n // Test with custom caller\n const customError = new NoSuspenseBoundaryError({ caller: \"CustomComponent\" });\n expect(customError.name).toBe(\"NoSuspenseBoundaryError\");\n expect(customError.reason).toBe(\"CustomComponent\");\n expect(customError.digest).toBe(\"BAILOUT_TO_CLIENT_SIDE_RENDERING\");\n expect(customError.message).toContain(\"CustomComponent attempted to display a loading indicator\");\n\n // Verify error message contains all the necessary information\n expect(customError.message).toContain(\"loading.tsx\");\n expect(customError.message).toContain(\"route groups\");\n expect(customError.message).toContain(\"https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout\");\n});\n\n\n/**\n * Use this in a component or a hook to disable SSR. Should be wrapped in a Suspense boundary, or it will throw an error.\n */\nexport function suspendIfSsr(caller?: string) {\n if (!isBrowserLike()) {\n throw new NoSuspenseBoundaryError({ caller });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,mBAAwC;AACxC,iBAA8B;AAC9B,sBAA6B;AAC7B,qBAAyB;AAElB,SAAS,mBAA8B,QAAoF;AAGhI,QAAM,UAAU,aAAAA,QAAM;AACtB,QAAM,QAAQ,SAAS,QAAQ,MAAM,GAAG,EAAE,CAAC,CAAC;AAC5C,MAAI,QAAQ,IAAI;AACd,WAAO,aAAAA,QAAM,WAAiB,MAAa;AAAA,EAC7C,OAAO;AACL,WAAQ,CAAC,UAAa,OAAO,OAAQ,MAAc,GAAG;AAAA,EACxD;AACF;AAkCO,SAAS,YAAY,MAA+B;AACzD,MAAI,CAAC,UAAU,QAAQ,EAAE,SAAS,OAAO,IAAI,GAAG;AAC9C,WAAO,GAAG,IAAI;AAAA,EAChB;AACA,MAAI,CAAC,MAAM;AACT,WAAO;AAAA,EACT;AACA,MAAI,MAAM,QAAQ,IAAI,GAAG;AACvB,WAAO,KAAK,IAAI,WAAW,EAAE,KAAK,EAAE;AAAA,EACtC;AACA,MAAI,OAAO,SAAS,YAAY,WAAW,MAAM;AAC/C,WAAO,YAAY,KAAK,MAAM,QAAQ;AAAA,EACxC;AACA,QAAM,IAAI,MAAM,sBAAsB,OAAO,IAAI,EAAE;AACrD;AAoDO,SAAS,UAAiB;AAC/B,eAAAA,QAAM,QAAI,8BAAa,CAAC;AACxB,QAAM,IAAI,MAAM,qDAAqD;AACvE;AAOO,SAAS,gBAAmB,cAA2D;AAC5F,QAAM,CAAC,EAAE,QAAQ,IAAI,aAAAA,QAAM,SAAS,YAAY;AAChD,QAAM,MAAM,aAAAA,QAAM,OAAO,YAAY;AACrC,QAAM,WAAW,aAAAA,QAAM,YAAY,CAAC,UAAa;AAC/C,aAAS,KAAK;AACd,QAAI,UAAU;AAAA,EAChB,GAAG,CAAC,CAAC;AACL,SAAO,CAAC,KAAK,QAAQ;AACvB;AAEO,IAAM,0BAAN,cAAsC,MAAM;AAAA,EAIjD,YAAY,SAA8B;AACxC,UAAM;AAAA,QACF,QAAQ,UAAU,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,kHASwE,QAAQ,UAAU,gBAAgB;AAAA;AAAA;AAAA,6GAGvC,QAAQ,UAAU,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAW1I;AAED,SAAK,OAAO;AACZ,SAAK,SAAS,QAAQ,UAAU;AAIhC,SAAK,SAAS;AAAA,EAChB;AACF;AA0BO,SAAS,aAAa,QAAiB;AAC5C,MAAI,KAAC,0BAAc,GAAG;AACpB,UAAM,IAAI,wBAAwB,EAAE,OAAO,CAAC;AAAA,EAC9C;AACF;","names":["React"]}
1
+ {"version":3,"sources":["../../src/utils/react.tsx"],"sourcesContent":["import React, { SetStateAction } from \"react\";\nimport { isBrowserLike } from \"./env\";\nimport { neverResolve } from \"./promises\";\nimport { deindent } from \"./strings\";\n\nexport function componentWrapper<\n C extends React.ComponentType<any> | keyof React.JSX.IntrinsicElements,\n ExtraProps extends {} = {}\n>(displayName: string, render: React.ForwardRefRenderFunction<RefFromComponent<C>, React.ComponentPropsWithRef<C> & ExtraProps>) {\n const Component = forwardRefIfNeeded(render);\n Component.displayName = displayName;\n return Component;\n}\ntype RefFromComponent<C extends React.ComponentType<any> | keyof React.JSX.IntrinsicElements> = NonNullable<RefFromComponentDistCond<React.ComponentPropsWithRef<C>[\"ref\"]>>;\ntype RefFromComponentDistCond<A> = A extends React.RefObject<infer T> ? T : never; // distributive conditional type; see https://www.typescriptlang.org/docs/handbook/2/conditional-types.html#distributive-conditional-types\n\nexport function forwardRefIfNeeded<T, P = {}>(render: React.ForwardRefRenderFunction<T, P>): React.FC<P & { ref?: React.Ref<T> }> {\n // TODO: when we drop support for react 18, remove this\n\n const version = React.version;\n const major = parseInt(version.split(\".\")[0]);\n if (major < 19) {\n return React.forwardRef<T, P>(render as any) as any;\n } else {\n return ((props: P) => render(props, (props as any).ref)) as any;\n }\n}\n\nexport function getNodeText(node: React.ReactNode): string {\n if ([\"number\", \"string\"].includes(typeof node)) {\n return `${node}`;\n }\n if (!node) {\n return \"\";\n }\n if (Array.isArray(node)) {\n return node.map(getNodeText).join(\"\");\n }\n if (typeof node === \"object\" && \"props\" in node) {\n return getNodeText(node.props.children);\n }\n throw new Error(`Unknown node type: ${typeof node}`);\n}\nundefined?.test(\"getNodeText\", ({ expect }) => {\n // Test with string\n expect(getNodeText(\"hello\")).toBe(\"hello\");\n\n // Test with number\n expect(getNodeText(42)).toBe(\"42\");\n\n // Test with null/undefined\n expect(getNodeText(null)).toBe(\"\");\n expect(getNodeText(undefined)).toBe(\"\");\n\n // Test with array\n expect(getNodeText([\"hello\", \" \", \"world\"])).toBe(\"hello world\");\n expect(getNodeText([1, 2, 3])).toBe(\"123\");\n\n // Test with mixed array\n expect(getNodeText([\"hello\", 42, null])).toBe(\"hello42\");\n\n // Test with React element (mocked)\n const mockElement = {\n props: {\n children: \"child text\"\n }\n } as React.ReactElement;\n expect(getNodeText(mockElement)).toBe(\"child text\");\n\n // Test with nested React elements\n const nestedElement = {\n props: {\n children: {\n props: {\n children: \"nested text\"\n }\n } as React.ReactElement\n }\n } as React.ReactElement;\n expect(getNodeText(nestedElement)).toBe(\"nested text\");\n\n // Test with array of React elements\n const arrayOfElements = [\n { props: { children: \"first\" } } as React.ReactElement,\n { props: { children: \"second\" } } as React.ReactElement\n ];\n expect(getNodeText(arrayOfElements)).toBe(\"firstsecond\");\n});\n\n/**\n * Suspends the currently rendered component indefinitely. Will not unsuspend unless the component rerenders.\n *\n * You can use this to translate older query- or AsyncResult-based code to new the Suspense system, for example: `if (query.isLoading) suspend();`\n */\nexport function suspend(): never {\n React.use(neverResolve());\n throw new Error(\"Somehow a Promise that never resolves was resolved?\");\n}\n\nexport function mapRef<T, R>(ref: ReadonlyRef<T>, mapper: (value: T) => R): ReadonlyRef<R> {\n let last: [T, R] | null = null;\n return {\n get current() {\n const input = ref.current;\n if (last === null || input !== last[0]) {\n last = [input, mapper(input)];\n }\n return last[1];\n },\n };\n}\n\nexport type ReadonlyRef<T> = {\n readonly current: T,\n};\n\nexport type RefState<T> = ReadonlyRef<T> & {\n set: (updater: SetStateAction<T>) => void,\n};\n\n/**\n * Like useState, but its value is immediately available on refState.current after being set.\n *\n * Like useRef, but setting the value will cause a rerender.\n *\n * Note that useRefState returns a new object every time a rerender happens due to a value change, which is intentional\n * as it allows you to specify it in a dependency array like this:\n *\n * ```tsx\n * useEffect(() => {\n * // do something with refState.current\n * }, [refState]); // instead of refState.current\n * ```\n *\n * If you don't want this, you can wrap the result in a useMemo call.\n */\nexport function useRefState<T>(initialValue: T): RefState<T> {\n const [, setState] = React.useState(initialValue);\n const ref = React.useRef(initialValue);\n const setValue = React.useCallback((updater: SetStateAction<T>) => {\n const value: T = typeof updater === \"function\" ? (updater as any)(ref.current) : updater;\n setState(value);\n ref.current = value;\n }, []);\n const res = React.useMemo(() => ({\n current: ref.current,\n set: setValue,\n }), [ref.current, setValue]);\n return res;\n}\n\nexport function mapRefState<T, R>(refState: RefState<T>, mapper: (value: T) => R, reverseMapper: (oldT: T, newR: R) => T): RefState<R> {\n let last: [T, R] | null = null;\n return {\n get current() {\n const input = refState.current;\n if (last === null || input !== last[0]) {\n last = [input, mapper(input)];\n }\n return last[1];\n },\n set(updater: SetStateAction<R>) {\n const value: R = typeof updater === \"function\" ? (updater as any)(this.current) : updater;\n refState.set(reverseMapper(refState.current, value));\n },\n };\n}\n\nexport class NoSuspenseBoundaryError extends Error {\n digest: string;\n reason: string;\n\n constructor(options: { caller?: string }) {\n super(deindent`\n ${options.caller ?? \"This code path\"} attempted to display a loading indicator, but didn't find a Suspense boundary above it. Please read the error message below carefully.\n \n The fix depends on which of the 3 scenarios caused it:\n \n 1. You are missing a loading.tsx file in your app directory. Fix it by adding a loading.tsx file in your app directory.\n\n 2. The component is rendered in the root (outermost) layout.tsx or template.tsx file. Next.js does not wrap those files in a Suspense boundary, even if there is a loading.tsx file in the same folder. To fix it, wrap your layout inside a route group like this:\n\n - app\n - - layout.tsx // contains <html> and <body>, alongside providers and other components that don't need ${options.caller ?? \"this code path\"}\n - - loading.tsx // required for suspense\n - - (main)\n - - - layout.tsx // contains the main layout of your app, like a sidebar or a header, and can use ${options.caller ?? \"this code path\"}\n - - - route.tsx // your actual main page\n - - - the rest of your app\n\n For more information on this approach, see Next's documentation on route groups: https://nextjs.org/docs/app/building-your-application/routing/route-groups\n \n 3. You caught this error with try-catch or a custom error boundary. Fix this by rethrowing the error or not catching it in the first place.\n\n See: https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout\n\n More information on SSR and Suspense boundaries: https://react.dev/reference/react/Suspense#providing-a-fallback-for-server-errors-and-client-only-content\n `);\n\n this.name = \"NoSuspenseBoundaryError\";\n this.reason = options.caller ?? \"suspendIfSsr()\";\n\n // set the digest so nextjs doesn't log the error\n // https://github.com/vercel/next.js/blob/d01d6d9c35a8c2725b3d74c1402ab76d4779a6cf/packages/next/src/shared/lib/lazy-dynamic/bailout-to-csr.ts#L14\n this.digest = \"BAILOUT_TO_CLIENT_SIDE_RENDERING\";\n }\n}\nundefined?.test(\"NoSuspenseBoundaryError\", ({ expect }) => {\n // Test with default options\n const defaultError = new NoSuspenseBoundaryError({});\n expect(defaultError.name).toBe(\"NoSuspenseBoundaryError\");\n expect(defaultError.reason).toBe(\"suspendIfSsr()\");\n expect(defaultError.digest).toBe(\"BAILOUT_TO_CLIENT_SIDE_RENDERING\");\n expect(defaultError.message).toContain(\"This code path attempted to display a loading indicator\");\n\n // Test with custom caller\n const customError = new NoSuspenseBoundaryError({ caller: \"CustomComponent\" });\n expect(customError.name).toBe(\"NoSuspenseBoundaryError\");\n expect(customError.reason).toBe(\"CustomComponent\");\n expect(customError.digest).toBe(\"BAILOUT_TO_CLIENT_SIDE_RENDERING\");\n expect(customError.message).toContain(\"CustomComponent attempted to display a loading indicator\");\n\n // Verify error message contains all the necessary information\n expect(customError.message).toContain(\"loading.tsx\");\n expect(customError.message).toContain(\"route groups\");\n expect(customError.message).toContain(\"https://nextjs.org/docs/messages/missing-suspense-with-csr-bailout\");\n});\n\n\n/**\n * Use this in a component or a hook to disable SSR. Should be wrapped in a Suspense boundary, or it will throw an error.\n */\nexport function suspendIfSsr(caller?: string) {\n if (!isBrowserLike()) {\n throw new NoSuspenseBoundaryError({ caller });\n }\n}\n"],"mappings":"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","names":["React"]}
@@ -1,5 +1,6 @@
1
1
  type IsAny<T> = 0 extends (1 & T) ? true : false;
2
- type isNullish<T> = T extends null | undefined ? true : false;
2
+ type IsNever<T> = [T] extends [never] ? true : false;
3
+ type IsNullish<T> = T extends null | undefined ? true : false;
3
4
  type NullishCoalesce<T, U> = T extends null | undefined ? U : T;
4
5
  type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never;
5
6
  /**
@@ -20,4 +21,4 @@ type PrettifyType<T> = T extends object ? {
20
21
  [K in keyof T]: T[K];
21
22
  } & {} : T;
22
23
 
23
- export type { IfAndOnlyIf, IsAny, NullishCoalesce, PrettifyType, UnionToIntersection, isNullish };
24
+ export type { IfAndOnlyIf, IsAny, IsNever, IsNullish, NullishCoalesce, PrettifyType, UnionToIntersection };
@@ -1,5 +1,6 @@
1
1
  type IsAny<T> = 0 extends (1 & T) ? true : false;
2
- type isNullish<T> = T extends null | undefined ? true : false;
2
+ type IsNever<T> = [T] extends [never] ? true : false;
3
+ type IsNullish<T> = T extends null | undefined ? true : false;
3
4
  type NullishCoalesce<T, U> = T extends null | undefined ? U : T;
4
5
  type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never;
5
6
  /**
@@ -20,4 +21,4 @@ type PrettifyType<T> = T extends object ? {
20
21
  [K in keyof T]: T[K];
21
22
  } & {} : T;
22
23
 
23
- export type { IfAndOnlyIf, IsAny, NullishCoalesce, PrettifyType, UnionToIntersection, isNullish };
24
+ export type { IfAndOnlyIf, IsAny, IsNever, IsNullish, NullishCoalesce, PrettifyType, UnionToIntersection };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/types.tsx"],"sourcesContent":["export type IsAny<T> = 0 extends (1 & T) ? true : false;\nexport type isNullish<T> = T extends null | undefined ? true : false;\n\nexport type NullishCoalesce<T, U> = T extends null | undefined ? U : T;\n\n// distributive conditional type magic. See: https://stackoverflow.com/a/50375286\nexport type UnionToIntersection<U> =\n (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never\n\n\n/**\n * A variation of TypeScript's conditionals with slightly different semantics. It is the perfect type for cases where:\n *\n * - If all possible values are contained in `Extends`, then it will be mapped to `Then`.\n * - If all possible values are not contained in `Extends`, then it will be mapped to `Otherwise`.\n * - If some possible values are contained in `Extends` and some are not, then it will be mapped to `Then | Otherwise`.\n *\n * This is different from TypeScript's built-in conditional types (`Value extends Extends ? Then : Otherwise`), which\n * returns `Otherwise` for the third case (causing unsoundness in many real-world cases).\n */\nexport type IfAndOnlyIf<Value, Extends, Then, Otherwise> =\n | (Value extends Extends ? never : Otherwise)\n | (Value & Extends extends never ? never : Then);\n\n\n/**\n * Can be used to prettify a type in the IDE; for example, some complicated intersected types can be flattened into a single type.\n */\nexport type PrettifyType<T> = T extends object ? { [K in keyof T]: T[K] } & {} : T;\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/types.tsx"],"sourcesContent":["export type IsAny<T> = 0 extends (1 & T) ? true : false;\nexport type IsNever<T> = [T] extends [never] ? true : false;\nexport type IsNullish<T> = T extends null | undefined ? true : false;\n\nexport type NullishCoalesce<T, U> = T extends null | undefined ? U : T;\n\n// distributive conditional type magic. See: https://stackoverflow.com/a/50375286\nexport type UnionToIntersection<U> =\n (U extends any ? (x: U) => void : never) extends ((x: infer I) => void) ? I : never\n\n\n/**\n * A variation of TypeScript's conditionals with slightly different semantics. It is the perfect type for cases where:\n *\n * - If all possible values are contained in `Extends`, then it will be mapped to `Then`.\n * - If all possible values are not contained in `Extends`, then it will be mapped to `Otherwise`.\n * - If some possible values are contained in `Extends` and some are not, then it will be mapped to `Then | Otherwise`.\n *\n * This is different from TypeScript's built-in conditional types (`Value extends Extends ? Then : Otherwise`), which\n * returns `Otherwise` for the third case (causing unsoundness in many real-world cases).\n */\nexport type IfAndOnlyIf<Value, Extends, Then, Otherwise> =\n | (Value extends Extends ? never : Otherwise)\n | (Value & Extends extends never ? never : Then);\n\n\n/**\n * Can be used to prettify a type in the IDE; for example, some complicated intersected types can be flattened into a single type.\n */\nexport type PrettifyType<T> = T extends object ? { [K in keyof T]: T[K] } & {} : T;\n"],"mappings":";;;;;;;;;;;;;;;;AAAA;AAAA;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@stackframe/stack-shared",
3
- "version": "2.8.20",
3
+ "version": "2.8.22",
4
4
  "files": [
5
5
  "README.md",
6
6
  "dist",