@evolu/common 6.0.1-preview.27 → 6.0.1-preview.28
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.
- package/dist/src/Evolu/Evolu.d.ts +5 -10
- package/dist/src/Evolu/Evolu.d.ts.map +1 -1
- package/dist/src/Evolu/Evolu.js +18 -23
- package/dist/src/Multiton.d.ts +50 -0
- package/dist/src/Multiton.d.ts.map +1 -0
- package/dist/src/Multiton.js +33 -0
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +1 -0
- package/package.json +1 -1
- package/src/Evolu/Evolu.ts +26 -25
- package/src/Multiton.ts +98 -0
- package/src/index.ts +1 -0
|
@@ -42,7 +42,7 @@ export interface EvoluConfig extends Partial<DbConfig> {
|
|
|
42
42
|
*/
|
|
43
43
|
readonly reloadUrl?: string;
|
|
44
44
|
}
|
|
45
|
-
export interface Evolu<S extends EvoluSchema = EvoluSchema> {
|
|
45
|
+
export interface Evolu<S extends EvoluSchema = EvoluSchema> extends Disposable {
|
|
46
46
|
/**
|
|
47
47
|
* Subscribe to {@link EvoluError} changes.
|
|
48
48
|
*
|
|
@@ -399,15 +399,10 @@ export type EvoluDeps = ConsoleDep & CreateDbWorkerDep & Partial<FlushSyncDep> &
|
|
|
399
399
|
*
|
|
400
400
|
* ### Instance Caching
|
|
401
401
|
*
|
|
402
|
-
*
|
|
403
|
-
*
|
|
404
|
-
*
|
|
405
|
-
*
|
|
406
|
-
* where edits don't interrupt ongoing sync or lose in-memory state.
|
|
407
|
-
*
|
|
408
|
-
* For testing, either dispose of instances after each test (TODO: implement
|
|
409
|
-
* dispose method) or use unique instance names to ensure proper isolation
|
|
410
|
-
* between test cases.
|
|
402
|
+
* `createEvolu` caches instances using {@link Multiton} by {@link EvoluConfig}
|
|
403
|
+
* name to enable hot reloading and prevent database corruption from multiple
|
|
404
|
+
* connections. For testing, use unique instance names to ensure proper
|
|
405
|
+
* isolation.
|
|
411
406
|
*/
|
|
412
407
|
export declare const createEvolu: (deps: EvoluDeps) => <S extends EvoluSchema>(schema: ValidateSchema<S> extends never ? S : ValidateSchema<S>, config?: EvoluConfig) => Evolu<S>;
|
|
413
408
|
export declare const createQuery: <R extends Row>(queryCallback: Parameters<CreateQuery<EvoluSchema>>[0], options?: Parameters<CreateQuery<EvoluSchema>>[1]) => Query<R>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Evolu.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Evolu.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,cAAc,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"Evolu.d.ts","sourceRoot":"","sources":["../../../src/Evolu/Evolu.ts"],"names":[],"mappings":"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"}
|
package/dist/src/Evolu/Evolu.js
CHANGED
|
@@ -4,6 +4,7 @@ import { assert, assertNonEmptyArray } from "../Assert.js";
|
|
|
4
4
|
import { createCallbackRegistry } from "../CallbackRegistry.js";
|
|
5
5
|
import { eqArrayNumber } from "../Eq.js";
|
|
6
6
|
import { exhaustiveCheck } from "../Function.js";
|
|
7
|
+
import { createMultiton } from "../Multiton.js";
|
|
7
8
|
import { err, ok } from "../Result.js";
|
|
8
9
|
import { isSqlMutation } from "../Sqlite.js";
|
|
9
10
|
import { createStore } from "../Store.js";
|
|
@@ -14,7 +15,11 @@ import { createSubscribedQueries, emptyRows, serializeQuery, } from "./Query.js"
|
|
|
14
15
|
import { evoluSchemaToDbSchema, insertable, kysely, updateable, upsertable, } from "./Schema.js";
|
|
15
16
|
import { DbChange } from "./Storage.js";
|
|
16
17
|
import { initialSyncState } from "./Sync.js";
|
|
17
|
-
const evoluInstances =
|
|
18
|
+
const evoluInstances = createMultiton();
|
|
19
|
+
/**
|
|
20
|
+
* Unique identifier for the current browser tab or app instance, lazily
|
|
21
|
+
* initialized on first use to distinguish between multiple tabs.
|
|
22
|
+
*/
|
|
18
23
|
let tabId = null;
|
|
19
24
|
/**
|
|
20
25
|
* Creates an {@link Evolu} instance for a platform configured with the specified
|
|
@@ -51,29 +56,15 @@ let tabId = null;
|
|
|
51
56
|
*
|
|
52
57
|
* ### Instance Caching
|
|
53
58
|
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
* where edits don't interrupt ongoing sync or lose in-memory state.
|
|
59
|
-
*
|
|
60
|
-
* For testing, either dispose of instances after each test (TODO: implement
|
|
61
|
-
* dispose method) or use unique instance names to ensure proper isolation
|
|
62
|
-
* between test cases.
|
|
59
|
+
* `createEvolu` caches instances using {@link Multiton} by {@link EvoluConfig}
|
|
60
|
+
* name to enable hot reloading and prevent database corruption from multiple
|
|
61
|
+
* connections. For testing, use unique instance names to ensure proper
|
|
62
|
+
* isolation.
|
|
63
63
|
*/
|
|
64
|
-
export const createEvolu = (deps) => (schema, config) => {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
evolu = createEvoluInstance(deps)(schema, config);
|
|
69
|
-
evoluInstances.set(name, evolu);
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
// Hot reloading. Note that indexes are intentionally omitted.
|
|
73
|
-
evolu.ensureSchema(schema);
|
|
74
|
-
}
|
|
75
|
-
return evolu;
|
|
76
|
-
};
|
|
64
|
+
export const createEvolu = (deps) => (schema, config) => evoluInstances.ensure(config?.name ?? defaultDbConfig.name, () => createEvoluInstance(deps)(schema, config), (evolu) => {
|
|
65
|
+
// Hot reloading. Note that indexes are intentionally omitted.
|
|
66
|
+
evolu.ensureSchema(schema);
|
|
67
|
+
});
|
|
77
68
|
const createEvoluInstance = (deps) => (schema, config) => {
|
|
78
69
|
deps.console.enabled = config?.enableLogging ?? false;
|
|
79
70
|
const { indexes, reloadUrl = "/", ...partialDbConfig } = config ?? {};
|
|
@@ -376,6 +367,10 @@ const createEvoluInstance = (deps) => (schema, config) => {
|
|
|
376
367
|
};
|
|
377
368
|
return unuse;
|
|
378
369
|
},
|
|
370
|
+
/** Disposal is not implemented yet. */
|
|
371
|
+
[Symbol.dispose]: () => {
|
|
372
|
+
throw new Error("Evolu instance disposal is not yet implemented");
|
|
373
|
+
},
|
|
379
374
|
};
|
|
380
375
|
return evolu;
|
|
381
376
|
};
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages multiple named instances using the Multiton pattern.
|
|
3
|
+
*
|
|
4
|
+
* Unlike Singleton (one instance globally), Multiton maintains one instance per
|
|
5
|
+
* unique key.
|
|
6
|
+
*
|
|
7
|
+
* **Note:** Multiton is generally considered an anti-pattern because it
|
|
8
|
+
* introduces hidden global state and makes testing harder. Use it only when
|
|
9
|
+
* there's a compelling reason, such as:
|
|
10
|
+
*
|
|
11
|
+
* - Supporting hot reloading while preserving state across module reloads
|
|
12
|
+
* - Enforcing physical constraints (e.g., preventing multiple SQLite connections
|
|
13
|
+
* to the same database, which causes corruption)
|
|
14
|
+
* - Managing resources where instance identity is intrinsic to correctness
|
|
15
|
+
*
|
|
16
|
+
* For most cases, prefer explicit dependency injection and instance management.
|
|
17
|
+
*
|
|
18
|
+
* Compatibility and future work:
|
|
19
|
+
*
|
|
20
|
+
* - We will adopt the ECMAScript `DisposableStack` for structured cleanup and
|
|
21
|
+
* robust error handling as runtimes converge (Node.js ≥ 24, Safari stable).
|
|
22
|
+
* Safari Technology Preview already includes support, so broad availability
|
|
23
|
+
* is expected soon.
|
|
24
|
+
* - Until then, this module uses a simple Map-based approach and calls
|
|
25
|
+
* `instance[Symbol.dispose]()` directly during disposal.
|
|
26
|
+
* - We don't use a polyfill because we avoid global mutation, keep bundles lean,
|
|
27
|
+
* and prefer explicit feature detection.
|
|
28
|
+
* - MDN reference:
|
|
29
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DisposableStack
|
|
30
|
+
*/
|
|
31
|
+
export interface Multiton<K extends string, T extends Disposable> extends Disposable {
|
|
32
|
+
/**
|
|
33
|
+
* Ensures an instance exists for the given key, creating it if necessary. If
|
|
34
|
+
* the instance already exists, the optional `onCacheHit` callback is invoked
|
|
35
|
+
* to update the existing instance.
|
|
36
|
+
*/
|
|
37
|
+
readonly ensure: (key: K, create: () => T, onCacheHit?: (instance: T) => void) => T;
|
|
38
|
+
/** Gets an instance by key, or returns `null` if it doesn't exist. */
|
|
39
|
+
readonly get: (key: K) => T | null;
|
|
40
|
+
/** Checks if an instance exists for the given key. */
|
|
41
|
+
readonly has: (key: K) => boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Removes and disposes an instance by key. Returns `true` if the instance
|
|
44
|
+
* existed and was disposed, `false` otherwise.
|
|
45
|
+
*/
|
|
46
|
+
readonly disposeInstance: (key: K) => boolean;
|
|
47
|
+
}
|
|
48
|
+
/** Creates a {@link Multiton} instance manager. */
|
|
49
|
+
export declare const createMultiton: <K extends string, T extends Disposable>() => Multiton<K, T>;
|
|
50
|
+
//# sourceMappingURL=Multiton.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Multiton.d.ts","sourceRoot":"","sources":["../../src/Multiton.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,WAAW,QAAQ,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,UAAU,CAC9D,SAAQ,UAAU;IAClB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,CACf,GAAG,EAAE,CAAC,EACN,MAAM,EAAE,MAAM,CAAC,EACf,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,IAAI,KAC/B,CAAC,CAAC;IAEP,sEAAsE;IACtE,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,IAAI,CAAC;IAEnC,sDAAsD;IACtD,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;IAElC;;;OAGG;IACH,QAAQ,CAAC,eAAe,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,OAAO,CAAC;CAC/C;AAED,mDAAmD;AACnD,eAAO,MAAM,cAAc,GACzB,CAAC,SAAS,MAAM,EAChB,CAAC,SAAS,UAAU,OACjB,QAAQ,CAAC,CAAC,EAAE,CAAC,CAqCjB,CAAC"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/** Creates a {@link Multiton} instance manager. */
|
|
2
|
+
export const createMultiton = () => {
|
|
3
|
+
const instances = new Map();
|
|
4
|
+
return {
|
|
5
|
+
ensure: (key, create, onCacheHit) => {
|
|
6
|
+
let instance = instances.get(key);
|
|
7
|
+
if (instance == null) {
|
|
8
|
+
instance = create();
|
|
9
|
+
instances.set(key, instance);
|
|
10
|
+
}
|
|
11
|
+
else if (onCacheHit) {
|
|
12
|
+
onCacheHit(instance);
|
|
13
|
+
}
|
|
14
|
+
return instance;
|
|
15
|
+
},
|
|
16
|
+
get: (key) => instances.get(key) ?? null,
|
|
17
|
+
has: (key) => instances.has(key),
|
|
18
|
+
disposeInstance: (key) => {
|
|
19
|
+
const instance = instances.get(key);
|
|
20
|
+
if (instance) {
|
|
21
|
+
instance[Symbol.dispose]();
|
|
22
|
+
return instances.delete(key);
|
|
23
|
+
}
|
|
24
|
+
return false;
|
|
25
|
+
},
|
|
26
|
+
[Symbol.dispose]: () => {
|
|
27
|
+
for (const instance of instances.values()) {
|
|
28
|
+
instance[Symbol.dispose]();
|
|
29
|
+
}
|
|
30
|
+
instances.clear();
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
};
|
package/dist/src/index.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from "./Evolu/Public.js";
|
|
|
13
13
|
export * from "./Function.js";
|
|
14
14
|
export * from "./Identicon.js";
|
|
15
15
|
export * from "./ManyToManyMap.js";
|
|
16
|
+
export * from "./Multiton.js";
|
|
16
17
|
export * from "./Number.js";
|
|
17
18
|
export * from "./Object.js";
|
|
18
19
|
export * from "./Order.js";
|
package/dist/src/index.d.ts.map
CHANGED
|
@@ -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,YAAY,CAAC;AAC3B,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,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,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"}
|
|
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,YAAY,CAAC;AAC3B,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,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,eAAe,CAAC;AAC9B,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
|
@@ -13,6 +13,7 @@ export * from "./Evolu/Public.js";
|
|
|
13
13
|
export * from "./Function.js";
|
|
14
14
|
export * from "./Identicon.js";
|
|
15
15
|
export * from "./ManyToManyMap.js";
|
|
16
|
+
export * from "./Multiton.js";
|
|
16
17
|
export * from "./Number.js";
|
|
17
18
|
export * from "./Object.js";
|
|
18
19
|
export * from "./Order.js";
|
package/package.json
CHANGED
package/src/Evolu/Evolu.ts
CHANGED
|
@@ -7,6 +7,7 @@ import { RandomBytesDep, SymmetricCryptoDecryptError } from "../Crypto.js";
|
|
|
7
7
|
import { eqArrayNumber } from "../Eq.js";
|
|
8
8
|
import { TransferableError } from "../Error.js";
|
|
9
9
|
import { exhaustiveCheck } from "../Function.js";
|
|
10
|
+
import { createMultiton, Multiton } from "../Multiton.js";
|
|
10
11
|
import { err, ok, Result } from "../Result.js";
|
|
11
12
|
import { isSqlMutation, SafeSql, SqliteError, SqliteQuery } from "../Sqlite.js";
|
|
12
13
|
import { createStore, StoreSubscribe } from "../Store.js";
|
|
@@ -19,6 +20,7 @@ import {
|
|
|
19
20
|
InferType,
|
|
20
21
|
Mnemonic,
|
|
21
22
|
ObjectType,
|
|
23
|
+
SimpleName,
|
|
22
24
|
ValidMutationSize,
|
|
23
25
|
ValidMutationSizeError,
|
|
24
26
|
} from "../Type.js";
|
|
@@ -92,7 +94,7 @@ export interface EvoluConfig extends Partial<DbConfig> {
|
|
|
92
94
|
readonly reloadUrl?: string;
|
|
93
95
|
}
|
|
94
96
|
|
|
95
|
-
export interface Evolu<S extends EvoluSchema = EvoluSchema> {
|
|
97
|
+
export interface Evolu<S extends EvoluSchema = EvoluSchema> extends Disposable {
|
|
96
98
|
/**
|
|
97
99
|
* Subscribe to {@link EvoluError} changes.
|
|
98
100
|
*
|
|
@@ -457,8 +459,12 @@ export type EvoluDeps = ConsoleDep &
|
|
|
457
459
|
ReloadAppDep &
|
|
458
460
|
TimeDep;
|
|
459
461
|
|
|
460
|
-
const evoluInstances =
|
|
462
|
+
const evoluInstances = createMultiton<SimpleName, InternalEvoluInstance>();
|
|
461
463
|
|
|
464
|
+
/**
|
|
465
|
+
* Unique identifier for the current browser tab or app instance, lazily
|
|
466
|
+
* initialized on first use to distinguish between multiple tabs.
|
|
467
|
+
*/
|
|
462
468
|
let tabId: Id | null = null;
|
|
463
469
|
|
|
464
470
|
/**
|
|
@@ -496,35 +502,25 @@ let tabId: Id | null = null;
|
|
|
496
502
|
*
|
|
497
503
|
* ### Instance Caching
|
|
498
504
|
*
|
|
499
|
-
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
* where edits don't interrupt ongoing sync or lose in-memory state.
|
|
504
|
-
*
|
|
505
|
-
* For testing, either dispose of instances after each test (TODO: implement
|
|
506
|
-
* dispose method) or use unique instance names to ensure proper isolation
|
|
507
|
-
* between test cases.
|
|
505
|
+
* `createEvolu` caches instances using {@link Multiton} by {@link EvoluConfig}
|
|
506
|
+
* name to enable hot reloading and prevent database corruption from multiple
|
|
507
|
+
* connections. For testing, use unique instance names to ensure proper
|
|
508
|
+
* isolation.
|
|
508
509
|
*/
|
|
509
510
|
export const createEvolu =
|
|
510
511
|
(deps: EvoluDeps) =>
|
|
511
512
|
<S extends EvoluSchema>(
|
|
512
513
|
schema: ValidateSchema<S> extends never ? S : ValidateSchema<S>,
|
|
513
514
|
config?: EvoluConfig,
|
|
514
|
-
): Evolu<S> =>
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
evolu.ensureSchema(schema as EvoluSchema);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return evolu as Evolu<S>;
|
|
527
|
-
};
|
|
515
|
+
): Evolu<S> =>
|
|
516
|
+
evoluInstances.ensure(
|
|
517
|
+
config?.name ?? defaultDbConfig.name,
|
|
518
|
+
() => createEvoluInstance(deps)(schema as EvoluSchema, config),
|
|
519
|
+
(evolu) => {
|
|
520
|
+
// Hot reloading. Note that indexes are intentionally omitted.
|
|
521
|
+
evolu.ensureSchema(schema as EvoluSchema);
|
|
522
|
+
},
|
|
523
|
+
) as Evolu<S>;
|
|
528
524
|
|
|
529
525
|
const createEvoluInstance =
|
|
530
526
|
(deps: EvoluDeps) =>
|
|
@@ -943,6 +939,11 @@ const createEvoluInstance =
|
|
|
943
939
|
|
|
944
940
|
return unuse;
|
|
945
941
|
},
|
|
942
|
+
|
|
943
|
+
/** Disposal is not implemented yet. */
|
|
944
|
+
[Symbol.dispose]: () => {
|
|
945
|
+
throw new Error("Evolu instance disposal is not yet implemented");
|
|
946
|
+
},
|
|
946
947
|
};
|
|
947
948
|
|
|
948
949
|
return evolu;
|
package/src/Multiton.ts
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Manages multiple named instances using the Multiton pattern.
|
|
3
|
+
*
|
|
4
|
+
* Unlike Singleton (one instance globally), Multiton maintains one instance per
|
|
5
|
+
* unique key.
|
|
6
|
+
*
|
|
7
|
+
* **Note:** Multiton is generally considered an anti-pattern because it
|
|
8
|
+
* introduces hidden global state and makes testing harder. Use it only when
|
|
9
|
+
* there's a compelling reason, such as:
|
|
10
|
+
*
|
|
11
|
+
* - Supporting hot reloading while preserving state across module reloads
|
|
12
|
+
* - Enforcing physical constraints (e.g., preventing multiple SQLite connections
|
|
13
|
+
* to the same database, which causes corruption)
|
|
14
|
+
* - Managing resources where instance identity is intrinsic to correctness
|
|
15
|
+
*
|
|
16
|
+
* For most cases, prefer explicit dependency injection and instance management.
|
|
17
|
+
*
|
|
18
|
+
* Compatibility and future work:
|
|
19
|
+
*
|
|
20
|
+
* - We will adopt the ECMAScript `DisposableStack` for structured cleanup and
|
|
21
|
+
* robust error handling as runtimes converge (Node.js ≥ 24, Safari stable).
|
|
22
|
+
* Safari Technology Preview already includes support, so broad availability
|
|
23
|
+
* is expected soon.
|
|
24
|
+
* - Until then, this module uses a simple Map-based approach and calls
|
|
25
|
+
* `instance[Symbol.dispose]()` directly during disposal.
|
|
26
|
+
* - We don't use a polyfill because we avoid global mutation, keep bundles lean,
|
|
27
|
+
* and prefer explicit feature detection.
|
|
28
|
+
* - MDN reference:
|
|
29
|
+
* https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DisposableStack
|
|
30
|
+
*/
|
|
31
|
+
export interface Multiton<K extends string, T extends Disposable>
|
|
32
|
+
extends Disposable {
|
|
33
|
+
/**
|
|
34
|
+
* Ensures an instance exists for the given key, creating it if necessary. If
|
|
35
|
+
* the instance already exists, the optional `onCacheHit` callback is invoked
|
|
36
|
+
* to update the existing instance.
|
|
37
|
+
*/
|
|
38
|
+
readonly ensure: (
|
|
39
|
+
key: K,
|
|
40
|
+
create: () => T,
|
|
41
|
+
onCacheHit?: (instance: T) => void,
|
|
42
|
+
) => T;
|
|
43
|
+
|
|
44
|
+
/** Gets an instance by key, or returns `null` if it doesn't exist. */
|
|
45
|
+
readonly get: (key: K) => T | null;
|
|
46
|
+
|
|
47
|
+
/** Checks if an instance exists for the given key. */
|
|
48
|
+
readonly has: (key: K) => boolean;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Removes and disposes an instance by key. Returns `true` if the instance
|
|
52
|
+
* existed and was disposed, `false` otherwise.
|
|
53
|
+
*/
|
|
54
|
+
readonly disposeInstance: (key: K) => boolean;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Creates a {@link Multiton} instance manager. */
|
|
58
|
+
export const createMultiton = <
|
|
59
|
+
K extends string,
|
|
60
|
+
T extends Disposable,
|
|
61
|
+
>(): Multiton<K, T> => {
|
|
62
|
+
const instances = new Map<K, T>();
|
|
63
|
+
|
|
64
|
+
return {
|
|
65
|
+
ensure: (key, create, onCacheHit) => {
|
|
66
|
+
let instance = instances.get(key);
|
|
67
|
+
|
|
68
|
+
if (instance == null) {
|
|
69
|
+
instance = create();
|
|
70
|
+
instances.set(key, instance);
|
|
71
|
+
} else if (onCacheHit) {
|
|
72
|
+
onCacheHit(instance);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return instance;
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
get: (key) => instances.get(key) ?? null,
|
|
79
|
+
|
|
80
|
+
has: (key) => instances.has(key),
|
|
81
|
+
|
|
82
|
+
disposeInstance: (key) => {
|
|
83
|
+
const instance = instances.get(key);
|
|
84
|
+
if (instance) {
|
|
85
|
+
instance[Symbol.dispose]();
|
|
86
|
+
return instances.delete(key);
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
[Symbol.dispose]: () => {
|
|
92
|
+
for (const instance of instances.values()) {
|
|
93
|
+
instance[Symbol.dispose]();
|
|
94
|
+
}
|
|
95
|
+
instances.clear();
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -13,6 +13,7 @@ export * from "./Evolu/Public.js";
|
|
|
13
13
|
export * from "./Function.js";
|
|
14
14
|
export * from "./Identicon.js";
|
|
15
15
|
export * from "./ManyToManyMap.js";
|
|
16
|
+
export * from "./Multiton.js";
|
|
16
17
|
export * from "./Number.js";
|
|
17
18
|
export * from "./Object.js";
|
|
18
19
|
export * from "./Order.js";
|