@evolu/common 6.0.1-preview.19 → 6.0.1-preview.20

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 (140) hide show
  1. package/dist/src/Assert.d.ts.map +1 -1
  2. package/dist/src/Assert.js +1 -1
  3. package/dist/src/Buffer.d.ts +1 -1
  4. package/dist/src/Buffer.d.ts.map +1 -1
  5. package/dist/src/Buffer.js +1 -1
  6. package/dist/src/CallbackRegistry.d.ts +53 -0
  7. package/dist/src/CallbackRegistry.d.ts.map +1 -0
  8. package/dist/src/CallbackRegistry.js +25 -0
  9. package/dist/src/Console.d.ts +31 -6
  10. package/dist/src/Console.d.ts.map +1 -1
  11. package/dist/src/Console.js +72 -9
  12. package/dist/src/Crypto.d.ts +48 -37
  13. package/dist/src/Crypto.d.ts.map +1 -1
  14. package/dist/src/Crypto.js +27 -50
  15. package/dist/src/Evolu/Db.d.ts +138 -66
  16. package/dist/src/Evolu/Db.d.ts.map +1 -1
  17. package/dist/src/Evolu/Db.js +248 -645
  18. package/dist/src/Evolu/Diff.d.ts +3 -3
  19. package/dist/src/Evolu/Diff.d.ts.map +1 -1
  20. package/dist/src/Evolu/Diff.js +7 -5
  21. package/dist/src/Evolu/Evolu.d.ts +79 -116
  22. package/dist/src/Evolu/Evolu.d.ts.map +1 -1
  23. package/dist/src/Evolu/Evolu.js +275 -132
  24. package/dist/src/Evolu/Internal.d.ts +0 -2
  25. package/dist/src/Evolu/Internal.d.ts.map +1 -1
  26. package/dist/src/Evolu/Internal.js +0 -2
  27. package/dist/src/Evolu/LocalAuth.d.ts +144 -0
  28. package/dist/src/Evolu/LocalAuth.d.ts.map +1 -0
  29. package/dist/src/Evolu/LocalAuth.js +171 -0
  30. package/dist/src/Evolu/Owner.d.ts +129 -83
  31. package/dist/src/Evolu/Owner.d.ts.map +1 -1
  32. package/dist/src/Evolu/Owner.js +80 -89
  33. package/dist/src/Evolu/Platform.d.ts +9 -7
  34. package/dist/src/Evolu/Platform.d.ts.map +1 -1
  35. package/dist/src/Evolu/Protocol.d.ts +114 -191
  36. package/dist/src/Evolu/Protocol.d.ts.map +1 -1
  37. package/dist/src/Evolu/Protocol.js +409 -416
  38. package/dist/src/Evolu/Public.d.ts +6 -8
  39. package/dist/src/Evolu/Public.d.ts.map +1 -1
  40. package/dist/src/Evolu/Public.js +2 -3
  41. package/dist/src/Evolu/PublicKysely.js +3 -3
  42. package/dist/src/Evolu/Relay.d.ts +1 -2
  43. package/dist/src/Evolu/Relay.d.ts.map +1 -1
  44. package/dist/src/Evolu/Relay.js +11 -9
  45. package/dist/src/Evolu/Schema.d.ts +88 -27
  46. package/dist/src/Evolu/Schema.d.ts.map +1 -1
  47. package/dist/src/Evolu/Schema.js +141 -24
  48. package/dist/src/Evolu/Storage.d.ts +158 -14
  49. package/dist/src/Evolu/Storage.d.ts.map +1 -1
  50. package/dist/src/Evolu/Storage.js +32 -32
  51. package/dist/src/Evolu/Sync.d.ts +77 -13
  52. package/dist/src/Evolu/Sync.d.ts.map +1 -1
  53. package/dist/src/Evolu/Sync.js +453 -20
  54. package/dist/src/Evolu/Timestamp.d.ts +29 -27
  55. package/dist/src/Evolu/Timestamp.d.ts.map +1 -1
  56. package/dist/src/Evolu/Timestamp.js +20 -18
  57. package/dist/src/ManyToManyMap.d.ts +74 -10
  58. package/dist/src/ManyToManyMap.d.ts.map +1 -1
  59. package/dist/src/ManyToManyMap.js +41 -6
  60. package/dist/src/Random.d.ts +3 -2
  61. package/dist/src/Random.d.ts.map +1 -1
  62. package/dist/src/RefCountedResourceManager.d.ts +119 -0
  63. package/dist/src/RefCountedResourceManager.d.ts.map +1 -0
  64. package/dist/src/RefCountedResourceManager.js +197 -0
  65. package/dist/src/Result.d.ts +144 -22
  66. package/dist/src/Result.d.ts.map +1 -1
  67. package/dist/src/Result.js +5 -2
  68. package/dist/src/Sqlite.d.ts +20 -4
  69. package/dist/src/Sqlite.d.ts.map +1 -1
  70. package/dist/src/Sqlite.js +50 -8
  71. package/dist/src/Task.d.ts +511 -0
  72. package/dist/src/Task.d.ts.map +1 -0
  73. package/dist/src/Task.js +410 -0
  74. package/dist/src/Time.d.ts +59 -0
  75. package/dist/src/Time.d.ts.map +1 -1
  76. package/dist/src/Time.js +87 -4
  77. package/dist/src/Type.d.ts +431 -341
  78. package/dist/src/Type.d.ts.map +1 -1
  79. package/dist/src/Type.js +458 -466
  80. package/dist/src/WebSocket.d.ts +5 -2
  81. package/dist/src/WebSocket.d.ts.map +1 -1
  82. package/dist/src/WebSocket.js +12 -13
  83. package/dist/src/Worker.d.ts +39 -11
  84. package/dist/src/Worker.d.ts.map +1 -1
  85. package/dist/src/Worker.js +22 -4
  86. package/dist/src/index.d.ts +2 -3
  87. package/dist/src/index.d.ts.map +1 -1
  88. package/dist/src/index.js +2 -3
  89. package/package.json +7 -7
  90. package/src/Assert.ts +2 -4
  91. package/src/Buffer.ts +1 -1
  92. package/src/CallbackRegistry.ts +84 -0
  93. package/src/Console.ts +91 -11
  94. package/src/Crypto.ts +78 -91
  95. package/src/Evolu/Db.ts +455 -947
  96. package/src/Evolu/Diff.ts +7 -5
  97. package/src/Evolu/Evolu.ts +545 -307
  98. package/src/Evolu/Internal.ts +0 -2
  99. package/src/Evolu/LocalAuth.ts +422 -0
  100. package/src/Evolu/Owner.ts +191 -131
  101. package/src/Evolu/Platform.ts +9 -9
  102. package/src/Evolu/Protocol.ts +536 -653
  103. package/src/Evolu/Public.ts +7 -9
  104. package/src/Evolu/PublicKysely.ts +3 -3
  105. package/src/Evolu/Relay.ts +17 -12
  106. package/src/Evolu/Schema.ts +271 -66
  107. package/src/Evolu/Storage.ts +263 -55
  108. package/src/Evolu/Sync.ts +758 -37
  109. package/src/Evolu/Timestamp.ts +30 -35
  110. package/src/ManyToManyMap.ts +127 -24
  111. package/src/Random.ts +3 -2
  112. package/src/RefCountedResourceManager.ts +368 -0
  113. package/src/Result.ts +149 -23
  114. package/src/Sqlite.ts +59 -24
  115. package/src/Task.ts +779 -0
  116. package/src/Time.ts +168 -4
  117. package/src/Type.ts +657 -695
  118. package/src/WebSocket.ts +23 -17
  119. package/src/Worker.ts +72 -23
  120. package/src/index.ts +2 -3
  121. package/dist/src/Callbacks.d.ts +0 -20
  122. package/dist/src/Callbacks.d.ts.map +0 -1
  123. package/dist/src/Callbacks.js +0 -18
  124. package/dist/src/Evolu/Config.d.ts +0 -82
  125. package/dist/src/Evolu/Config.d.ts.map +0 -1
  126. package/dist/src/Evolu/Config.js +0 -9
  127. package/dist/src/Evolu/Kysely.d.ts +0 -6
  128. package/dist/src/Evolu/Kysely.d.ts.map +0 -1
  129. package/dist/src/Evolu/Kysely.js +0 -21
  130. package/dist/src/NanoId.d.ts +0 -27
  131. package/dist/src/NanoId.d.ts.map +0 -1
  132. package/dist/src/NanoId.js +0 -6
  133. package/dist/src/Promise.d.ts +0 -180
  134. package/dist/src/Promise.d.ts.map +0 -1
  135. package/dist/src/Promise.js +0 -176
  136. package/src/Callbacks.ts +0 -43
  137. package/src/Evolu/Config.ts +0 -97
  138. package/src/Evolu/Kysely.ts +0 -38
  139. package/src/NanoId.ts +0 -39
  140. package/src/Promise.ts +0 -295
