Skip to content

Commit e7ba922

Browse files
committed
Rename Callbacks to CallbackRegistry, improve docs
1 parent 2cfad1a commit e7ba922

10 files changed

Lines changed: 83 additions & 76 deletions

File tree

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import { Brand } from "./Brand.js";
2+
import { NanoIdLibDep } from "./NanoId.js";
3+
import { Result } from "./Result.js";
4+
5+
/**
6+
* A registry for one-time callback functions.
7+
*
8+
* Stores callbacks with unique IDs and executes them once with an optional
9+
* argument. Executed callbacks are automatically removed from the registry.
10+
*
11+
* This is useful for correlating asynchronous operations across boundaries
12+
* where callback functions cannot be passed directly (e.g., web workers).
13+
*
14+
* The `execute` method intentionally does not use try-catch or {@link Result}
15+
* because it's the callback's responsibility to handle its own errors. The
16+
* registry is just a correlation mechanism and should not interfere with error
17+
* handling or debugging by masking the original error location.
18+
*/
19+
export interface CallbackRegistry {
20+
/** Registers a callback function and returns a unique ID. */
21+
readonly register: (callback: (arg?: unknown) => void) => CallbackId;
22+
23+
/** Executes and removes a callback associated with the given ID. */
24+
readonly execute: (id: CallbackId, arg?: unknown) => void;
25+
}
26+
27+
export type CallbackId = string & Brand<"CallbackId">;
28+
29+
export const createCallbackRegistry = (
30+
deps: NanoIdLibDep,
31+
): CallbackRegistry => {
32+
const callbackMap = new Map<CallbackId, (arg?: unknown) => void>();
33+
34+
return {
35+
register: (callback) => {
36+
const id = deps.nanoIdLib.nanoid() as CallbackId;
37+
callbackMap.set(id, callback);
38+
return id;
39+
},
40+
41+
execute: (id, arg) => {
42+
const callback = callbackMap.get(id);
43+
if (callback) {
44+
callbackMap.delete(id);
45+
callback(arg);
46+
}
47+
},
48+
};
49+
};

packages/common/src/Callbacks.ts

Lines changed: 0 additions & 43 deletions
This file was deleted.

packages/common/src/Evolu/Db.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { isNonEmptyArray, NonEmptyReadonlyArray } from "../Array.js";
2-
import { CallbackId } from "../Callbacks.js";
2+
import { CallbackId } from "../CallbackRegistry.js";
33
import { ConsoleDep } from "../Console.js";
44
import {
55
createSymmetricCrypto,

packages/common/src/Evolu/Evolu.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { pack } from "msgpackr";
22
import { isNonEmptyArray, isNonEmptyReadonlyArray } from "../Array.js";
33
import { assert, assertNonEmptyArray } from "../Assert.js";
4-
import { createCallbacks } from "../Callbacks.js";
4+
import { createCallbackRegistry } from "../CallbackRegistry.js";
55
import { ConsoleDep } from "../Console.js";
66
import { SymmetricCryptoDecryptError } from "../Crypto.js";
77
import { eqArrayNumber } from "../Eq.js";
@@ -673,7 +673,7 @@ const createEvoluInstance =
673673

674674
const subscribedQueries = createSubscribedQueries(rowsStore);
675675
const loadingPromises = createLoadingPromises(subscribedQueries);
676-
const callbacks = createCallbacks(deps);
676+
const callbackRegistry = createCallbackRegistry(deps);
677677

678678
const appState = deps.createAppState(config);
679679
const dbWorker = deps.createDbWorker(config.name);
@@ -726,7 +726,7 @@ const createEvoluInstance =
726726
}
727727

728728
for (const id of message.onCompleteIds) {
729-
callbacks.execute(id);
729+
callbackRegistry.execute(id);
730730
}
731731
break;
732732
}
@@ -746,13 +746,13 @@ const createEvoluInstance =
746746
if (message.reload) {
747747
appState.reset();
748748
} else {
749-
callbacks.execute(message.onCompleteId);
749+
callbackRegistry.execute(message.onCompleteId);
750750
}
751751
break;
752752
}
753753

