jazz-tools 0.18.19 → 0.18.21

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/.svelte-kit/__package__/Provider.svelte +61 -53
  2. package/.svelte-kit/__package__/Provider.svelte.d.ts.map +1 -1
  3. package/.turbo/turbo-build.log +60 -62
  4. package/CHANGELOG.md +25 -0
  5. package/dist/browser/index.js +2 -2
  6. package/dist/browser/index.js.map +1 -1
  7. package/dist/chunk-BOMSRY5H.js +26 -0
  8. package/dist/chunk-BOMSRY5H.js.map +1 -0
  9. package/dist/{chunk-RN3Y24WX.js → chunk-D5L6ES2M.js} +51 -6
  10. package/dist/chunk-D5L6ES2M.js.map +1 -0
  11. package/dist/chunk-PZ5AY32C.js +10 -0
  12. package/dist/chunk-PZ5AY32C.js.map +1 -0
  13. package/dist/index.js +6 -1
  14. package/dist/index.js.map +1 -1
  15. package/dist/react/ssr.d.ts +1 -3
  16. package/dist/react/ssr.d.ts.map +1 -1
  17. package/dist/react/ssr.js +3 -1
  18. package/dist/react/ssr.js.map +1 -1
  19. package/dist/react-core/hooks.d.ts.map +1 -1
  20. package/dist/react-core/index.js +2 -2
  21. package/dist/react-core/index.js.map +1 -1
  22. package/dist/svelte/Provider.svelte +61 -53
  23. package/dist/svelte/Provider.svelte.d.ts.map +1 -1
  24. package/dist/testing.js +2 -1
  25. package/dist/testing.js.map +1 -1
  26. package/dist/tools/coValues/coList.d.ts.map +1 -1
  27. package/dist/tools/coValues/coPlainText.d.ts.map +1 -1
  28. package/dist/tools/exports.d.ts +1 -0
  29. package/dist/tools/exports.d.ts.map +1 -1
  30. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts +1 -1
  31. package/dist/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.d.ts.map +1 -1
  32. package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts +1 -1
  33. package/dist/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.d.ts.map +1 -1
  34. package/dist/tools/implementation/zodSchema/zodReExport.d.ts +1 -1
  35. package/dist/tools/implementation/zodSchema/zodReExport.d.ts.map +1 -1
  36. package/dist/tools/ssr/index.d.ts +2 -0
  37. package/dist/tools/ssr/index.d.ts.map +1 -0
  38. package/dist/tools/ssr/ssr.d.ts +4 -0
  39. package/dist/tools/ssr/ssr.d.ts.map +1 -0
  40. package/dist/tools/ssr.js +8 -0
  41. package/dist/tools/ssr.js.map +1 -0
  42. package/package.json +15 -10
  43. package/src/browser/auth/PasskeyAuth.ts +2 -2
  44. package/src/react/ssr.ts +2 -23
  45. package/src/react-core/hooks.ts +3 -2
  46. package/src/react-core/tests/useInboxSender.test.ts +37 -3
  47. package/src/svelte/Provider.svelte +61 -53
  48. package/src/tools/coValues/coList.ts +11 -0
  49. package/src/tools/coValues/coPlainText.ts +9 -0
  50. package/src/tools/exports.ts +2 -0
  51. package/src/tools/implementation/zodSchema/runtimeConverters/schemaFieldToCoFieldDef.ts +47 -2
  52. package/src/tools/implementation/zodSchema/typeConverters/TypeOfZodSchema.ts +9 -4
  53. package/src/tools/implementation/zodSchema/zodReExport.ts +1 -0
  54. package/src/tools/ssr/index.ts +1 -0
  55. package/src/tools/ssr/ssr.ts +23 -0
  56. package/src/tools/tests/coList.test.ts +34 -0
  57. package/src/tools/tests/coPlainText.test.ts +25 -1
  58. package/src/tools/tests/zod.test.ts +131 -0
  59. package/tsup.config.ts +2 -7
  60. package/dist/chunk-RN3Y24WX.js.map +0 -1
  61. package/jazz-tools-0.18.6.tgz +0 -0
