rwsdk 1.0.0-beta.30-test.20251120210809 → 1.0.0-beta.30-test.20251120213828

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.
@@ -413,7 +413,12 @@ function createSDKTestRunner() {
413
413
  console.warn("Failed to connect to existing browser instance. " +
414
414
  "This might happen if you are running a single test file. " +
415
415
  "Launching a new browser instance instead.");
416
- browser = await launchBrowser();
416
+ // Check for RWSDK_HEADLESS environment variable (default to true if not set)
417
+ // Set RWSDK_HEADLESS=0 or RWSDK_HEADLESS=false to run in headed mode
418
+ const headless = process.env.RWSDK_HEADLESS === undefined ||
419
+ process.env.RWSDK_HEADLESS === "1" ||
420
+ process.env.RWSDK_HEADLESS === "true";
421
+ browser = await launchBrowser(undefined, headless);
417
422
  }
418
423
  }, SETUP_WAIT_TIMEOUT);
419
424
  afterAll(async () => {
@@ -0,0 +1,21 @@
1
+ import { RpcStub } from "capnweb";
2
+ import { DurableObject } from "cloudflare:workers";
3
+ export type SyncedStateValue = unknown;
4
+ type OnSetHandler = (key: string, value: SyncedStateValue) => void;
5
+ type OnGetHandler = (key: string, value: SyncedStateValue | undefined) => void;
6
+ /**
7
+ * Durable Object that keeps shared state for multiple clients and notifies subscribers.
8
+ */
9
+ export declare class SyncedStateServer extends DurableObject {
10
+ #private;
11
+ static registerKeyHandler(handler: (key: string) => Promise<string>): void;
12
+ static getKeyHandler(): ((key: string) => Promise<string>) | null;
13
+ static registerSetStateHandler(handler: OnSetHandler | null): void;
14
+ static registerGetStateHandler(handler: OnGetHandler | null): void;
15
+ getState(key: string): SyncedStateValue;
16
+ setState(value: SyncedStateValue, key: string): void;
17
+ subscribe(key: string, client: RpcStub<(value: SyncedStateValue) => void>): void;
18
+ unsubscribe(key: string, client: RpcStub<(value: SyncedStateValue) => void>): void;
19
+ fetch(request: Request): Promise<Response>;
20
+ }
21
+ export {};
@@ -9,51 +9,51 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _a, _SyncStateServer_keyHandler, _SyncStateServer_setStateHandler, _SyncStateServer_getStateHandler, _SyncStateServer_stateStore, _SyncStateServer_subscriptions, _SyncStateServer_subscriptionRefs, _CoordinatorApi_coordinator;
13
- import { RpcTarget } from "capnweb";
12
+ var _a, _SyncedStateServer_keyHandler, _SyncedStateServer_setStateHandler, _SyncedStateServer_getStateHandler, _SyncedStateServer_stateStore, _SyncedStateServer_subscriptions, _SyncedStateServer_subscriptionRefs, _CoordinatorApi_coordinator;
13
+ import { RpcTarget, newWorkersRpcResponse } from "capnweb";
14
14
  import { DurableObject } from "cloudflare:workers";
15
15
  /**
16
16
  * Durable Object that keeps shared state for multiple clients and notifies subscribers.
17
17
  */
18
- export class SyncStateServer extends DurableObject {
18
+ export class SyncedStateServer extends DurableObject {
19
19
  constructor() {
20
20
  super(...arguments);
21
- _SyncStateServer_stateStore.set(this, new Map());
22
- _SyncStateServer_subscriptions.set(this, new Map());
23
- _SyncStateServer_subscriptionRefs.set(this, new Map());
21
+ _SyncedStateServer_stateStore.set(this, new Map());
22
+ _SyncedStateServer_subscriptions.set(this, new Map());
23
+ _SyncedStateServer_subscriptionRefs.set(this, new Map());
24
24
  }
25
25
  static registerKeyHandler(handler) {
26
- __classPrivateFieldSet(_a, _a, handler, "f", _SyncStateServer_keyHandler);
26
+ __classPrivateFieldSet(_a, _a, handler, "f", _SyncedStateServer_keyHandler);
27
27
  }
28
28
  static getKeyHandler() {
29
- return __classPrivateFieldGet(_a, _a, "f", _SyncStateServer_keyHandler);
29
+ return __classPrivateFieldGet(_a, _a, "f", _SyncedStateServer_keyHandler);
30
30
  }
31
31
  static registerSetStateHandler(handler) {
32
- __classPrivateFieldSet(_a, _a, handler, "f", _SyncStateServer_setStateHandler);
32
+ __classPrivateFieldSet(_a, _a, handler, "f", _SyncedStateServer_setStateHandler);
33
33
  }
34
34
  static registerGetStateHandler(handler) {
35
- __classPrivateFieldSet(_a, _a, handler, "f", _SyncStateServer_getStateHandler);
35
+ __classPrivateFieldSet(_a, _a, handler, "f", _SyncedStateServer_getStateHandler);
36
36
  }
37
37
  getState(key) {
38
- const value = __classPrivateFieldGet(this, _SyncStateServer_stateStore, "f").get(key);
39
- if (__classPrivateFieldGet(_a, _a, "f", _SyncStateServer_getStateHandler)) {
40
- __classPrivateFieldGet(_a, _a, "f", _SyncStateServer_getStateHandler).call(_a, key, value);
38
+ const value = __classPrivateFieldGet(this, _SyncedStateServer_stateStore, "f").get(key);
39
+ if (__classPrivateFieldGet(_a, _a, "f", _SyncedStateServer_getStateHandler)) {
40
+ __classPrivateFieldGet(_a, _a, "f", _SyncedStateServer_getStateHandler).call(_a, key, value);
41
41
  }
42
42
  return value;
43
43
  }
44
44
  setState(value, key) {
45
- __classPrivateFieldGet(this, _SyncStateServer_stateStore, "f").set(key, value);
46
- if (__classPrivateFieldGet(_a, _a, "f", _SyncStateServer_setStateHandler)) {
47
- __classPrivateFieldGet(_a, _a, "f", _SyncStateServer_setStateHandler).call(_a, key, value);
45
+ __classPrivateFieldGet(this, _SyncedStateServer_stateStore, "f").set(key, value);
46
+ if (__classPrivateFieldGet(_a, _a, "f", _SyncedStateServer_setStateHandler)) {
47
+ __classPrivateFieldGet(_a, _a, "f", _SyncedStateServer_setStateHandler).call(_a, key, value);
48
48
  }
49
- const subscribers = __classPrivateFieldGet(this, _SyncStateServer_subscriptions, "f").get(key);
49
+ const subscribers = __classPrivateFieldGet(this, _SyncedStateServer_subscriptions, "f").get(key);
50
50
  if (!subscribers) {
51
51
  return;
52
52
  }
53
53
  for (const subscriber of subscribers) {
54
54
  subscriber(value).catch(() => {
55
55
  subscribers.delete(subscriber);
56
- const refs = __classPrivateFieldGet(this, _SyncStateServer_subscriptionRefs, "f").get(key);
56
+ const refs = __classPrivateFieldGet(this, _SyncedStateServer_subscriptionRefs, "f").get(key);
57
57
  if (refs) {
58
58
  for (const [original, duplicate] of refs) {
59
59
  if (duplicate === subscriber) {
@@ -62,46 +62,50 @@ export class SyncStateServer extends DurableObject {
62
62
  }
63
63
  }
64
64
  if (refs.size === 0) {
65
- __classPrivateFieldGet(this, _SyncStateServer_subscriptionRefs, "f").delete(key);
65
+ __classPrivateFieldGet(this, _SyncedStateServer_subscriptionRefs, "f").delete(key);
66
66
  }
67
67
  }
68
68
  });
69
69
  }
70
70
  if (subscribers.size === 0) {
71
- __classPrivateFieldGet(this, _SyncStateServer_subscriptions, "f").delete(key);
71
+ __classPrivateFieldGet(this, _SyncedStateServer_subscriptions, "f").delete(key);
72
72
  }
73
73
  }
74
74
  subscribe(key, client) {
75
- if (!__classPrivateFieldGet(this, _SyncStateServer_subscriptions, "f").has(key)) {
76
- __classPrivateFieldGet(this, _SyncStateServer_subscriptions, "f").set(key, new Set());
75
+ if (!__classPrivateFieldGet(this, _SyncedStateServer_subscriptions, "f").has(key)) {
76
+ __classPrivateFieldGet(this, _SyncedStateServer_subscriptions, "f").set(key, new Set());
77
77
  }
78
- if (!__classPrivateFieldGet(this, _SyncStateServer_subscriptionRefs, "f").has(key)) {
79
- __classPrivateFieldGet(this, _SyncStateServer_subscriptionRefs, "f").set(key, new Map());
78
+ if (!__classPrivateFieldGet(this, _SyncedStateServer_subscriptionRefs, "f").has(key)) {
79
+ __classPrivateFieldGet(this, _SyncedStateServer_subscriptionRefs, "f").set(key, new Map());
80
80
  }
81
81
  const duplicate = client.dup();
82
- __classPrivateFieldGet(this, _SyncStateServer_subscriptions, "f").get(key).add(duplicate);
83
- __classPrivateFieldGet(this, _SyncStateServer_subscriptionRefs, "f").get(key).set(client, duplicate);
82
+ __classPrivateFieldGet(this, _SyncedStateServer_subscriptions, "f").get(key).add(duplicate);
83
+ __classPrivateFieldGet(this, _SyncedStateServer_subscriptionRefs, "f").get(key).set(client, duplicate);
84
84
  }
85
85
  unsubscribe(key, client) {
86
- const duplicates = __classPrivateFieldGet(this, _SyncStateServer_subscriptionRefs, "f").get(key);
86
+ const duplicates = __classPrivateFieldGet(this, _SyncedStateServer_subscriptionRefs, "f").get(key);
87
87
  const duplicate = duplicates?.get(client);
88
- const subscribers = __classPrivateFieldGet(this, _SyncStateServer_subscriptions, "f").get(key);
88
+ const subscribers = __classPrivateFieldGet(this, _SyncedStateServer_subscriptions, "f").get(key);
89
89
  if (duplicate && subscribers) {
90
90
  subscribers.delete(duplicate);
91
91
  duplicates.delete(client);
92
92
  if (subscribers.size === 0) {
93
- __classPrivateFieldGet(this, _SyncStateServer_subscriptions, "f").delete(key);
93
+ __classPrivateFieldGet(this, _SyncedStateServer_subscriptions, "f").delete(key);
94
94
  }
95
95
  if (duplicates.size === 0) {
96
- __classPrivateFieldGet(this, _SyncStateServer_subscriptionRefs, "f").delete(key);
96
+ __classPrivateFieldGet(this, _SyncedStateServer_subscriptionRefs, "f").delete(key);
97
97
  }
98
98
  }
99
99
  }
100
+ async fetch(request) {
101
+ const api = new CoordinatorApi(this);
102
+ return newWorkersRpcResponse(request, api);
103
+ }
100
104
  }
101
- _a = SyncStateServer, _SyncStateServer_stateStore = new WeakMap(), _SyncStateServer_subscriptions = new WeakMap(), _SyncStateServer_subscriptionRefs = new WeakMap();
102
- _SyncStateServer_keyHandler = { value: null };
103
- _SyncStateServer_setStateHandler = { value: null };
104
- _SyncStateServer_getStateHandler = { value: null };
105
+ _a = SyncedStateServer, _SyncedStateServer_stateStore = new WeakMap(), _SyncedStateServer_subscriptions = new WeakMap(), _SyncedStateServer_subscriptionRefs = new WeakMap();
106
+ _SyncedStateServer_keyHandler = { value: null };
107
+ _SyncedStateServer_setStateHandler = { value: null };
108
+ _SyncedStateServer_getStateHandler = { value: null };
105
109
  class CoordinatorApi extends RpcTarget {
106
110
  constructor(coordinator) {
107
111
  super();
@@ -4,7 +4,7 @@ vi.mock("cloudflare:workers", () => {
4
4
  }
5
5
  return { DurableObject };
6
6
  });
7
- import { SyncStateServer } from "../SyncStateServer.mjs";
7
+ import { SyncedStateServer } from "../SyncedStateServer.mjs";
8
8
  const createStub = (onInvoke) => {
9
9
  const fn = Object.assign(async (value) => {
10
10
  await onInvoke(value);
@@ -13,9 +13,9 @@ const createStub = (onInvoke) => {
13
13
  });
14
14
  return fn;
15
15
  };
16
- describe("SyncStateServer", () => {
16
+ describe("SyncedStateServer", () => {
17
17
  it("notifies subscribers when state changes", async () => {
18
- const coordinator = new SyncStateServer({}, {});
18
+ const coordinator = new SyncedStateServer({}, {});
19
19
  const received = [];
20
20
  const stub = createStub((value) => {
21
21
  received.push(value);
@@ -26,7 +26,7 @@ describe("SyncStateServer", () => {
26
26
  expect(received).toEqual([5]);
27
27
  });
28
28
  it("removes subscriptions on unsubscribe", () => {
29
- const coordinator = new SyncStateServer({}, {});
29
+ const coordinator = new SyncedStateServer({}, {});
30
30
  const stub = createStub(() => { });
31
31
  coordinator.subscribe("counter", stub);
32
32
  coordinator.unsubscribe("counter", stub);
@@ -34,7 +34,7 @@ describe("SyncStateServer", () => {
34
34
  expect(coordinator.getState("counter")).toBe(1);
35
35
  });
36
36
  it("drops failing subscribers", async () => {
37
- const coordinator = new SyncStateServer({}, {});
37
+ const coordinator = new SyncedStateServer({}, {});
38
38
  const stub = createStub(async () => {
39
39
  throw new Error("fail");
40
40
  });
@@ -45,45 +45,45 @@ describe("SyncStateServer", () => {
45
45
  expect(coordinator.getState("counter")).toBe(4);
46
46
  });
47
47
  it("invokes registered onSet handler", () => {
48
- const coordinator = new SyncStateServer({}, {});
48
+ const coordinator = new SyncedStateServer({}, {});
49
49
  const calls = [];
50
- SyncStateServer.registerSetStateHandler((key, value) => {
50
+ SyncedStateServer.registerSetStateHandler((key, value) => {
51
51
  calls.push({ key, value });
52
52
  });
53
53
  coordinator.setState(2, "counter");
54
54
  expect(calls).toEqual([{ key: "counter", value: 2 }]);
55
- SyncStateServer.registerSetStateHandler(null);
55
+ SyncedStateServer.registerSetStateHandler(null);
56
56
  });
57
57
  it("invokes registered onGet handler", () => {
58
- const coordinator = new SyncStateServer({}, {});
58
+ const coordinator = new SyncedStateServer({}, {});
59
59
  const calls = [];
60
- SyncStateServer.registerGetStateHandler((key, value) => {
60
+ SyncedStateServer.registerGetStateHandler((key, value) => {
61
61
  calls.push({ key, value });
62
62
  });
63
63
  coordinator.setState(4, "counter");
64
64
  expect(coordinator.getState("counter")).toBe(4);
65
65
  expect(calls).toEqual([{ key: "counter", value: 4 }]);
66
- SyncStateServer.registerGetStateHandler(null);
66
+ SyncedStateServer.registerGetStateHandler(null);
67
67
  });
68
68
  describe("registerKeyHandler", () => {
69
69
  afterEach(() => {
70
- SyncStateServer.registerKeyHandler(async (key) => key);
70
+ SyncedStateServer.registerKeyHandler(async (key) => key);
71
71
  });
72
72
  it("stores and retrieves the registered handler", async () => {
73
73
  const handler = async (key) => `transformed:${key}`;
74
- SyncStateServer.registerKeyHandler(handler);
75
- const retrievedHandler = SyncStateServer.getKeyHandler();
74
+ SyncedStateServer.registerKeyHandler(handler);
75
+ const retrievedHandler = SyncedStateServer.getKeyHandler();
76
76
  expect(retrievedHandler).toBe(handler);
77
77
  });
78
78
  it("transforms keys using the registered handler", async () => {
79
79
  const handler = async (key) => `user:123:${key}`;
80
- SyncStateServer.registerKeyHandler(handler);
80
+ SyncedStateServer.registerKeyHandler(handler);
81
81
  const result = await handler("counter");
82
82
  expect(result).toBe("user:123:counter");
83
83
  });
84
84
  it("returns null when no handler is registered", () => {
85
- SyncStateServer.registerKeyHandler(async (key) => key);
86
- const handler = SyncStateServer.getKeyHandler();
85
+ SyncedStateServer.registerKeyHandler(async (key) => key);
86
+ const handler = SyncedStateServer.getKeyHandler();
87
87
  expect(handler).not.toBeNull();
88
88
  });
89
89
  it("allows handler to be async", async () => {
@@ -91,7 +91,7 @@ describe("SyncStateServer", () => {
91
91
  await new Promise((resolve) => setTimeout(resolve, 10));
92
92
  return `async:${key}`;
93
93
  };
94
- SyncStateServer.registerKeyHandler(handler);
94
+ SyncedStateServer.registerKeyHandler(handler);
95
95
  const result = await handler("test");
96
96
  expect(result).toBe("async:test");
97
97
  });
@@ -101,7 +101,7 @@ describe("SyncStateServer", () => {
101
101
  receivedKey = key;
102
102
  return key;
103
103
  };
104
- SyncStateServer.registerKeyHandler(handler);
104
+ SyncedStateServer.registerKeyHandler(handler);
105
105
  await handler("myKey");
106
106
  expect(receivedKey).toBe("myKey");
107
107
  });
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { setSyncStateClientForTesting } from "../client";
3
- import { createSyncStateHook, } from "../useSyncedState";
2
+ import { setSyncedStateClientForTesting, } from "../client";
3
+ import { createSyncedStateHook, } from "../useSyncedState";
4
4
  const createStateHarness = () => {
5
5
  let currentState;
6
6
  const cleanups = [];
@@ -39,7 +39,7 @@ const createStateHarness = () => {
39
39
  runCleanups: () => cleanups.forEach((fn) => fn()),
40
40
  };
41
41
  };
42
- describe("createSyncStateHook", () => {
42
+ describe("createSyncedStateHook", () => {
43
43
  const subscribeHandlers = new Map();
44
44
  const client = {
45
45
  async getState() {
@@ -65,15 +65,15 @@ describe("createSyncStateHook", () => {
65
65
  };
66
66
  beforeEach(() => {
67
67
  resetClient();
68
- setSyncStateClientForTesting(client);
68
+ setSyncedStateClientForTesting(client);
69
69
  subscribeHandlers.clear();
70
70
  });
71
71
  afterEach(() => {
72
- setSyncStateClientForTesting(null);
72
+ setSyncedStateClientForTesting(null);
73
73
  });
74
74
  it("loads remote state and updates local value", async () => {
75
75
  const harness = createStateHarness();
76
- const useSyncedState = createSyncStateHook({ hooks: harness.deps });
76
+ const useSyncedState = createSyncedStateHook({ hooks: harness.deps });
77
77
  const [value] = useSyncedState(0, "counter");
78
78
  expect(value).toBe(0);
79
79
  await Promise.resolve();
@@ -85,7 +85,7 @@ describe("createSyncStateHook", () => {
85
85
  client.setState = async (value, key) => {
86
86
  setCalls.push({ key, value });
87
87
  };
88
- const useSyncedState = createSyncStateHook({ hooks: harness.deps });
88
+ const useSyncedState = createSyncedStateHook({ hooks: harness.deps });
89
89
  const [, setSyncValue] = useSyncedState(0, "counter");
90
90
  setSyncValue(9);
91
91
  expect(harness.getState()).toBe(9);
@@ -93,7 +93,7 @@ describe("createSyncStateHook", () => {
93
93
  });
94
94
  it("applies remote updates from the subscription handler", async () => {
95
95
  const harness = createStateHarness();
96
- const useSyncedState = createSyncStateHook({ hooks: harness.deps });
96
+ const useSyncedState = createSyncedStateHook({ hooks: harness.deps });
97
97
  useSyncedState(0, "counter");
98
98
  await Promise.resolve();
99
99
  const handler = subscribeHandlers.get("counter");
@@ -107,7 +107,7 @@ describe("createSyncStateHook", () => {
107
107
  unsubscribed.push({ key });
108
108
  subscribeHandlers.delete(key);
109
109
  };
110
- const useSyncedState = createSyncStateHook({ hooks: harness.deps });
110
+ const useSyncedState = createSyncedStateHook({ hooks: harness.deps });
111
111
  useSyncedState(0, "counter");
112
112
  harness.runCleanups();
113
113
  expect(unsubscribed).toEqual([{ key: "counter" }]);
@@ -1,6 +1,6 @@
1
1
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
2
- import { setSyncStateClientForTesting, } from "../client";
3
- import { createSyncStateHook, } from "../useSyncedState";
2
+ import { setSyncedStateClientForTesting, } from "../client";
3
+ import { createSyncedStateHook, } from "../useSyncedState";
4
4
  const createStateHarness = () => {
5
5
  let currentState;
6
6
  const cleanups = [];
@@ -39,7 +39,7 @@ const createStateHarness = () => {
39
39
  runCleanups: () => cleanups.forEach((fn) => fn()),
40
40
  };
41
41
  };
42
- describe("createSyncStateHook", () => {
42
+ describe("createSyncedStateHook", () => {
43
43
  const subscribeHandlers = new Map();
44
44
  const client = {
45
45
  async getState() {
@@ -65,15 +65,15 @@ describe("createSyncStateHook", () => {
65
65
  };
66
66
  beforeEach(() => {
67
67
  resetClient();
68
- setSyncStateClientForTesting(client);
68
+ setSyncedStateClientForTesting(client);
69
69
  subscribeHandlers.clear();
70
70
  });
71
71
  afterEach(() => {
72
- setSyncStateClientForTesting(null);
72
+ setSyncedStateClientForTesting(null);
73
73
  });
74
74
  it("loads remote state and updates local value", async () => {
75
75
  const harness = createStateHarness();
76
- const useSyncedState = createSyncStateHook({ hooks: harness.deps });
76
+ const useSyncedState = createSyncedStateHook({ hooks: harness.deps });
77
77
  const [value] = useSyncedState(0, "counter");
78
78
  expect(value).toBe(0);
79
79
  await Promise.resolve();
@@ -85,7 +85,7 @@ describe("createSyncStateHook", () => {
85
85
  client.setState = async (value, key) => {
86
86
  setCalls.push({ key, value });
87
87
  };
88
- const useSyncedState = createSyncStateHook({ hooks: harness.deps });
88
+ const useSyncedState = createSyncedStateHook({ hooks: harness.deps });
89
89
  const [, setSyncValue] = useSyncedState(0, "counter");
90
90
  setSyncValue(9);
91
91
  expect(harness.getState()).toBe(9);
@@ -93,7 +93,7 @@ describe("createSyncStateHook", () => {
93
93
  });
94
94
  it("applies remote updates from the subscription handler", async () => {
95
95
  const harness = createStateHarness();
96
- const useSyncedState = createSyncStateHook({ hooks: harness.deps });
96
+ const useSyncedState = createSyncedStateHook({ hooks: harness.deps });
97
97
  useSyncedState(0, "counter");
98
98
  await Promise.resolve();
99
99
  const handler = subscribeHandlers.get("counter");
@@ -107,7 +107,7 @@ describe("createSyncStateHook", () => {
107
107
  unsubscribed.push({ key });
108
108
  subscribeHandlers.delete(key);
109
109
  };
110
- const useSyncedState = createSyncStateHook({ hooks: harness.deps });
110
+ const useSyncedState = createSyncedStateHook({ hooks: harness.deps });
111
111
  useSyncedState(0, "counter");
112
112
  harness.runCleanups();
113
113
  expect(unsubscribed).toEqual([{ key: "counter" }]);
@@ -12,18 +12,18 @@ vi.mock("capnweb", () => ({
12
12
  vi.mock("../runtime/entries/router", () => ({
13
13
  route: vi.fn((path, handler) => ({ path, handler })),
14
14
  }));
15
- import { SyncStateServer } from "../SyncStateServer.mjs";
16
- describe("SyncStateProxy", () => {
15
+ import { SyncedStateServer } from "../SyncedStateServer.mjs";
16
+ describe("SyncedStateProxy", () => {
17
17
  let mockCoordinator;
18
18
  beforeEach(() => {
19
- mockCoordinator = new SyncStateServer({}, {});
19
+ mockCoordinator = new SyncedStateServer({}, {});
20
20
  });
21
21
  afterEach(() => {
22
- SyncStateServer.registerKeyHandler(async (key) => key);
22
+ SyncedStateServer.registerKeyHandler(async (key) => key);
23
23
  });
24
24
  it("transforms keys before calling coordinator methods when handler is registered", async () => {
25
25
  const handler = async (key) => `transformed:${key}`;
26
- SyncStateServer.registerKeyHandler(handler);
26
+ SyncedStateServer.registerKeyHandler(handler);
27
27
  const transformedKey = await handler("counter");
28
28
  expect(transformedKey).toBe("transformed:counter");
29
29
  mockCoordinator.setState(5, transformedKey);
@@ -31,13 +31,13 @@ describe("SyncStateProxy", () => {
31
31
  expect(value).toBe(5);
32
32
  });
33
33
  it("does not transform keys when no handler is registered", () => {
34
- SyncStateServer.registerKeyHandler(async (key) => key);
35
- const handler = SyncStateServer.getKeyHandler();
34
+ SyncedStateServer.registerKeyHandler(async (key) => key);
35
+ const handler = SyncedStateServer.getKeyHandler();
36
36
  expect(handler).not.toBeNull();
37
37
  });
38
38
  it("passes through original key when handler returns it unchanged", async () => {
39
39
  const handler = async (key) => key;
40
- SyncStateServer.registerKeyHandler(handler);
40
+ SyncedStateServer.registerKeyHandler(handler);
41
41
  const result = await handler("counter");
42
42
  expect(result).toBe("counter");
43
43
  });
@@ -46,7 +46,7 @@ describe("SyncStateProxy", () => {
46
46
  const userId = "user123";
47
47
  return `user:${userId}:${key}`;
48
48
  };
49
- SyncStateServer.registerKeyHandler(handler);
49
+ SyncedStateServer.registerKeyHandler(handler);
50
50
  const result = await handler("settings");
51
51
  expect(result).toBe("user:user123:settings");
52
52
  });
@@ -54,7 +54,7 @@ describe("SyncStateProxy", () => {
54
54
  const handler = async (_key) => {
55
55
  throw new Error("Handler error");
56
56
  };
57
- SyncStateServer.registerKeyHandler(handler);
57
+ SyncedStateServer.registerKeyHandler(handler);
58
58
  await expect(handler("test")).rejects.toThrow("Handler error");
59
59
  });
60
60
  it("handles async operations in handler", async () => {
@@ -62,7 +62,7 @@ describe("SyncStateProxy", () => {
62
62
  await new Promise((resolve) => setTimeout(resolve, 5));
63
63
  return `async:${key}`;
64
64
  };
65
- SyncStateServer.registerKeyHandler(handler);
65
+ SyncedStateServer.registerKeyHandler(handler);
66
66
  const result = await handler("data");
67
67
  expect(result).toBe("async:data");
68
68
  });
@@ -0,0 +1,26 @@
1
+ export type SyncedStateClient = {
2
+ getState(key: string): Promise<unknown>;
3
+ setState(value: unknown, key: string): Promise<void>;
4
+ subscribe(key: string, handler: (value: unknown) => void): Promise<void>;
5
+ unsubscribe(key: string, handler: (value: unknown) => void): Promise<void>;
6
+ };
7
+ /**
8
+ * Returns a cached client for the provided endpoint, creating it when necessary.
9
+ * @param endpoint Endpoint to connect to.
10
+ * @returns RPC client instance.
11
+ */
12
+ export declare const getSyncedStateClient: (endpoint?: string) => SyncedStateClient;
13
+ /**
14
+ * Initializes and caches an RPC client instance for the sync state endpoint.
15
+ * @param options Optional endpoint override.
16
+ * @returns Cached client instance or `null` when running without `window`.
17
+ */
18
+ export declare const initSyncedStateClient: (options?: {
19
+ endpoint?: string;
20
+ }) => SyncedStateClient | null;
21
+ /**
22
+ * Injects a client instance for tests and updates the cached endpoint.
23
+ * @param client Stub client instance or `null` to clear the cache.
24
+ * @param endpoint Endpoint associated with the injected client.
25
+ */
26
+ export declare const setSyncedStateClientForTesting: (client: SyncedStateClient | null, endpoint?: string) => void;
@@ -0,0 +1,39 @@
1
+ import { newWebSocketRpcSession } from "capnweb";
2
+ import { DEFAULT_SYNCED_STATE_PATH } from "./constants.mjs";
3
+ let cachedClient = null;
4
+ let cachedEndpoint = DEFAULT_SYNCED_STATE_PATH;
5
+ /**
6
+ * Returns a cached client for the provided endpoint, creating it when necessary.
7
+ * @param endpoint Endpoint to connect to.
8
+ * @returns RPC client instance.
9
+ */
10
+ export const getSyncedStateClient = (endpoint = cachedEndpoint) => {
11
+ if (cachedClient && endpoint === cachedEndpoint) {
12
+ return cachedClient;
13
+ }
14
+ cachedEndpoint = endpoint;
15
+ cachedClient = newWebSocketRpcSession(cachedEndpoint);
16
+ return cachedClient;
17
+ };
18
+ /**
19
+ * Initializes and caches an RPC client instance for the sync state endpoint.
20
+ * @param options Optional endpoint override.
21
+ * @returns Cached client instance or `null` when running without `window`.
22
+ */
23
+ export const initSyncedStateClient = (options = {}) => {
24
+ cachedEndpoint = options.endpoint ?? DEFAULT_SYNCED_STATE_PATH;
25
+ if (typeof window === "undefined") {
26
+ return null;
27
+ }
28
+ cachedClient = newWebSocketRpcSession(cachedEndpoint);
29
+ return cachedClient;
30
+ };
31
+ /**
32
+ * Injects a client instance for tests and updates the cached endpoint.
33
+ * @param client Stub client instance or `null` to clear the cache.
34
+ * @param endpoint Endpoint associated with the injected client.
35
+ */
36
+ export const setSyncedStateClientForTesting = (client, endpoint = DEFAULT_SYNCED_STATE_PATH) => {
37
+ cachedClient = client;
38
+ cachedEndpoint = endpoint;
39
+ };
@@ -1,28 +1,3 @@
1
- export type SyncStateClient = {
2
- getState(key: string): Promise<unknown>;
3
- setState(value: unknown, key: string): Promise<void>;
4
- subscribe(key: string, handler: (value: unknown) => void): Promise<void>;
5
- unsubscribe(key: string, handler: (value: unknown) => void): Promise<void>;
6
- };
7
- type InitOptions = {
8
- endpoint?: string;
9
- };
10
- /**
11
- * Initializes and caches an RPC client instance for the sync state endpoint.
12
- * @param options Optional endpoint override.
13
- * @returns Cached client instance or `null` when running without `window`.
14
- */
15
- export declare const initSyncStateClient: (options?: InitOptions) => SyncStateClient | null;
16
- /**
17
- * Returns a cached client for the provided endpoint, creating it when necessary.
18
- * @param endpoint Endpoint to connect to.
19
- * @returns RPC client instance.
20
- */
21
- export declare const getSyncStateClient: (endpoint?: string) => SyncStateClient;
22
- /**
23
- * Injects a client instance for tests and updates the cached endpoint.
24
- * @param client Stub client instance or `null` to clear the cache.
25
- * @param endpoint Endpoint associated with the injected client.
26
- */
27
- export declare const setSyncStateClientForTesting: (client: SyncStateClient | null, endpoint?: string) => void;
28
- export {};
1
+ export { getSyncedStateClient, initSyncedStateClient, setSyncedStateClientForTesting, } from "./client-core.js";
2
+ export type { SyncedStateClient } from "./client-core.js";
3
+ export { useSyncedState } from "./useSyncedState.js";
@@ -1,39 +1,4 @@
1
- import { newWebSocketRpcSession } from "capnweb";
2
- import { DEFAULT_SYNC_STATE_PATH } from "./constants.mjs";
3
- let cachedClient = null;
4
- let cachedEndpoint = DEFAULT_SYNC_STATE_PATH;
5
- /**
6
- * Initializes and caches an RPC client instance for the sync state endpoint.
7
- * @param options Optional endpoint override.
8
- * @returns Cached client instance or `null` when running without `window`.
9
- */
10
- export const initSyncStateClient = (options = {}) => {
11
- cachedEndpoint = options.endpoint ?? DEFAULT_SYNC_STATE_PATH;
12
- if (typeof window === "undefined") {
13
- return null;
14
- }
15
- cachedClient = newWebSocketRpcSession(cachedEndpoint);
16
- return cachedClient;
17
- };
18
- /**
19
- * Returns a cached client for the provided endpoint, creating it when necessary.
20
- * @param endpoint Endpoint to connect to.
21
- * @returns RPC client instance.
22
- */
23
- export const getSyncStateClient = (endpoint = cachedEndpoint) => {
24
- if (cachedClient && endpoint === cachedEndpoint) {
25
- return cachedClient;
26
- }
27
- cachedEndpoint = endpoint;
28
- cachedClient = newWebSocketRpcSession(cachedEndpoint);
29
- return cachedClient;
30
- };
31
- /**
32
- * Injects a client instance for tests and updates the cached endpoint.
33
- * @param client Stub client instance or `null` to clear the cache.
34
- * @param endpoint Endpoint associated with the injected client.
35
- */
36
- export const setSyncStateClientForTesting = (client, endpoint = DEFAULT_SYNC_STATE_PATH) => {
37
- cachedClient = client;
38
- cachedEndpoint = endpoint;
39
- };
1
+ // Re-export everything from client-core to maintain the public API
2
+ export { getSyncedStateClient, initSyncedStateClient, setSyncedStateClientForTesting, } from "./client-core.js";
3
+ // Re-export useSyncedState (no circular dependency since useSyncedState imports from client-core, not client)
4
+ export { useSyncedState } from "./useSyncedState.js";
@@ -1 +1 @@
1
- export declare const DEFAULT_SYNC_STATE_PATH = "/__sync-state";
1
+ export declare const DEFAULT_SYNCED_STATE_PATH = "/__synced-state";
@@ -1 +1 @@
1
- export const DEFAULT_SYNC_STATE_PATH = "/__sync-state";
1
+ export const DEFAULT_SYNCED_STATE_PATH = "/__synced-state";
@@ -1,4 +1,4 @@
1
- import { React } from "../runtime/client/client";
1
+ import React from "react";
2
2
  type HookDeps = {
3
3
  useState: typeof React.useState;
4
4
  useEffect: typeof React.useEffect;
@@ -6,7 +6,7 @@ type HookDeps = {
6
6
  useCallback: typeof React.useCallback;
7
7
  };
8
8
  type Setter<T> = (value: T | ((previous: T) => T)) => void;
9
- export type CreateSyncStateHookOptions = {
9
+ export type CreateSyncedStateHookOptions = {
10
10
  url?: string;
11
11
  hooks?: HookDeps;
12
12
  };
@@ -15,6 +15,6 @@ export type CreateSyncStateHookOptions = {
15
15
  * @param options Optional overrides for endpoint and React primitives.
16
16
  * @returns Hook that syncs state through the sync state service.
17
17
  */
18
- export declare const createSyncStateHook: (options?: CreateSyncStateHookOptions) => <T>(initialValue: T, key: string) => [T, Setter<T>];
18
+ export declare const createSyncedStateHook: (options?: CreateSyncedStateHookOptions) => <T>(initialValue: T, key: string) => [T, Setter<T>];
19
19
  export declare const useSyncedState: <T>(initialValue: T, key: string) => [T, Setter<T>];
20
20
  export {};
@@ -1,6 +1,6 @@
1
- import { React } from "../runtime/client/client";
2
- import { getSyncStateClient } from "./client";
3
- import { DEFAULT_SYNC_STATE_PATH } from "./constants.mjs";
1
+ import React from "react";
2
+ import { getSyncedStateClient } from "./client-core.js";
3
+ import { DEFAULT_SYNCED_STATE_PATH } from "./constants.mjs";
4
4
  const defaultDeps = {
5
5
  useState: React.useState,
6
6
  useEffect: React.useEffect,
@@ -12,15 +12,15 @@ const defaultDeps = {
12
12
  * @param options Optional overrides for endpoint and React primitives.
13
13
  * @returns Hook that syncs state through the sync state service.
14
14
  */
15
- export const createSyncStateHook = (options = {}) => {
16
- const resolvedUrl = options.url ?? DEFAULT_SYNC_STATE_PATH;
15
+ export const createSyncedStateHook = (options = {}) => {
16
+ const resolvedUrl = options.url ?? DEFAULT_SYNCED_STATE_PATH;
17
17
  const deps = options.hooks ?? defaultDeps;
18
18
  const { useState, useEffect, useRef, useCallback } = deps;
19
19
  return function useSyncedState(initialValue, key) {
20
20
  if (typeof window === "undefined" && !options.hooks) {
21
21
  return [initialValue, () => { }];
22
22
  }
23
- const client = getSyncStateClient(resolvedUrl);
23
+ const client = getSyncedStateClient(resolvedUrl);
24
24
  const [value, setValue] = useState(initialValue);
25
25
  const valueRef = useRef(value);
26
26
  valueRef.current = value;
@@ -55,4 +55,4 @@ export const createSyncStateHook = (options = {}) => {
55
55
  return [value, setSyncValue];
56
56
  };
57
57
  };
58
- export const useSyncedState = createSyncStateHook();
58
+ export const useSyncedState = createSyncedStateHook();
@@ -1,14 +1,13 @@
1
- import { SyncStateServer } from "./SyncStateServer.mjs";
2
- export { SyncStateServer } from "./SyncStateServer.mjs";
3
- export type SyncStateRouteOptions = {
1
+ import { SyncedStateServer } from "./SyncedStateServer.mjs";
2
+ export { SyncedStateServer };
3
+ export type SyncedStateRouteOptions = {
4
4
  basePath?: string;
5
- resetPath?: string;
6
5
  durableObjectName?: string;
7
6
  };
8
7
  /**
9
8
  * Registers routes that forward sync state requests to the configured Durable Object namespace.
10
9
  * @param getNamespace Function that returns the Durable Object namespace from the Worker env.
11
- * @param options Optional overrides for base path, reset path, and object name.
12
- * @returns Router entries for the sync state API and reset endpoint.
10
+ * @param options Optional overrides for base path and object name.
11
+ * @returns Router entries for the sync state API.
13
12
  */
14
- export declare const syncStateRoutes: (getNamespace: (env: Cloudflare.Env) => DurableObjectNamespace<SyncStateServer>, options?: SyncStateRouteOptions) => import("../runtime/lib/router.js").RouteDefinition<`/${string}`, import("../runtime/worker.js").RequestInfo<any, import("../runtime/worker.js").DefaultAppContext>>[];
13
+ export declare const syncedStateRoutes: (getNamespace: (env: Cloudflare.Env) => DurableObjectNamespace<SyncedStateServer>, options?: SyncedStateRouteOptions) => import("../runtime/lib/router.js").RouteDefinition<`/${string}`, import("../runtime/worker.js").RequestInfo<any, import("../runtime/worker.js").DefaultAppContext>>[];
@@ -9,52 +9,51 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _SyncStateProxy_stub, _SyncStateProxy_keyHandler;
12
+ var _SyncedStateProxy_stub, _SyncedStateProxy_keyHandler;
13
13
  import { RpcTarget, newWorkersRpcResponse } from "capnweb";
14
14
  import { env } from "cloudflare:workers";
15
15
  import { route } from "../runtime/entries/router";
16
- import { SyncStateServer } from "./SyncStateServer.mjs";
17
- import { DEFAULT_SYNC_STATE_PATH } from "./constants.mjs";
18
- export { SyncStateServer } from "./SyncStateServer.mjs";
16
+ import { SyncedStateServer, } from "./SyncedStateServer.mjs";
17
+ import { DEFAULT_SYNCED_STATE_PATH } from "./constants.mjs";
18
+ export { SyncedStateServer };
19
19
  const DEFAULT_SYNC_STATE_NAME = "syncedState";
20
- class SyncStateProxy extends RpcTarget {
20
+ class SyncedStateProxy extends RpcTarget {
21
21
  constructor(stub, keyHandler) {
22
22
  super();
23
- _SyncStateProxy_stub.set(this, void 0);
24
- _SyncStateProxy_keyHandler.set(this, void 0);
25
- __classPrivateFieldSet(this, _SyncStateProxy_stub, stub, "f");
26
- __classPrivateFieldSet(this, _SyncStateProxy_keyHandler, keyHandler, "f");
23
+ _SyncedStateProxy_stub.set(this, void 0);
24
+ _SyncedStateProxy_keyHandler.set(this, void 0);
25
+ __classPrivateFieldSet(this, _SyncedStateProxy_stub, stub, "f");
26
+ __classPrivateFieldSet(this, _SyncedStateProxy_keyHandler, keyHandler, "f");
27
27
  }
28
28
  async getState(key) {
29
- const transformedKey = __classPrivateFieldGet(this, _SyncStateProxy_keyHandler, "f") ? await __classPrivateFieldGet(this, _SyncStateProxy_keyHandler, "f").call(this, key) : key;
30
- return __classPrivateFieldGet(this, _SyncStateProxy_stub, "f").getState(transformedKey);
29
+ const transformedKey = __classPrivateFieldGet(this, _SyncedStateProxy_keyHandler, "f") ? await __classPrivateFieldGet(this, _SyncedStateProxy_keyHandler, "f").call(this, key) : key;
30
+ return __classPrivateFieldGet(this, _SyncedStateProxy_stub, "f").getState(transformedKey);
31
31
  }
32
32
  async setState(value, key) {
33
- const transformedKey = __classPrivateFieldGet(this, _SyncStateProxy_keyHandler, "f") ? await __classPrivateFieldGet(this, _SyncStateProxy_keyHandler, "f").call(this, key) : key;
34
- return __classPrivateFieldGet(this, _SyncStateProxy_stub, "f").setState(value, transformedKey);
33
+ const transformedKey = __classPrivateFieldGet(this, _SyncedStateProxy_keyHandler, "f") ? await __classPrivateFieldGet(this, _SyncedStateProxy_keyHandler, "f").call(this, key) : key;
34
+ return __classPrivateFieldGet(this, _SyncedStateProxy_stub, "f").setState(value, transformedKey);
35
35
  }
36
36
  async subscribe(key, client) {
37
- const transformedKey = __classPrivateFieldGet(this, _SyncStateProxy_keyHandler, "f") ? await __classPrivateFieldGet(this, _SyncStateProxy_keyHandler, "f").call(this, key) : key;
38
- return __classPrivateFieldGet(this, _SyncStateProxy_stub, "f").subscribe(transformedKey, client);
37
+ const transformedKey = __classPrivateFieldGet(this, _SyncedStateProxy_keyHandler, "f") ? await __classPrivateFieldGet(this, _SyncedStateProxy_keyHandler, "f").call(this, key) : key;
38
+ return __classPrivateFieldGet(this, _SyncedStateProxy_stub, "f").subscribe(transformedKey, client);
39
39
  }
40
40
  async unsubscribe(key, client) {
41
- const transformedKey = __classPrivateFieldGet(this, _SyncStateProxy_keyHandler, "f") ? await __classPrivateFieldGet(this, _SyncStateProxy_keyHandler, "f").call(this, key) : key;
42
- return __classPrivateFieldGet(this, _SyncStateProxy_stub, "f").unsubscribe(transformedKey, client);
41
+ const transformedKey = __classPrivateFieldGet(this, _SyncedStateProxy_keyHandler, "f") ? await __classPrivateFieldGet(this, _SyncedStateProxy_keyHandler, "f").call(this, key) : key;
42
+ return __classPrivateFieldGet(this, _SyncedStateProxy_stub, "f").unsubscribe(transformedKey, client);
43
43
  }
44
44
  }
45
- _SyncStateProxy_stub = new WeakMap(), _SyncStateProxy_keyHandler = new WeakMap();
45
+ _SyncedStateProxy_stub = new WeakMap(), _SyncedStateProxy_keyHandler = new WeakMap();
46
46
  /**
47
47
  * Registers routes that forward sync state requests to the configured Durable Object namespace.
48
48
  * @param getNamespace Function that returns the Durable Object namespace from the Worker env.
49
- * @param options Optional overrides for base path, reset path, and object name.
50
- * @returns Router entries for the sync state API and reset endpoint.
49
+ * @param options Optional overrides for base path and object name.
50
+ * @returns Router entries for the sync state API.
51
51
  */
52
- export const syncStateRoutes = (getNamespace, options = {}) => {
53
- const basePath = options.basePath ?? DEFAULT_SYNC_STATE_PATH;
54
- const resetPath = options.resetPath ?? `${basePath}/reset`;
52
+ export const syncedStateRoutes = (getNamespace, options = {}) => {
53
+ const basePath = options.basePath ?? DEFAULT_SYNCED_STATE_PATH;
55
54
  const durableObjectName = options.durableObjectName ?? DEFAULT_SYNC_STATE_NAME;
56
55
  const forwardRequest = async (request) => {
57
- const keyHandler = SyncStateServer.getKeyHandler();
56
+ const keyHandler = SyncedStateServer.getKeyHandler();
58
57
  if (!keyHandler) {
59
58
  const namespace = getNamespace(env);
60
59
  const id = namespace.idFromName(durableObjectName);
@@ -63,11 +62,8 @@ export const syncStateRoutes = (getNamespace, options = {}) => {
63
62
  const namespace = getNamespace(env);
64
63
  const id = namespace.idFromName(durableObjectName);
65
64
  const coordinator = namespace.get(id);
66
- const proxy = new SyncStateProxy(coordinator, keyHandler);
65
+ const proxy = new SyncedStateProxy(coordinator, keyHandler);
67
66
  return newWorkersRpcResponse(request, proxy);
68
67
  };
69
- return [
70
- route(basePath, ({ request }) => forwardRequest(request)),
71
- route(resetPath, ({ request }) => forwardRequest(request)),
72
- ];
68
+ return [route(basePath, ({ request }) => forwardRequest(request))];
73
69
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rwsdk",
3
- "version": "1.0.0-beta.30-test.20251120210809",
3
+ "version": "1.0.0-beta.30-test.20251120213828",
4
4
  "description": "Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime",
5
5
  "type": "module",
6
6
  "bin": {
@@ -110,8 +110,8 @@
110
110
  "default": "./dist/use-synced-state/client.js"
111
111
  },
112
112
  "./use-synced-state/worker": {
113
- "types": "./dist/use-synced-state/worker.d.ts",
114
- "default": "./dist/use-synced-state/worker.js"
113
+ "types": "./dist/use-synced-state/worker.d.mts",
114
+ "workerd": "./dist/use-synced-state/worker.mjs"
115
115
  }
116
116
  },
117
117
  "keywords": [
@@ -157,7 +157,6 @@
157
157
  "@types/react-dom": "~19.1.2",
158
158
  "@types/react-is": "~19.0.0",
159
159
  "@vitejs/plugin-react": "~5.0.0",
160
- "capnweb": "~0.2.0",
161
160
  "chokidar": "~4.0.0",
162
161
  "debug": "~4.4.0",
163
162
  "decompress": "~4.2.1",
@@ -187,6 +186,7 @@
187
186
  },
188
187
  "peerDependencies": {
189
188
  "@cloudflare/vite-plugin": "^1.13.10",
189
+ "capnweb": "~0.2.0",
190
190
  "react": ">=19.2.0-0 <19.3.0 || >=19.3.0-0 <20.0.0",
191
191
  "react-dom": ">=19.2.0-0 <19.3.0 || >=19.3.0-0 <20.0.0",
192
192
  "react-server-dom-webpack": ">=19.2.0-0 <19.3.0 || >=19.3.0-0 <20.0.0",
@@ -196,6 +196,7 @@
196
196
  "packageManager": "pnpm@10.0.0+sha512.b8fef5494bd3fe4cbd4edabd0745df2ee5be3e4b0b8b08fa643aa3e4c6702ccc0f00d68fa8a8c9858a735a0032485a44990ed2810526c875e416f001b17df12b",
197
197
  "devDependencies": {
198
198
  "@cloudflare/vite-plugin": "1.13.3",
199
+ "capnweb": "~0.2.0",
199
200
  "@types/debug": "~4.1.12",
200
201
  "@types/js-beautify": "~1.14.3",
201
202
  "@types/lodash": "~4.17.16",
@@ -1,20 +0,0 @@
1
- import { RpcStub } from "capnweb";
2
- import { DurableObject } from "cloudflare:workers";
3
- export type SyncStateValue = unknown;
4
- type OnSetHandler = (key: string, value: SyncStateValue) => void;
5
- type OnGetHandler = (key: string, value: SyncStateValue | undefined) => void;
6
- /**
7
- * Durable Object that keeps shared state for multiple clients and notifies subscribers.
8
- */
9
- export declare class SyncStateServer extends DurableObject {
10
- #private;
11
- static registerKeyHandler(handler: (key: string) => Promise<string>): void;
12
- static getKeyHandler(): ((key: string) => Promise<string>) | null;
13
- static registerSetStateHandler(handler: OnSetHandler | null): void;
14
- static registerGetStateHandler(handler: OnGetHandler | null): void;
15
- getState(key: string): SyncStateValue;
16
- setState(value: SyncStateValue, key: string): void;
17
- subscribe(key: string, client: RpcStub<(value: SyncStateValue) => void>): void;
18
- unsubscribe(key: string, client: RpcStub<(value: SyncStateValue) => void>): void;
19
- }
20
- export {};
@@ -1,20 +0,0 @@
1
- import { React } from "../runtime/client/client";
2
- type HookDeps = {
3
- useState: typeof React.useState;
4
- useEffect: typeof React.useEffect;
5
- useRef: typeof React.useRef;
6
- useCallback: typeof React.useCallback;
7
- };
8
- type Setter<T> = (value: T | ((previous: T) => T)) => void;
9
- export type CreateSyncStateHookOptions = {
10
- url?: string;
11
- hooks?: HookDeps;
12
- };
13
- /**
14
- * Builds a `useSyncedState` hook configured with optional endpoint and hook overrides.
15
- * @param options Optional overrides for endpoint and React primitives.
16
- * @returns Hook that syncs state through the sync state service.
17
- */
18
- export declare const createSyncStateHook: (options?: CreateSyncStateHookOptions) => <T>(initialValue: T, key: string) => [T, Setter<T>];
19
- export declare const useSyncedState: <T>(initialValue: T, key: string) => [T, Setter<T>];
20
- export {};
@@ -1,58 +0,0 @@
1
- import { React } from "../runtime/client/client";
2
- import { getSyncStateClient } from "./client";
3
- import { DEFAULT_SYNC_STATE_PATH } from "./constants.mjs";
4
- const defaultDeps = {
5
- useState: React.useState,
6
- useEffect: React.useEffect,
7
- useRef: React.useRef,
8
- useCallback: React.useCallback,
9
- };
10
- /**
11
- * Builds a `useSyncedState` hook configured with optional endpoint and hook overrides.
12
- * @param options Optional overrides for endpoint and React primitives.
13
- * @returns Hook that syncs state through the sync state service.
14
- */
15
- export const createSyncStateHook = (options = {}) => {
16
- const resolvedUrl = options.url ?? DEFAULT_SYNC_STATE_PATH;
17
- const deps = options.hooks ?? defaultDeps;
18
- const { useState, useEffect, useRef, useCallback } = deps;
19
- return function useSyncedState(initialValue, key) {
20
- if (typeof window === "undefined" && !options.hooks) {
21
- return [initialValue, () => { }];
22
- }
23
- const client = getSyncStateClient(resolvedUrl);
24
- const [value, setValue] = useState(initialValue);
25
- const valueRef = useRef(value);
26
- valueRef.current = value;
27
- const setSyncValue = useCallback((nextValue) => {
28
- const resolved = typeof nextValue === "function"
29
- ? nextValue(valueRef.current)
30
- : nextValue;
31
- setValue(resolved);
32
- valueRef.current = resolved;
33
- void client.setState(resolved, key);
34
- }, [client, key, setValue, valueRef]);
35
- useEffect(() => {
36
- let isActive = true;
37
- const handleUpdate = (next) => {
38
- if (isActive) {
39
- setValue(next);
40
- valueRef.current = next;
41
- }
42
- };
43
- void client.getState(key).then((existing) => {
44
- if (existing !== undefined && isActive) {
45
- setValue(existing);
46
- valueRef.current = existing;
47
- }
48
- });
49
- void client.subscribe(key, handleUpdate);
50
- return () => {
51
- isActive = false;
52
- void client.unsubscribe(key, handleUpdate);
53
- };
54
- }, [client, key, setValue, valueRef]);
55
- return [value, setSyncValue];
56
- };
57
- };
58
- export const useSyncedState = createSyncStateHook();