754754
case "onExport": {
755-
callbacks.execute(message.onCompleteId, message.file);
755+
callbackRegistry.execute(message.onCompleteId, message.file);
756756
break;
757757
}
758758

@@ -867,7 +867,7 @@ const createEvoluInstance =
867867
}
868868

869869
const onCompleteIds = onCompletes.map((onComplete) =>
870-
callbacks.register(onComplete),
870+
callbackRegistry.register(onComplete),
871871
);
872872

873873
loadingPromises.releaseUnsubscribed();
@@ -960,7 +960,7 @@ const createEvoluInstance =
960960
// Eslint bug, Promise<void> is correct by docs.
961961
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
962962
const { promise, resolve } = Promise.withResolvers<void>();
963-
const onCompleteId = callbacks.register(() => {
963+
const onCompleteId = callbackRegistry.register(() => {
964964
resolve();
965965
});
966966
dbWorker.postMessage({
@@ -975,7 +975,7 @@ const createEvoluInstance =
975975
// Eslint bug, Promise<void> is correct by docs.
976976
// eslint-disable-next-line @typescript-eslint/no-invalid-void-type
977977
const { promise, resolve } = Promise.withResolvers<void>();
978-
const onCompleteId = callbacks.register(() => {
978+
const onCompleteId = callbackRegistry.register(() => {
979979
resolve();
980980
});
981981

@@ -1000,7 +1000,7 @@ const createEvoluInstance =
10001000

10011001
exportDatabase: () => {
10021002
const { promise, resolve } = Promise.withResolvers<Uint8Array>();
1003-
const onCompleteId = callbacks.register((arg) => {
1003+
const onCompleteId = callbackRegistry.register((arg) => {
10041004
if (arg instanceof Uint8Array) resolve(arg);
10051005
});
10061006
dbWorker.postMessage({ type: "export", onCompleteId });

packages/common/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ export * from "./Assert.js";
33
export * from "./BigInt.js";
44
export * from "./Brand.js";
55
export * from "./Buffer.js";
6-
export * from "./Callbacks.js";
6+
export * from "./CallbackRegistry.js";
77
export * from "./Console.js";
88
export * from "./Crypto.js";
99
export * from "./Eq.js";
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { expect, test } from "vitest";
2+
import { createCallbackRegistry } from "../src/CallbackRegistry.js";
3+
import { testNanoIdLibDep } from "./_deps.js";
4+
5+
test("CallbackRegistry", () => {
6+
const registry = createCallbackRegistry(testNanoIdLibDep);
7+
8+
let called = false;
9+
const id = registry.register(() => {
10+
called = true;
11+
});
12+
13+
expect(id).toBeDefined();
14+
registry.execute(id);
15+
expect(called).toBe(true);
16+
17+
called = false;
18+
registry.execute(id);
19+
expect(called).toBe(false);
20+
});

packages/common/test/Callbacks.test.ts

Lines changed: 0 additions & 19 deletions
This file was deleted.

packages/common/test/Evolu/Db.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { describe, expect, test } from "vitest";
2-
import { CallbackId } from "../../src/Callbacks.js";
2+
import { CallbackId } from "../../src/CallbackRegistry.js";
33
import { createConsole } from "../../src/Console.js";
44
import { defaultConfig } from "../../src/Evolu/Config.js";
55
import {

packages/common/test/Evolu/Relay.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ test("deleteOwner", async () => {
6363
change: new Uint8Array([1, 2, 3]) as EncryptedDbChange,
6464
};
6565

66-
storage.writeMessages(testOwnerBinaryId, [message]);
66+
await storage.writeMessages(testOwnerBinaryId, [message]);
6767

6868
expect(storage.getSize(testOwnerBinaryId)).toBe(1);
6969

packages/common/typedoc.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
"src/BigInt.ts",
1717
"src/Brand.ts",
1818
"src/Buffer.ts",
19-
"src/Callbacks.ts",
19+
"src/CallbackRegistry.ts",
2020
"src/Console.ts",
2121
"src/Crypto.ts",
2222
"src/Eq.ts",

0 commit comments

Comments
 (0)