@@ -1,59 +1,67 @@
1
- <script lang="ts" generics="S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema">
2
- import type { AuthSecretStorage, InstanceOfSchema } from "jazz-tools";
3
- import {
4
- Account,
5
- type AccountClass,
6
- type AnyAccountSchema,
7
- type CoValueFromRaw,
8
- } from "jazz-tools";
9
- import {
10
- JazzBrowserContextManager,
11
- type JazzContextManagerProps,
12
- } from "jazz-tools/browser";
13
- import { type Snippet, setContext, untrack } from "svelte";
14
- import { JAZZ_AUTH_CTX, JAZZ_CTX, type JazzContext } from "./jazz.svelte.js";
15
-
16
- let props: JazzContextManagerProps<S> & { children?: Snippet } = $props();
17
-
18
- const contextManager = new JazzBrowserContextManager<S>();
19
-
20
- const ctx = $state<JazzContext<InstanceOfSchema<S>>>({ current: undefined });
21
-
22
- setContext<JazzContext<InstanceOfSchema<S>>>(JAZZ_CTX, ctx);
23
- setContext<AuthSecretStorage>(
24
- JAZZ_AUTH_CTX,
25
- contextManager.getAuthSecretStorage(),
26
- );
27
-
28
- $effect(() => {
29
- props.sync.when;
30
- props.sync.peer;
31
- props.storage;
32
- props.guestMode;
33
- return untrack(() => {
34
- if (!props.sync) return;
35
-
36
- contextManager
37
- .createContext({
38
- sync: props.sync,
39
- storage: props.storage,
40
- guestMode: props.guestMode,
41
- AccountSchema: props.AccountSchema,
42
- defaultProfileName: props.defaultProfileName,
43
- onAnonymousAccountDiscarded: props.onAnonymousAccountDiscarded,
44
- onLogOut: props.onLogOut,
45
- })
46
- .catch((error) => {
47
- console.error("Error creating Jazz browser context:", error);
48
- });
1
+ <script
2
+ lang="ts"
3
+ generics="S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema"
4
+ >
5
+ import type { AuthSecretStorage, InstanceOfSchema } from "jazz-tools";
6
+ import {
7
+ Account,
8
+ type AccountClass,
9
+ type AnyAccountSchema,
10
+ type CoValueFromRaw,
11
+ } from "jazz-tools";
12
+ import {
13
+ JazzBrowserContextManager,
14
+ type JazzContextManagerProps,
15
+ } from "jazz-tools/browser";
16
+ import { type Snippet, setContext, untrack } from "svelte";
17
+ import { JAZZ_AUTH_CTX, JAZZ_CTX, type JazzContext } from "./jazz.svelte.js";
18
+
19
+ let props: JazzContextManagerProps<S> & {
20
+ children?: Snippet;
21
+ enableSSR?: boolean;
22
+ } = $props();
23
+
24
+ const contextManager = new JazzBrowserContextManager<S>({
25
+ useAnonymousFallback: props.enableSSR,
26
+ });
27
+
28
+ const ctx = $state<JazzContext<InstanceOfSchema<S>>>({ current: undefined });
29
+
30
+ setContext<JazzContext<InstanceOfSchema<S>>>(JAZZ_CTX, ctx);
31
+ setContext<AuthSecretStorage>(
32
+ JAZZ_AUTH_CTX,
33
+ contextManager.getAuthSecretStorage(),
34
+ );
35
+
36
+ $effect(() => {
37
+ props.sync.when;
38
+ props.sync.peer;
39
+ props.storage;
40
+ props.guestMode;
41
+ return untrack(() => {
42
+ if (!props.sync) return;
43
+
44
+ contextManager
45
+ .createContext({
46
+ sync: props.sync,
47
+ storage: props.storage,
48
+ guestMode: props.guestMode,
49
+ AccountSchema: props.AccountSchema,
50
+ defaultProfileName: props.defaultProfileName,
51
+ onAnonymousAccountDiscarded: props.onAnonymousAccountDiscarded,
52
+ onLogOut: props.onLogOut,
53
+ })
54
+ .catch((error) => {
55
+ console.error("Error creating Jazz browser context:", error);
56
+ });
57
+ });
49
58
  });
50
- });
51
59
 
52
- $effect(() => {
53
- return contextManager.subscribe(() => {
54
- ctx.current = contextManager.getCurrentValue();
60
+ $effect(() => {
61
+ return contextManager.subscribe(() => {
62
+ ctx.current = contextManager.getCurrentValue();
63
+ });
55
64
  });
56
- });
57
65
  </script>
58
66
 
59
67
  {#if ctx.current}
@@ -739,9 +739,20 @@ export class CoListJazzApi<L extends CoList> extends CoValueJazzApi<L> {
739
739
  : undefined;
740
740
 
741
741
  const patches = [...calcPatch(current, result, comparator)];
742
+
743
+ if (patches.length === 0) {
744
+ return this.coList;
745
+ }
746
+
747
+ // Turns off updates in the middle of applyDiff to improve the performance
748
+ this.raw.core.pauseNotifyUpdate();
749
+
742
750
  for (const [from, to, insert] of patches.reverse()) {
743
751
  this.splice(from, to - from, ...insert);
744
752
  }
753
+
754
+ this.raw.core.resumeNotifyUpdate();
755
+
745
756
  return this.coList;
746
757
  }
747
758
 
@@ -246,6 +246,13 @@ export class CoTextJazzApi<T extends CoPlainText> extends CoValueJazzApi<T> {
246
246
  // Calculate the diff on grapheme arrays
247
247
  const patches = [...calcPatch(currentGraphemes, otherGraphemes)];
248
248
 
249
+ if (patches.length === 0) {
250
+ return;
251
+ }
252
+
253
+ // Turns off updates in the middle of applyDiff to improve the performance
254
+ this.raw.core.pauseNotifyUpdate();
255
+
249
256
  // Apply patches in reverse order to avoid index shifting issues
250
257
  for (const [from, to, insert] of patches.reverse()) {
251
258
  if (to > from) {
@@ -256,6 +263,8 @@ export class CoTextJazzApi<T extends CoPlainText> extends CoValueJazzApi<T> {
256
263
  this.coText.insertBefore(from, this.raw.fromGraphemes(insert));
257
264
  }
258
265
  }
266
+
267
+ this.raw.core.resumeNotifyUpdate();
259
268
  }
260
269
 
261
270
  /**
@@ -118,3 +118,5 @@ export {
118
118
  isJazzRequestError,
119
119
  type HttpRoute,
120
120
  } from "./coValues/request.js";
121
+
122
+ export * from "./ssr/index.js";
@@ -1,3 +1,4 @@
1
+ import type { JsonValue } from "cojson";
1
2
  import { CoValueClass, isCoValueClass } from "../../../internal.js";
2
3
  import { coField } from "../../schema.js";
3
4
  import { CoreCoValueSchema } from "../schemaTypes/CoValueSchema.js";
@@ -32,11 +33,31 @@ export type SchemaField =
32
33
  | z.core.$ZodLazy<z.core.$ZodType>
33
34
  | z.core.$ZodTemplateLiteral<any>
34
35
  | z.core.$ZodLiteral<any>
35
- | z.core.$ZodCatch<z.core.$ZodType>
36
36
  | z.core.$ZodEnum<any>
37
+ | z.core.$ZodCodec<z.core.$ZodType, z.core.$ZodType>
37
38
  | z.core.$ZodDefault<z.core.$ZodType>
38
39
  | z.core.$ZodCatch<z.core.$ZodType>;
39
40
 
41
+ function makeCodecCoField(
42
+ codec: z.core.$ZodCodec<z.core.$ZodType, z.core.$ZodType>,
43
+ ) {
44
+ return coField.optional.encoded({
45
+ encode: (value: any) => {
46
+ if (value === undefined) return undefined as unknown as JsonValue;
47
+ if (value === null) return null;
48
+ return codec._zod.def.reverseTransform(value, {
49
+ value,
50
+ issues: [],
51
+ }) as JsonValue;
52
+ },
53
+ decode: (value) => {
54
+ if (value === null) return null;
55
+ if (value === undefined) return undefined;
56
+ return codec._zod.def.transform(value, { value, issues: [] });
57
+ },
58
+ });
59
+ }
60
+
40
61
  export function schemaFieldToCoFieldDef(schema: SchemaField) {
41
62
  if (isCoValueClass(schema)) {
42
63
  return coField.ref(schema);
@@ -54,7 +75,7 @@ export function schemaFieldToCoFieldDef(schema: SchemaField) {
54
75
  zodSchemaDef.type === "optional" ||
55
76
  zodSchemaDef.type === "nullable"
56
77
  ) {
57
- const inner = zodSchemaDef.innerType as ZodPrimitiveSchema;
78
+ const inner = zodSchemaDef.innerType as SchemaField;
58
79
  const coFieldDef: any = schemaFieldToCoFieldDef(inner);
59
80
  if (
60
81
  zodSchemaDef.type === "nullable" &&
@@ -137,6 +158,30 @@ export function schemaFieldToCoFieldDef(schema: SchemaField) {
137
158
  "z.union()/z.discriminatedUnion() of collaborative types is not supported. Use co.discriminatedUnion() instead.",
138
159
  );
139
160
  }
161
+ } else if (zodSchemaDef.type === "pipe") {
162
+ const isCodec =
163
+ zodSchemaDef.transform !== undefined &&
164
+ zodSchemaDef.reverseTransform !== undefined;
165
+
166
+ if (!isCodec) {
167
+ throw new Error(
168
+ "z.pipe() is not supported. Only z.codec() is supported.",
169
+ );
170
+ }
171
+
172
+ try {
173
+ schemaFieldToCoFieldDef(zodSchemaDef.in as SchemaField);
174
+ } catch (error) {
175
+ if (error instanceof Error) {
176
+ error.message = `z.codec() is only supported if the input schema is already supported. ${error.message}`;
177
+ }
178
+
179
+ throw error;
180
+ }
181
+
182
+ return makeCodecCoField(
183
+ schema as z.core.$ZodCodec<z.core.$ZodType, z.core.$ZodType>,
184
+ );
140
185
  } else {
141
186
  throw new Error(
142
187
  `Unsupported zod type: ${(schema._zod?.def as any)?.type || JSON.stringify(schema)}`,
@@ -71,8 +71,13 @@ export type TypeOfZodSchema<S extends z.core.$ZodType> =
71
71
  infer Default extends z.core.$ZodType
72
72
  >
73
73
  ? TypeOfZodSchema<Default>
74
- : S extends z.core.$ZodCatch<
75
- infer Catch extends z.core.$ZodType
74
+ : S extends z.core.$ZodCodec<
75
+ any,
76
+ infer Out extends z.core.$ZodType
76
77
  >
77
- ? TypeOfZodSchema<Catch>
78
- : never;
78
+ ? Out["_zod"]["output"]
79
+ : S extends z.core.$ZodCatch<
80
+ infer Catch extends z.core.$ZodType
81
+ >
82
+ ? TypeOfZodSchema<Catch>
83
+ : never;
@@ -35,6 +35,7 @@ export {
35
35
  // record,
36
36
  // intersection,
37
37
  int,
38
+ codec,
38
39
  optional,
39
40
  array,
40
41
  tuple,
@@ -0,0 +1 @@
1
+ export * from "./ssr.js";
@@ -0,0 +1,23 @@
1
+ import { WebSocketPeerWithReconnection } from "cojson-transport-ws";
2
+ import { PureJSCrypto } from "cojson/dist/crypto/PureJSCrypto";
3
+ import { createAnonymousJazzContext } from "jazz-tools";
4
+
5
+ export function createSSRJazzAgent(opts: { peer: string }) {
6
+ const ssrNode = createAnonymousJazzContext({
7
+ crypto: new PureJSCrypto(),
8
+ peersToLoadFrom: [],
9
+ });
10
+
11
+ const wsPeer = new WebSocketPeerWithReconnection({
12
+ peer: opts.peer,
13
+ reconnectionTimeout: 100,
14
+ addPeer: (peer) => {
15
+ ssrNode.agent.node.syncManager.addPeer(peer);
16
+ },
17
+ removePeer: () => {},
18
+ });
19
+
20
+ wsPeer.enable();
21
+
22
+ return ssrNode.agent;
23
+ }
@@ -680,6 +680,40 @@ describe("CoList applyDiff operations", async () => {
680
680
  list.$jazz.applyDiff(["e", "c", "new", "y", "x"]);
681
681
  expect(list.$jazz.raw.asArray()).toEqual(["e", "c", "new", "y", "x"]);
682
682
  });
683
+
684
+ test("applyDiff should emit a single update", () => {
685
+ const TestMap = co.map({
686
+ type: z.string(),
687
+ });
688
+
689
+ const TestList = co.list(TestMap);
690
+
691
+ const bread = TestMap.create({ type: "bread" }, me);
692
+ const butter = TestMap.create({ type: "butter" }, me);
693
+ const onion = TestMap.create({ type: "onion" }, me);
694
+
695
+ const list = TestList.create([bread, butter, onion], me);
696
+
697
+ const updateFn = vi.fn();
698
+
699
+ const unsubscribe = TestList.subscribe(
700
+ list.$jazz.id,
701
+ {
702
+ resolve: {
703
+ $each: true,
704
+ },
705
+ },
706
+ updateFn,
707
+ );
708
+
709
+ updateFn.mockClear();
710
+
711
+ list.$jazz.applyDiff([bread]);
712
+
713
+ expect(updateFn).toHaveBeenCalledTimes(1);
714
+
715
+ unsubscribe();
716
+ });
683
717
  });
684
718
 
685
719
  describe("CoList resolution", async () => {
@@ -1,6 +1,6 @@
1
1
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
2
  import { Channel } from "queueueue";
3
- import { describe, expect, test } from "vitest";
3
+ import { describe, expect, test, vi } from "vitest";
4
4
  import {
5
5
  Account,
6
6
  cojsonInternals,
@@ -94,6 +94,30 @@ describe("CoPlainText", () => {
94
94
  text.$jazz.applyDiff(`😊👋 안녕!`);
95
95
  expect(text.toString()).toEqual(`😊👋 안녕!`);
96
96
  });
97
+
98
+ test("applyDiff should emit a single update", () => {
99
+ const Text = co.plainText();
100
+
101
+ const text = Text.create(`😊`, { owner: me });
102
+
103
+ const updateFn = vi.fn();
104
+
105
+ const unsubscribe = Text.subscribe(
106
+ text.$jazz.id,
107
+ {
108
+ loadAs: me,
109
+ },
110
+ updateFn,
111
+ );
112
+
113
+ updateFn.mockClear();
114
+
115
+ text.$jazz.applyDiff(`😊👋 안녕!`);
116
+
117
+ expect(updateFn).toHaveBeenCalledTimes(1);
118
+
119
+ unsubscribe();
120
+ });
97
121
  });
98
122
 
99
123
  describe("Properties", () => {
@@ -511,6 +511,137 @@ describe("co.map and Zod schema compatibility", () => {
511
511
  expect(map.readonly).toEqual({ name: "John" });
512
512
  });
513
513
  });
514
+
515
+ describe("Codec types", () => {
516
+ class DateRange {
517
+ constructor(
518
+ public start: Date,
519
+ public end: Date,
520
+ ) {}
521
+
522
+ isDateInRange(date: Date) {
523
+ return date >= this.start && date <= this.end;
524
+ }
525
+ }
526
+
527
+ const dateRangeCodec = z.codec(
528
+ z.tuple([z.string(), z.string()]),
529
+ z.z.instanceof(DateRange),
530
+ {
531
+ encode: (value) =>
532
+ [value.start.toISOString(), value.end.toISOString()] as [
533
+ string,
534
+ string,
535
+ ],
536
+ decode: ([start, end]) => {
537
+ return new DateRange(new Date(start), new Date(end));
538
+ },
539
+ },
540
+ );
541
+
542
+ it("should handle codec field", async () => {
543
+ const schema = co.map({
544
+ range: dateRangeCodec,
545
+ });
546
+
547
+ const map = schema.create({
548
+ range: new DateRange(new Date("2025-01-01"), new Date("2025-01-31")),
549
+ });
550
+
551
+ expect(map.range.isDateInRange(new Date("2025-01-15"))).toEqual(true);
552
+ });
553
+
554
+ it("should handle codec field with RegExp", async () => {
555
+ const schema = co.map({
556
+ regexp: z.codec(z.string(), z.z.instanceof(RegExp), {
557
+ encode: (value) => value.toString(),
558
+ decode: (value) => {
559
+ const [, pattern, flags] = value.match(/^\/(.*)\/([a-z]*)$/i)!;
560
+ if (!pattern) throw new Error("Invalid RegExp string");
561
+ return new RegExp(pattern, flags);
562
+ },
563
+ }),
564
+ });
565
+
566
+ const map = schema.create({
567
+ regexp: /^\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/,
568
+ });
569
+
570
+ expect(map.regexp.test("2001-01-31")).toEqual(true);
571
+ });
572
+
573
+ it("should handle optional codec field", async () => {
574
+ const schema = co.map({
575
+ range: dateRangeCodec.optional(),
576
+ });
577
+ const map = schema.create({});
578
+
579
+ expect(map.range).toBeUndefined();
580
+ expect(map.$jazz.has("range")).toEqual(false);
581
+ });
582
+
583
+ it("should handle nullable codec field", async () => {
584
+ const schema = co.map({
585
+ range: dateRangeCodec.nullable(),
586
+ });
587
+ const map = schema.create({ range: null });
588
+
589
+ expect(map.range).toBeNull();
590
+
591
+ map.$jazz.set(
592
+ "range",
593
+ new DateRange(new Date("2025-01-01"), new Date("2025-01-31")),
594
+ );
595
+ expect(map.range?.isDateInRange(new Date("2025-01-15"))).toEqual(true);
596
+ });
597
+
598
+ it("should handle nullish codec field", async () => {
599
+ const schema = co.map({
600
+ range: dateRangeCodec.nullish(),
601
+ });
602
+
603
+ const map = schema.create({});
604
+ expect(map.range).toBeUndefined();
605
+ expect(map.$jazz.has("range")).toEqual(false);
606
+
607
+ map.$jazz.set("range", undefined);
608
+ expect(map.range).toBeUndefined();
609
+ expect(map.$jazz.has("range")).toEqual(true);
610
+
611
+ map.$jazz.set("range", null);
612
+ expect(map.range).toBeNull();
613
+
614
+ map.$jazz.set(
615
+ "range",
616
+ new DateRange(new Date("2025-01-01"), new Date("2025-01-31")),
617
+ );
618
+ expect(map.range?.isDateInRange(new Date("2025-01-15"))).toEqual(true);
619
+ });
620
+
621
+ it("should not handle codec field with unsupported inner field", async () => {
622
+ const schema = co.map({
623
+ record: z.codec(
624
+ z.z.map(z.string(), z.string()),
625
+ z.z.record(z.string(), z.string()),
626
+ {
627
+ encode: (value) => new Map(Object.entries(value)),
628
+ decode: (value) => Object.fromEntries(value.entries()),
629
+ },
630
+ ),
631
+ });
632
+
633
+ expect(() =>
634
+ schema.create({
635
+ record: {
636
+ key1: "value1",
637
+ key2: "value2",
638
+ },
639
+ }),
640
+ ).toThrow(
641
+ "z.codec() is only supported if the input schema is already supported. Unsupported zod type: map",
642
+ );
643
+ });
644
+ });
514
645
  });
515
646
 
516
647
  describe("z.object() and CoValue schema compatibility", () => {
package/tsup.config.ts CHANGED
@@ -16,6 +16,7 @@ export default defineConfig([
16
16
  entry: {
17
17
  index: "src/index.ts",
18
18
  testing: "src/testing.ts",
19
+ "tools/ssr": "src/tools/ssr/index.ts",
19
20
  },
20
21
  outDir: "dist",
21
22
  },
@@ -73,18 +74,12 @@ export default defineConfig([
73
74
  },
74
75
  outDir: "dist/prosemirror",
75
76
  },
76
- {
77
- ...cfg,
78
- entry: {
79
- ssr: "src/react/ssr.ts",
80
- },
81
- outDir: "dist/react",
82
- },
83
77
  {
84
78
  ...cfg,
85
79
  entry: {
86
80
  index: "src/react/index.ts",
87
81
  testing: "src/react/testing.tsx",
82
+ ssr: "src/react/ssr.ts",
88
83
  },
89
84
  outDir: "dist/react",
90
85
  esbuildOptions: (options) => {