@@ -3,8 +3,8 @@
3
3
  *
4
4
  * @module
5
5
  */
6
- import { RetryError, RetryOptions } from "./Promise.js";
7
6
  import { Result } from "./Result.js";
7
+ import { RetryError, RetryOptions } from "./Task.js";
8
8
  /** WebSocket with auto-reconnect and offline support. */
9
9
  export interface WebSocket extends Disposable {
10
10
  /**
@@ -13,9 +13,12 @@ export interface WebSocket extends Disposable {
13
13
  */
14
14
  send: (data: string | ArrayBufferLike | Blob | ArrayBufferView) => Result<void, WebSocketSendError>;
15
15
  readonly getReadyState: () => WebSocketReadyState;
16
+ /** Returns true if the WebSocket is open and ready to send data. */
17
+ readonly isOpen: () => boolean;
16
18
  }
17
19
  /**
18
- * An error that occurs when trying to send data but WebSocket is connecting.
20
+ * An error that occurs when trying to send data but WebSocket is not available
21
+ * or is in the CONNECTING state.
19
22
  *
20
23
  * https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
21
24
  */
@@ -1 +1 @@
1
- {"version":3,"file":"WebSocket.d.ts","sourceRoot":"","sources":["../../src/WebSocket.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAS,UAAU,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC/D,OAAO,EAAW,MAAM,EAAE,MAAM,aAAa,CAAC;AAE9C,yDAAyD;AACzD,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C;;;OAGG;IACH,IAAI,EAAE,CACJ,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,GAAG,eAAe,KACpD,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;IAEtC,QAAQ,CAAC,aAAa,EAAE,MAAM,mBAAmB,CAAC;CACnD;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;CACrC;AAED,mCAAmC;AACnC,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/E,MAAM,MAAM,eAAe,GAAG,CAC5B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,KACvB,SAAS,CAAC;AAEf,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAEnC,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC;IAEpC,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAE1C,8CAA8C;IAC9C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAEtC,8CAA8C;IAC9C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAExD,kCAAkC;IAClC,YAAY,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,EAAE,QAAQ,CAAC,CAAC;IAEjE;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,OAAO,UAAU,CAAC,SAAS,CAAC;CACpD;AAED,MAAM,MAAM,cAAc,GACtB,qBAAqB,GACrB,wBAAwB,GACxB,UAAU,CAAC,mBAAmB,CAAC,CAAC;AAEpC;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED,MAAM,MAAM,mBAAmB,GAC3B,qBAAqB,GACrB,6BAA6B,CAAC;AAElC,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,eAAe,EAAE,eA0H7B,CAAC"}
1
+ {"version":3,"file":"WebSocket.d.ts","sourceRoot":"","sources":["../../src/WebSocket.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAW,MAAM,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,EAAS,UAAU,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG5D,yDAAyD;AACzD,MAAM,WAAW,SAAU,SAAQ,UAAU;IAC3C;;;OAGG;IACH,IAAI,EAAE,CACJ,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,IAAI,GAAG,eAAe,KACpD,MAAM,CAAC,IAAI,EAAE,kBAAkB,CAAC,CAAC;IAEtC,QAAQ,CAAC,aAAa,EAAE,MAAM,mBAAmB,CAAC;IAElD,oEAAoE;IACpE,QAAQ,CAAC,MAAM,EAAE,MAAM,OAAO,CAAC;CAChC;AAED;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,oBAAoB,CAAC;CACrC;AAED,mCAAmC;AACnC,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE/E,MAAM,MAAM,eAAe,GAAG,CAC5B,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,gBAAgB,KACvB,SAAS,CAAC;AAEf,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,eAAe,EAAE,eAAe,CAAC;CAC3C;AAED,6CAA6C;AAC7C,MAAM,WAAW,gBAAgB;IAC/B,wDAAwD;IACxD,SAAS,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC;IAEnC,wDAAwD;IACxD,UAAU,CAAC,EAAE,MAAM,GAAG,aAAa,CAAC;IAEpC,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IAEpB,qCAAqC;IACrC,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,cAAc,KAAK,IAAI,CAAC;IAE1C,8CAA8C;IAC9C,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IAEtC,8CAA8C;IAC9C,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,KAAK,IAAI,CAAC;IAExD,kCAAkC;IAClC,YAAY,CAAC,EAAE,IAAI,CAAC,YAAY,CAAC,mBAAmB,CAAC,EAAE,QAAQ,CAAC,CAAC;IAEjE;;;;;;OAMG;IACH,oBAAoB,CAAC,EAAE,OAAO,UAAU,CAAC,SAAS,CAAC;CACpD;AAED,MAAM,MAAM,cAAc,GACtB,qBAAqB,GACrB,wBAAwB,GACxB,UAAU,CAAC,mBAAmB,CAAC,CAAC;AAEpC;;;GAGG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,IAAI,EAAE,uBAAuB,CAAC;IACvC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED;;;;;GAKG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,IAAI,EAAE,0BAA0B,CAAC;IAC1C,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;CACvB;AAED,MAAM,MAAM,mBAAmB,GAC3B,qBAAqB,GACrB,6BAA6B,CAAC;AAElC,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,CAAC,IAAI,EAAE,+BAA+B,CAAC;IAC/C,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,eAAe,EAAE,eA2H7B,CAAC"}
@@ -4,8 +4,9 @@
4
4
  * @module
5
5
  */
6
6
  import { assertNoErrorInCatch } from "./Assert.js";
7
- import { retry } from "./Promise.js";
8
7
  import { err, ok } from "./Result.js";
8
+ import { retry } from "./Task.js";
9
+ import { maxPositiveInt } from "./Type.js";
9
10
  /**
10
11
  * Create a new {@link WebSocket}.
11
12
  *
@@ -37,13 +38,11 @@ import { err, ok } from "./Result.js";
37
38
  *
38
39
  * TODO:
39
40
  */
40
- export const createWebSocket = (url, options = {}) => {
41
- const { protocols, binaryType, onOpen, onClose, onMessage, onError, retryOptions, WebSocketConstructor = globalThis.WebSocket, } = options;
41
+ export const createWebSocket = (url, { protocols, binaryType, onOpen, onClose, onMessage, onError, retryOptions, WebSocketConstructor = globalThis.WebSocket, } = {}) => {
42
42
  let isDisposed = false;
43
43
  const reconnectController = new AbortController();
44
44
  const defaultRetryOptions = {
45
- signal: reconnectController.signal,
46
- maxRetries: Infinity,
45
+ retries: maxPositiveInt, // Practically infinite retries
47
46
  };
48
47
  let socket = null;
49
48
  const disposeSocket = () => {
@@ -66,7 +65,10 @@ export const createWebSocket = (url, options = {}) => {
66
65
  * - Is rejected when a connection is closed.
67
66
  * - Is resolved when WebSocket is disposed().
68
67
  */
69
- retry(() => new Promise((resolve) => {
68
+ retry({
69
+ ...defaultRetryOptions,
70
+ ...retryOptions,
71
+ }, () => new Promise((resolve) => {
70
72
  disposePromise = () => {
71
73
  resolve(ok());
72
74
  };
@@ -98,19 +100,16 @@ export const createWebSocket = (url, options = {}) => {
98
100
  socket.onmessage = (event) => {
99
101
  onMessage?.(event.data);
100
102
  };
101
- }), {
102
- ...defaultRetryOptions,
103
- ...retryOptions,
104
- })
103
+ }))(reconnectController)
105
104
  .then((result) => {
106
- if (result.ok || result.error.type === "RetryAbortError")
105
+ if (result.ok || result.error.type === "AbortError")
107
106
  return;
108
107
  onError?.(result.error);
109
108
  })
110
109
  .catch((error) => {
111
110
  assertNoErrorInCatch("WebSocket retry", error);
112
111
  });
113
- const webSocket = {
112
+ return {
114
113
  send: (data) => {
115
114
  // https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send
116
115
  if (!socket || socket.readyState === socket.CONNECTING) {
@@ -120,6 +119,7 @@ export const createWebSocket = (url, options = {}) => {
120
119
  return ok();
121
120
  },
122
121
  getReadyState: () => socket ? nativeToStringState[socket.readyState] : "connecting",
122
+ isOpen: () => (socket ? socket.readyState === socket.OPEN : false),
123
123
  [Symbol.dispose]() {
124
124
  if (isDisposed)
125
125
  return;
@@ -129,7 +129,6 @@ export const createWebSocket = (url, options = {}) => {
129
129
  disposePromise?.();
130
130
  },
131
131
  };
132
- return webSocket;
133
132
  };
134
133
  const nativeToStringState = {
135
134
  [WebSocket.CONNECTING]: "connecting",
@@ -14,6 +14,21 @@ export interface Worker<Input, Output> {
14
14
  export interface WorkerPostMessageDep<Output> {
15
15
  readonly postMessage: (message: Output) => void;
16
16
  }
17
+ /**
18
+ * Error reporting wrapper that catches synchronous errors in handlers and
19
+ * converts them to transferable error messages sent to the main thread.
20
+ */
21
+ export type WithErrorReporting = <A extends Array<any>>(handler: (...args: A) => void) => (...args: A) => void;
22
+ type HasInit<Input> = Extract<Input, {
23
+ type: "init";
24
+ }> extends never ? ["Input must contain a variant with { type: 'init' }"] : unknown;
25
+ type HasWorkerErrorOutput<T> = Extract<T, {
26
+ type: "onError";
27
+ }> extends infer E ? [E] extends [never] ? [
28
+ "Output must contain { type: 'onError'; error: TransferableError | ... }"
29
+ ] : E extends {
30
+ error: infer Err;
31
+ } ? TransferableError extends Err ? unknown : ["Output.onError.error must include TransferableError"] : ["Output.onError must have an error property"] : never;
17
32
  /**
18
33
  * Creates a {@link Worker} that supports initialization with dependencies and
19
34
  * safe error handling.
@@ -25,20 +40,33 @@ export declare const createInitializedWorker: <Input extends {
25
40
  } & HasWorkerErrorOutput<Output>, Deps>({ init, onMessage, }: {
26
41
  readonly init: (initMessage: Extract<Input, {
27
42
  type: "init";
28
- }>, postMessage: (msg: Output) => void, safeHandler: <A extends Array<any>>(handler: (...args: A) => void) => (...args: A) => void) => Promise<Deps | null>;
43
+ }>, postMessage: (msg: Output) => void, withErrorReporting: WithErrorReporting) => Promise<Deps | null>;
29
44
  readonly onMessage: (deps: Deps) => (message: Exclude<Input, {
30
45
  type: "init";
31
46
  }>) => void;
32
47
  }) => Worker<Input, Output>;
33
- type HasInit<Input> = Extract<Input, {
34
- type: "init";
35
- }> extends never ? ["Input must contain a variant with { type: 'init' }"] : unknown;
36
- type HasWorkerErrorOutput<T> = Extract<T, {
37
- type: "onError";
38
- }> extends infer E ? [E] extends [never] ? [
39
- "Output must contain { type: 'onError'; error: TransferableError | ... }"
40
- ] : E extends {
41
- error: infer Err;
42
- } ? TransferableError extends Err ? unknown : ["Output.onError.error must include TransferableError"] : ["Output.onError must have an error property"] : never;
48
+ /** Type helper to extract message types from a union type */
49
+ export type MessageHandlers<Input extends {
50
+ readonly type: string;
51
+ }, Deps> = {
52
+ readonly [K in Input["type"]]: (deps: Deps) => (message: Extract<Input, {
53
+ type: K;
54
+ }>) => void;
55
+ };
56
+ /**
57
+ * Creates a {@link Worker} with type-safe message handlers for each message
58
+ * type. This provides better type safety and organization compared to a single
59
+ * onMessage handler.
60
+ */
61
+ export declare const createInitializedWorkerWithHandlers: <Input extends {
62
+ readonly type: string;
63
+ } & HasInit<Input>, Output extends {
64
+ readonly type: string;
65
+ } & HasWorkerErrorOutput<Output>, Deps>({ init, handlers, }: {
66
+ readonly init: (initMessage: Extract<Input, {
67
+ type: "init";
68
+ }>, postMessage: (msg: Output) => void, withErrorReporting: WithErrorReporting) => Promise<Deps | null>;
69
+ readonly handlers: Omit<MessageHandlers<Input, Deps>, "init">;
70
+ }) => Worker<Input, Output>;
43
71
  export {};
44
72
  //# sourceMappingURL=Worker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"Worker.d.ts","sourceRoot":"","sources":["../../src/Worker.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAA2B,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAExE,yCAAyC;AACzC,MAAM,WAAW,MAAM,CAAC,KAAK,EAAE,MAAM;IACnC,qCAAqC;IACrC,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,KAAK,KAAK,IAAI,CAAC;IAE/C,oDAAoD;IACpD,QAAQ,CAAC,SAAS,EAAE,CAAC,QAAQ,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,KAAK,IAAI,CAAC;CACnE;AAED,MAAM,WAAW,oBAAoB,CAAC,MAAM;IAC1C,QAAQ,CAAC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACjD;AAED;;;GAGG;AACH,eAAO,MAAM,uBAAuB,GAClC,KAAK,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,OAAO,CAAC,KAAK,CAAC,EACxD,MAAM,SAAS;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,oBAAoB,CAAC,MAAM,CAAC,EACvE,IAAI,EACJ,sBAGC;IACD,QAAQ,CAAC,IAAI,EAAE,CACb,WAAW,EAAE,OAAO,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,EAC7C,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,EAClC,WAAW,EAAE,CAAC,CAAC,SAAS,KAAK,CAAC,GAAG,CAAC,EAChC,OAAO,EAAE,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,IAAI,KAC1B,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,IAAI,KACtB,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,CAClB,IAAI,EAAE,IAAI,KACP,CAAC,OAAO,EAAE,OAAO,CAAC,KAAK,EAAE;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,KAAK,IAAI,CAAC;CAC1D,KAAG,MAAM,CAAC,KAAK,EAAE,MAAM,CAkEvB,CAAC;AAEF,KAAK,OAAO,CAAC,KAAK,IAChB,OAAO,CAAC,KAAK,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,SAAS,KAAK,GAC1C,CAAC,oDAAoD,CAAC,GACtD,OAAO,CAAC;AAEd,KAAK,oBAAoB,CAAC,CAAC,IACzB,OAAO,CAAC,CAAC,EAAE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC,SAAS,MAAM,CAAC,GAC3C,CAAC,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,GACjB;IACE,yEAAyE;CAC1E,GACD,CAAC,SAAS;IAAE,KAAK,EAAE,MAAM,GAAG,CAAA;CAAE,GAC5B,iBAAiB,SAAS,GAAG,GAC3B,OAAO,GACP,CAAC,qDAAqD,CAAC,GACzD,CAAC,4CAA4C,CAAC,GAClD,KAAK,CAAC"}
1
+ {"version":3,"file":"Worker.d.ts","sourceRoot":"","sources":["../../src/Worker.ts"],"names":[],"mappings":"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"}
@@ -24,7 +24,11 @@ export const createInitializedWorker = ({ init, onMessage, }) => {
24
24
  error: createTransferableError(error),
25
25
  });
26
26
  };
27
- const safeHandler = (handler) => (...args) => {
27
+ /**
28
+ * Wraps function to catch errors and send them to the main thread instead of
29
+ * crashing the worker.
30
+ */
31
+ const withErrorReporting = (handler) => (...args) => {
28
32
  try {
29
33
  handler(...args);
30
34
  }
@@ -39,20 +43,20 @@ export const createInitializedWorker = ({ init, onMessage, }) => {
39
43
  pendingMessages.push(message);
40
44
  }
41
45
  else {
42
- safeHandler(onMessage(deps))(message);
46
+ withErrorReporting(onMessage(deps))(message);
43
47
  }
44
48
  return;
45
49
  }
46
50
  if (initializing)
47
51
  return;
48
52
  initializing = true;
49
- init(message, postMessage, safeHandler)
53
+ init(message, postMessage, withErrorReporting)
50
54
  .then((_deps) => {
51
55
  if (_deps == null)
52
56
  return;
53
57
  deps = _deps;
54
58
  for (const message of pendingMessages) {
55
- safeHandler(onMessage(deps))(message);
59
+ withErrorReporting(onMessage(deps))(message);
56
60
  }
57
61
  pendingMessages.length = 0;
58
62
  })
@@ -64,3 +68,17 @@ export const createInitializedWorker = ({ init, onMessage, }) => {
64
68
  };
65
69
  return worker;
66
70
  };
71
+ /**
72
+ * Creates a {@link Worker} with type-safe message handlers for each message
73
+ * type. This provides better type safety and organization compared to a single
74
+ * onMessage handler.
75
+ */
76
+ export const createInitializedWorkerWithHandlers = ({ init, handlers, }) => createInitializedWorker({
77
+ init,
78
+ onMessage: (deps) => (message) => {
79
+ const messageType = message.type;
80
+ const handler = handlers[messageType];
81
+ // TypeScript knows handler exists because handlers covers all non-init message types
82
+ handler(deps)(message);
83
+ },
84
+ });
@@ -3,7 +3,7 @@ export * from "./Assert.js";
3
3
  export * from "./BigInt.js";
4
4
  export * from "./Brand.js";
5
5
  export * from "./Buffer.js";
6
- export * from "./Callbacks.js";
6
+ export * from "./CallbackRegistry.js";
7
7
  export * from "./Console.js";
8
8
  export * from "./Crypto.js";
9
9
  export * from "./Eq.js";
@@ -11,17 +11,16 @@ export * from "./Error.js";
11
11
  export * from "./Evolu/Public.js";
12
12
  export * from "./Function.js";
13
13
  export * from "./ManyToManyMap.js";
14
- export * from "./NanoId.js";
15
14
  export * from "./Number.js";
16
15
  export * from "./Object.js";
17
16
  export * from "./Order.js";
18
- export * from "./Promise.js";
19
17
  export * from "./Random.js";
20
18
  export * from "./Ref.js";
21
19
  export * from "./Result.js";
22
20
  export * from "./Sqlite.js";
23
21
  export * from "./Store.js";
24
22
  export * from "./String.js";
23
+ export * from "./Task.js";
25
24
  export * from "./Time.js";
26
25
  export * from "./Type.js";
27
26
  export * from "./Types.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,uBAAuB,CAAC;AACtC,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,SAAS,CAAC;AACxB,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC"}
package/dist/src/index.js CHANGED
@@ -3,7 +3,7 @@ export * from "./Assert.js";
3
3
  export * from "./BigInt.js";
4
4
  export * from "./Brand.js";
5
5
  export * from "./Buffer.js";
6
- export * from "./Callbacks.js";
6
+ export * from "./CallbackRegistry.js";
7
7
  export * from "./Console.js";
8
8
  export * from "./Crypto.js";
9
9
  export * from "./Eq.js";
@@ -11,17 +11,16 @@ export * from "./Error.js";
11
11
  export * from "./Evolu/Public.js";
12
12
  export * from "./Function.js";
13
13
  export * from "./ManyToManyMap.js";
14
- export * from "./NanoId.js";
15
14
  export * from "./Number.js";
16
15
  export * from "./Object.js";
17
16
  export * from "./Order.js";
18
- export * from "./Promise.js";
19
17
  export * from "./Random.js";
20
18
  export * from "./Ref.js";
21
19
  export * from "./Result.js";
22
20
  export * from "./Sqlite.js";
23
21
  export * from "./Store.js";
24
22
  export * from "./String.js";
23
+ export * from "./Task.js";
25
24
  export * from "./Time.js";
26
25
  export * from "./Type.js";
27
26
  export * from "./Types.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@evolu/common",
3
- "version": "6.0.1-preview.19",
3
+ "version": "6.0.1-preview.20",
4
4
  "description": "TypeScript library and local-first framework",
5
5
  "keywords": [
6
6
  "evolu",
@@ -42,12 +42,11 @@
42
42
  "README.md"
43
43
  ],
44
44
  "dependencies": {
45
- "@noble/ciphers": "^1.3.0",
46
- "@noble/hashes": "^1.8.0",
47
- "@scure/bip39": "^1.6.0",
45
+ "@noble/ciphers": "^2.0.0",
46
+ "@noble/hashes": "^2.0.0",
47
+ "@scure/bip39": "^2.0.0",
48
48
  "kysely": "^0.28.4",
49
49
  "msgpackr": "^1.11.5",
50
- "nanoid": "^3.3.11",
51
50
  "random": "^5.4.1"
52
51
  },
53
52
  "devDependencies": {
@@ -55,9 +54,10 @@
55
54
  "@types/better-sqlite3": "^7.6.13",
56
55
  "@types/ws": "^8.18.1",
57
56
  "better-sqlite3": "^12.1.1",
58
- "shx": "^0.3.4",
57
+ "fast-check": "^4.2.0",
58
+ "shx": "^0.4.0",
59
59
  "typescript": "^5.9.2",
60
- "vitest": "^3.2.3",
60
+ "vitest": "^4.0.4",
61
61
  "ws": "^8.18.2",
62
62
  "@evolu/tsconfig": "0.0.2"
63
63
  },
package/src/Assert.ts CHANGED
@@ -110,10 +110,8 @@ export const assertNonEmptyReadonlyArray: <T>(
110
110
  * ```
111
111
  */
112
112
  export function assertNoErrorInCatch(context: string, error: unknown): never {
113
- throw Object.assign(
114
- new Error(
115
- `Error in ${context}: an unexpected error reached a catch block and requires a fix`,
116
- ),
113
+ throw new Error(
114
+ `Error in ${context}: an unexpected error reached a catch block and requires a fix`,
117
115
  { cause: error },
118
116
  );
119
117
  }
package/src/Buffer.ts CHANGED
@@ -6,7 +6,7 @@ export {
6
6
  concatBytes,
7
7
  hexToBytes,
8
8
  utf8ToBytes,
9
- } from "@noble/ciphers/utils";
9
+ } from "@noble/ciphers/utils.js";
10
10
 
11
11
  /**
12
12
  * Custom error for {@link Buffer}-related failures like premature end of data.
@@ -0,0 +1,84 @@
1
+ import { Brand } from "./Brand.js";
2
+ import { RandomBytesDep } from "./Crypto.js";
3
+ import { Result } from "./Result.js";
4
+ import { createId, Id } from "./Type.js";
5
+
6
+ /**
7
+ * A registry for one-time callback functions.
8
+ *
9
+ * Stores callbacks with unique IDs and executes them once with an optional
10
+ * argument. Executed callbacks are automatically removed from the registry.
11
+ *
12
+ * This is useful for correlating asynchronous operations across boundaries
13
+ * where callback functions cannot be passed directly (e.g., web workers).
14
+ *
15
+ * The `execute` method intentionally does not use try-catch or {@link Result}
16
+ * because it's the callback's responsibility to handle its own errors. The
17
+ * registry is just a correlation mechanism and should not interfere with error
18
+ * handling or debugging by masking the original error location.
19
+ *
20
+ * ### Example
21
+ *
22
+ * ```ts
23
+ * // No-argument callbacks
24
+ * const registry = createCallbackRegistry(deps);
25
+ * const id = registry.register(() => console.log("called"));
26
+ * registry.execute(id);
27
+ *
28
+ * // With argument callbacks
29
+ * const stringRegistry = createCallbackRegistry<string>(deps);
30
+ * const id = stringRegistry.register((value) => {
31
+ * console.log(value);
32
+ * });
33
+ * stringRegistry.execute(id, "hello");
34
+ *
35
+ * // Promise.withResolvers pattern
36
+ * const promiseRegistry = createCallbackRegistry<string>(deps);
37
+ * const { promise, resolve } = Promise.withResolvers<string>();
38
+ * const id = promiseRegistry.register(resolve);
39
+ * promiseRegistry.execute(id, "resolved value");
40
+ * await promise; // "resolved value"
41
+ * ```
42
+ *
43
+ * @template T - The type of argument passed to callbacks (defaults to undefined
44
+ * for no-argument callbacks)
45
+ */
46
+ export interface CallbackRegistry<T = undefined> {
47
+ /** Registers a callback function and returns a unique ID. */
48
+ readonly register: (callback: (arg: T) => void) => CallbackId;
49
+
50
+ /** Executes and removes a callback associated with the given ID. */
51
+ readonly execute: T extends undefined
52
+ ? (id: CallbackId) => undefined
53
+ : (id: CallbackId, arg: T) => undefined;
54
+ }
55
+
56
+ export type CallbackId = Id & Brand<"Callback">;
57
+
58
+ /** Creates a new {@link CallbackRegistry} for one-time callback functions. */
59
+ export const createCallbackRegistry = <T = undefined>(
60
+ deps: RandomBytesDep,
61
+ ): CallbackRegistry<T> => {
62
+ const callbackMap = new Map<CallbackId, (arg: T) => void>();
63
+
64
+ return {
65
+ register: (callback) => {
66
+ const id = createId<"Callback">(deps);
67
+ callbackMap.set(id, callback);
68
+ return id;
69
+ },
70
+
71
+ execute: (id: CallbackId, ...args: T extends undefined ? [] : [T]) => {
72
+ const callback = callbackMap.get(id);
73
+ if (callback) {
74
+ callbackMap.delete(id);
75
+ if (args.length === 0) {
76
+ // Called without argument (undefined case)
77
+ (callback as () => void)();
78
+ } else {
79
+ callback(args[0]);
80
+ }
81
+ }
82
+ },
83
+ } as CallbackRegistry<T>;
84
+ };
package/src/Console.ts CHANGED
@@ -13,14 +13,28 @@
13
13
  * ### Example
14
14
  *
15
15
  * ```ts
16
- * deps.console.log("[db]", "send data message", protocolMessage);
16
+ * deps.console.log("[evolu]", "createEvoluInstance", { name });
17
17
  * ```
18
18
  *
19
19
  * **Tip**: In browser dev tools, you can filter logs by tag (e.g., `[db]`) to
20
- * quickly find relevant messages.
20
+ * quickly find relevant messages. In Node.js, use `grep` to filter output:
21
21
  *
22
- * **Warning**: If you encounter platform-specific issues or missing methods,
23
- * please contribute a PR with details about the environment and behavior.
22
+ * ```bash
23
+ * node app.js | grep "\[relay\]" # Show only relay logs
24
+ * node app.js | grep -E "\[db\]|\[sql\]" # Show db and sql logs
25
+ * node app.js | grep -v "\[debug\]" # Hide debug logs
26
+ * ```
27
+ *
28
+ * Or add to package.json scripts:
29
+ *
30
+ * ```json
31
+ * {
32
+ * "scripts": {
33
+ * "dev:relay": "node app.js | grep \"\\[relay\\]\"",
34
+ * "dev:db": "node app.js | grep -E \"\\[db\\]|\\[sql\\]\""
35
+ * }
36
+ * }
37
+ * ```
24
38
  *
25
39
  * @module
26
40
  */
@@ -51,6 +65,9 @@ export interface Console {
51
65
  /** Starts a timer with an optional label */
52
66
  time: (label?: string) => void;
53
67
 
68
+ /** Logs the elapsed time for a timer without ending it */
69
+ timeLog: (label?: string, ...data: Array<any>) => void;
70
+
54
71
  /** Ends a timer and logs the elapsed time */
55
72
  timeEnd: (label?: string) => void;
56
73
 
@@ -69,9 +86,6 @@ export interface Console {
69
86
  /** Writes a message if the value is falsy, otherwise does nothing */
70
87
  assert: (value: any, message?: string, ...optionalParams: Array<any>) => void;
71
88
 
72
- /** Logs the elapsed time for a timer without ending it */
73
- timeLog: (label?: string, ...data: Array<any>) => void;
74
-
75
89
  /** Prints a stack trace with an optional message */
76
90
  trace: (message?: any, ...optionalParams: Array<any>) => void;
77
91
  }
@@ -121,6 +135,10 @@ export const createConsole = (config: ConsoleConfig = {}): Console => {
121
135
  // eslint-disable-next-line no-console
122
136
  if (instance.enabled) console.time(label);
123
137
  },
138
+ timeLog: (label, ...data) => {
139
+ // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
140
+ if (instance.enabled) console.timeLog(label, ...data);
141
+ },
124
142
  timeEnd: (label) => {
125
143
  // eslint-disable-next-line no-console
126
144
  if (instance.enabled) console.timeEnd(label);
@@ -145,10 +163,6 @@ export const createConsole = (config: ConsoleConfig = {}): Console => {
145
163
  // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
146
164
  if (instance.enabled) console.assert(value, message, ...optionalParams);
147
165
  },
148
- timeLog: (label, ...data) => {
149
- // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
150
- if (instance.enabled) console.timeLog(label, ...data);
151
- },
152
166
  trace: (message, ...optionalParams) => {
153
167
  // eslint-disable-next-line no-console, @typescript-eslint/no-unsafe-argument
154
168
  if (instance.enabled) console.trace(message, ...optionalParams);
@@ -157,3 +171,69 @@ export const createConsole = (config: ConsoleConfig = {}): Console => {
157
171
 
158
172
  return instance;
159
173
  };
174
+
175
+ export interface ConsoleWithTimeConfig extends ConsoleConfig {
176
+ /**
177
+ * Type of timestamp to prepend to log messages.
178
+ *
179
+ * - 'absolute': Shows actual time (e.g., "14:32:15.234")
180
+ * - 'relative': Shows time since console creation (e.g., "+1.234s")
181
+ */
182
+ readonly timestampType: "absolute" | "relative";
183
+ }
184
+
185
+ /** Creates a console instance with timestamp prefixes. */
186
+ export const createConsoleWithTime = (
187
+ config: ConsoleWithTimeConfig = { timestampType: "relative" },
188
+ ): Console => {
189
+ const console = createConsole(config);
190
+ const startTime = performance.now();
191
+
192
+ const getTimestamp = (): string => {
193
+ if (config.timestampType === "relative") {
194
+ const elapsed = (performance.now() - startTime) / 1000;
195
+
196
+ // Format for better readability at different time scales
197
+ if (elapsed < 60) {
198
+ // Under 1 minute: show seconds with millisecond precision
199
+ return `+${elapsed.toFixed(3)}s`;
200
+ } else if (elapsed < 3600) {
201
+ // 1 minute to 1 hour: show minutes and seconds with millisecond precision
202
+ const minutes = Math.floor(elapsed / 60);
203
+ const seconds = (elapsed % 60).toFixed(3);
204
+ return `+${minutes}m${seconds}s`;
205
+ } else {
206
+ // Over 1 hour: show hours, minutes, and seconds with millisecond precision
207
+ const hours = Math.floor(elapsed / 3600);
208
+ const minutes = Math.floor((elapsed % 3600) / 60);
209
+ const seconds = ((elapsed % 3600) % 60).toFixed(3);
210
+ return `+${hours}h${minutes}m${seconds}s`;
211
+ }
212
+ } else {
213
+ // Absolute time - format as HH:MM:SS.mmm
214
+ const now = new Date();
215
+ const hours = now.getHours().toString().padStart(2, "0");
216
+ const minutes = now.getMinutes().toString().padStart(2, "0");
217
+ const seconds = now.getSeconds().toString().padStart(2, "0");
218
+ const milliseconds = now.getMilliseconds().toString().padStart(3, "0");
219
+ return `${hours}:${minutes}:${seconds}.${milliseconds}`;
220
+ }
221
+ };
222
+
223
+ const withTimestamp =
224
+ (fn: (...args: Array<any>) => void) =>
225
+ (...args: Array<any>) => {
226
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
227
+ fn(`[${getTimestamp()}]`, ...args);
228
+ };
229
+
230
+ // Override methods that should have timestamps
231
+ console.log = withTimestamp(console.log);
232
+ console.info = withTimestamp(console.info);
233
+ console.warn = withTimestamp(console.warn);
234
+ console.error = withTimestamp(console.error);
235
+ console.debug = withTimestamp(console.debug);
236
+ console.trace = withTimestamp(console.trace);
237
+
238
+ return console;
239
+ };