@procedures/store 0.0.0-stage → 0.1.0
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/CHANGELOG.md +9 -0
- package/LICENSE +21 -0
- package/README.md +65 -2
- package/dist/.tsbuildinfo +1 -0
- package/dist/errors.d.ts +16 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +18 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/index.js.map +1 -0
- package/dist/module.d.ts +15 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +16 -0
- package/dist/module.js.map +1 -0
- package/dist/procedures.d.ts +13 -0
- package/dist/procedures.d.ts.map +1 -0
- package/dist/procedures.js +14 -0
- package/dist/procedures.js.map +1 -0
- package/dist/program-store/attach.d.ts +20 -0
- package/dist/program-store/attach.d.ts.map +1 -0
- package/dist/program-store/attach.js +36 -0
- package/dist/program-store/attach.js.map +1 -0
- package/dist/program-store/collection.d.ts +19 -0
- package/dist/program-store/collection.d.ts.map +1 -0
- package/dist/program-store/collection.js +34 -0
- package/dist/program-store/collection.js.map +1 -0
- package/dist/program-store/file.d.ts +29 -0
- package/dist/program-store/file.d.ts.map +1 -0
- package/dist/program-store/file.js +156 -0
- package/dist/program-store/file.js.map +1 -0
- package/dist/program-store/index.d.ts +11 -0
- package/dist/program-store/index.d.ts.map +1 -0
- package/dist/program-store/index.js +10 -0
- package/dist/program-store/index.js.map +1 -0
- package/dist/program-store/memory.d.ts +15 -0
- package/dist/program-store/memory.d.ts.map +1 -0
- package/dist/program-store/memory.js +25 -0
- package/dist/program-store/memory.js.map +1 -0
- package/dist/program-store/procedures.d.ts +23 -0
- package/dist/program-store/procedures.d.ts.map +1 -0
- package/dist/program-store/procedures.js +221 -0
- package/dist/program-store/procedures.js.map +1 -0
- package/dist/program-store/types.d.ts +50 -0
- package/dist/program-store/types.d.ts.map +1 -0
- package/dist/program-store/types.js +41 -0
- package/dist/program-store/types.js.map +1 -0
- package/dist/register.d.ts +8 -0
- package/dist/register.d.ts.map +1 -0
- package/dist/register.js +15 -0
- package/dist/register.js.map +1 -0
- package/dist/storage/adapter.d.ts +148 -0
- package/dist/storage/adapter.d.ts.map +1 -0
- package/dist/storage/adapter.js +214 -0
- package/dist/storage/adapter.js.map +1 -0
- package/dist/storage/api.d.ts +125 -0
- package/dist/storage/api.d.ts.map +1 -0
- package/dist/storage/api.js +220 -0
- package/dist/storage/api.js.map +1 -0
- package/dist/storage/factory.d.ts +134 -0
- package/dist/storage/factory.d.ts.map +1 -0
- package/dist/storage/factory.js +166 -0
- package/dist/storage/factory.js.map +1 -0
- package/dist/storage/hybrid.d.ts +193 -0
- package/dist/storage/hybrid.d.ts.map +1 -0
- package/dist/storage/hybrid.js +519 -0
- package/dist/storage/hybrid.js.map +1 -0
- package/dist/storage/index.d.ts +21 -0
- package/dist/storage/index.d.ts.map +1 -0
- package/dist/storage/index.js +20 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/storage/procedures.d.ts +14 -0
- package/dist/storage/procedures.d.ts.map +1 -0
- package/dist/storage/procedures.js +98 -0
- package/dist/storage/procedures.js.map +1 -0
- package/dist/storage/serialization.d.ts +123 -0
- package/dist/storage/serialization.d.ts.map +1 -0
- package/dist/storage/serialization.js +216 -0
- package/dist/storage/serialization.js.map +1 -0
- package/dist/storage/synced-registry.d.ts +246 -0
- package/dist/storage/synced-registry.d.ts.map +1 -0
- package/dist/storage/synced-registry.js +575 -0
- package/dist/storage/synced-registry.js.map +1 -0
- package/dist/storage/types.d.ts +130 -0
- package/dist/storage/types.d.ts.map +1 -0
- package/dist/storage/types.js +8 -0
- package/dist/storage/types.js.map +1 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +7 -0
- package/dist/types.js.map +1 -0
- package/package.json +79 -3
- package/src/errors.ts +20 -0
- package/src/index.ts +19 -0
- package/src/module.ts +18 -0
- package/src/procedures.ts +17 -0
- package/src/program-store/attach.ts +46 -0
- package/src/program-store/collection.ts +39 -0
- package/src/program-store/file.ts +157 -0
- package/src/program-store/index.ts +17 -0
- package/src/program-store/memory.ts +30 -0
- package/src/program-store/procedures.ts +344 -0
- package/src/program-store/types.ts +78 -0
- package/src/register.ts +18 -0
- package/src/storage/adapter.ts +273 -0
- package/src/storage/api.ts +335 -0
- package/src/storage/factory.ts +268 -0
- package/src/storage/hybrid.ts +676 -0
- package/src/storage/index.ts +61 -0
- package/src/storage/procedures.ts +146 -0
- package/src/storage/serialization.ts +283 -0
- package/src/storage/synced-registry.ts +698 -0
- package/src/storage/types.ts +180 -0
- package/src/types.ts +7 -0
package/dist/module.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module (no side effects): the procedures of client-store as data, for `createRegistry()`.
|
|
3
|
+
*
|
|
4
|
+
* The procedures of the store: `procedure.register` and the program store procedures
|
|
5
|
+
* `procedure.store`, `load`, `sync` and `remote`. The core registered the older versions of them
|
|
6
|
+
* before they moved here (roadmap 3.2).
|
|
7
|
+
*/
|
|
8
|
+
import { type ProcedureModule } from "@procedures/client";
|
|
9
|
+
/**
|
|
10
|
+
* The module of this package, for `createRegistry()`: its procedures as data. Importing this
|
|
11
|
+
* file registers nothing. (`register.ts` registers the same procedures in the global registry.)
|
|
12
|
+
*/
|
|
13
|
+
export declare const storeModule: ProcedureModule;
|
|
14
|
+
export default storeModule;
|
|
15
|
+
//# sourceMappingURL=module.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAgB,KAAK,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAGxE;;;GAGG;AACH,eAAO,MAAM,WAAW,EAAE,eAA+E,CAAC;eAE3F,WAAW"}
|
package/dist/module.js
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module (no side effects): the procedures of client-store as data, for `createRegistry()`.
|
|
3
|
+
*
|
|
4
|
+
* The procedures of the store: `procedure.register` and the program store procedures
|
|
5
|
+
* `procedure.store`, `load`, `sync` and `remote`. The core registered the older versions of them
|
|
6
|
+
* before they moved here (roadmap 3.2).
|
|
7
|
+
*/
|
|
8
|
+
import { defineModule } from "@procedures/client";
|
|
9
|
+
import { procedureStorageProcedures } from "./procedures.js";
|
|
10
|
+
/**
|
|
11
|
+
* The module of this package, for `createRegistry()`: its procedures as data. Importing this
|
|
12
|
+
* file registers nothing. (`register.ts` registers the same procedures in the global registry.)
|
|
13
|
+
*/
|
|
14
|
+
export const storeModule = defineModule("@procedures/store", procedureStorageProcedures);
|
|
15
|
+
export default storeModule;
|
|
16
|
+
//# sourceMappingURL=module.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,YAAY,EAAwB,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,0BAA0B,EAAE,MAAM,iBAAiB,CAAC;AAE7D;;;GAGG;AACH,MAAM,CAAC,MAAM,WAAW,GAAoB,YAAY,CAAC,mBAAmB,EAAE,0BAA0B,CAAC,CAAC;AAE1G,eAAe,WAAW,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every procedure of the package: procedure.register (the synced registry) and the procedures of
|
|
3
|
+
* the program store (procedure.store, load, sync and remote).
|
|
4
|
+
*/
|
|
5
|
+
import type { AnyProcedure } from "@procedures/client";
|
|
6
|
+
/** All the procedures of client-store. */
|
|
7
|
+
export declare const procedureStorageProcedures: AnyProcedure[];
|
|
8
|
+
/** All the procedures of client-store as a module. */
|
|
9
|
+
export declare const procedureStorageModule: {
|
|
10
|
+
name: string;
|
|
11
|
+
procedures: AnyProcedure[];
|
|
12
|
+
};
|
|
13
|
+
//# sourceMappingURL=procedures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAIvD,0CAA0C;AAC1C,eAAO,MAAM,0BAA0B,EAAE,YAAY,EAA4D,CAAC;AAElH,sDAAsD;AACtD,eAAO,MAAM,sBAAsB,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,YAAY,EAAE,CAAA;CAG9E,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every procedure of the package: procedure.register (the synced registry) and the procedures of
|
|
3
|
+
* the program store (procedure.store, load, sync and remote).
|
|
4
|
+
*/
|
|
5
|
+
import { procedureRegisterProcedure } from "./storage/procedures.js";
|
|
6
|
+
import { programStoreProcedures } from "./program-store/procedures.js";
|
|
7
|
+
/** All the procedures of client-store. */
|
|
8
|
+
export const procedureStorageProcedures = [procedureRegisterProcedure, ...programStoreProcedures];
|
|
9
|
+
/** All the procedures of client-store as a module. */
|
|
10
|
+
export const procedureStorageModule = {
|
|
11
|
+
name: "procedure-storage",
|
|
12
|
+
procedures: procedureStorageProcedures,
|
|
13
|
+
};
|
|
14
|
+
//# sourceMappingURL=procedures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"procedures.js","sourceRoot":"","sources":["../src/procedures.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,EAAE,0BAA0B,EAAE,MAAM,yBAAyB,CAAC;AACrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,+BAA+B,CAAC;AAEvE,0CAA0C;AAC1C,MAAM,CAAC,MAAM,0BAA0B,GAAmB,CAAC,0BAA0B,EAAE,GAAG,sBAAsB,CAAC,CAAC;AAElH,sDAAsD;AACtD,MAAM,CAAC,MAAM,sBAAsB,GAAiD;IAClF,IAAI,EAAE,mBAAmB;IACzB,UAAU,EAAE,0BAA0B;CACvC,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The program store of a registry.
|
|
3
|
+
*
|
|
4
|
+
* A host gives a registry its store with `attachProgramStore(registry, store)`. The procedures
|
|
5
|
+
* `procedure.store`, `load` and `sync` use the store of the caller's registry (`ctx.registry`,
|
|
6
|
+
* or the global registry). Without a store, they fail with the code NOT_CONFIGURED.
|
|
7
|
+
*/
|
|
8
|
+
import { type ProcedureContext, type ProcedureRegistryLike } from "@procedures/client";
|
|
9
|
+
import type { ProgramStore } from "./types.js";
|
|
10
|
+
/** Give a registry its program store. A second call replaces the store. */
|
|
11
|
+
export declare function attachProgramStore(registry: object, store: ProgramStore): void;
|
|
12
|
+
/** Take the program store from a registry. */
|
|
13
|
+
export declare function detachProgramStore(registry: object): void;
|
|
14
|
+
/** The program store of a registry, or undefined. */
|
|
15
|
+
export declare function getProgramStore(registry: object): ProgramStore | undefined;
|
|
16
|
+
/** The registry of a caller: the context's registry, or the global registry. */
|
|
17
|
+
export declare function callerRegistry(ctx: ProcedureContext | undefined): ProcedureRegistryLike;
|
|
18
|
+
/** The program store of the caller's registry, or a NOT_CONFIGURED error. */
|
|
19
|
+
export declare function requireProgramStore(ctx: ProcedureContext | undefined, what: string): ProgramStore;
|
|
20
|
+
//# sourceMappingURL=attach.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attach.d.ts","sourceRoot":"","sources":["../../src/program-store/attach.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAsB,KAAK,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAC3G,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAK/C,2EAA2E;AAC3E,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,CAE9E;AAED,8CAA8C;AAC9C,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAEzD;AAED,qDAAqD;AACrD,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS,CAE1E;AAED,gFAAgF;AAChF,wBAAgB,cAAc,CAAC,GAAG,EAAE,gBAAgB,GAAG,SAAS,GAAG,qBAAqB,CAEvF;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,gBAAgB,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,YAAY,CAUjG"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The program store of a registry.
|
|
3
|
+
*
|
|
4
|
+
* A host gives a registry its store with `attachProgramStore(registry, store)`. The procedures
|
|
5
|
+
* `procedure.store`, `load` and `sync` use the store of the caller's registry (`ctx.registry`,
|
|
6
|
+
* or the global registry). Without a store, they fail with the code NOT_CONFIGURED.
|
|
7
|
+
*/
|
|
8
|
+
import { PROCEDURE_REGISTRY } from "@procedures/client";
|
|
9
|
+
import { ProcedureStoreError } from "../storage/procedures.js";
|
|
10
|
+
const STORES = new WeakMap();
|
|
11
|
+
/** Give a registry its program store. A second call replaces the store. */
|
|
12
|
+
export function attachProgramStore(registry, store) {
|
|
13
|
+
STORES.set(registry, store);
|
|
14
|
+
}
|
|
15
|
+
/** Take the program store from a registry. */
|
|
16
|
+
export function detachProgramStore(registry) {
|
|
17
|
+
STORES.delete(registry);
|
|
18
|
+
}
|
|
19
|
+
/** The program store of a registry, or undefined. */
|
|
20
|
+
export function getProgramStore(registry) {
|
|
21
|
+
return STORES.get(registry);
|
|
22
|
+
}
|
|
23
|
+
/** The registry of a caller: the context's registry, or the global registry. */
|
|
24
|
+
export function callerRegistry(ctx) {
|
|
25
|
+
return ctx?.registry ?? PROCEDURE_REGISTRY;
|
|
26
|
+
}
|
|
27
|
+
/** The program store of the caller's registry, or a NOT_CONFIGURED error. */
|
|
28
|
+
export function requireProgramStore(ctx, what) {
|
|
29
|
+
const store = getProgramStore(callerRegistry(ctx));
|
|
30
|
+
if (!store) {
|
|
31
|
+
throw new ProcedureStoreError("NOT_CONFIGURED", `${what} needs a program store, and this registry has none. ` +
|
|
32
|
+
"Give it one with attachProgramStore(registry, new FileProgramStore(path)) (or MemoryProgramStore, CollectionProgramStore).");
|
|
33
|
+
}
|
|
34
|
+
return store;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=attach.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attach.js","sourceRoot":"","sources":["../../src/program-store/attach.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,kBAAkB,EAAqD,MAAM,oBAAoB,CAAC;AAE3G,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAE/D,MAAM,MAAM,GAAG,IAAI,OAAO,EAAwB,CAAC;AAEnD,2EAA2E;AAC3E,MAAM,UAAU,kBAAkB,CAAC,QAAgB,EAAE,KAAmB;IACtE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;AAC9B,CAAC;AAED,8CAA8C;AAC9C,MAAM,UAAU,kBAAkB,CAAC,QAAgB;IACjD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;AAC1B,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;AAC9B,CAAC;AAED,gFAAgF;AAChF,MAAM,UAAU,cAAc,CAAC,GAAiC;IAC9D,OAAO,GAAG,EAAE,QAAQ,IAAI,kBAAkB,CAAC;AAC7C,CAAC;AAED,6EAA6E;AAC7E,MAAM,UAAU,mBAAmB,CAAC,GAAiC,EAAE,IAAY;IACjF,MAAM,KAAK,GAAG,eAAe,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,CAAC;IACnD,IAAI,CAAC,KAAK,EAAE,CAAC;QACX,MAAM,IAAI,mBAAmB,CAC3B,gBAAgB,EAChB,GAAG,IAAI,sDAAsD;YAC3D,4HAA4H,CAC/H,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A program store on a collection storage: `InMemoryStorage` of client-collections, or a remote
|
|
3
|
+
* one, `ApiStorage` (the collection procedures of a server) or `HybridStorage` (a local cache with
|
|
4
|
+
* a remote copy). The key of each record is the key of the program.
|
|
5
|
+
*/
|
|
6
|
+
import type { CollectionStorage } from "@procedures/client";
|
|
7
|
+
import { type ProgramStore, type StoredProgram } from "./types.js";
|
|
8
|
+
export declare class CollectionProgramStore implements ProgramStore {
|
|
9
|
+
private readonly storage;
|
|
10
|
+
readonly kind = "collection";
|
|
11
|
+
/** @param storage - The storage of the records. The store closes it in `close()`. */
|
|
12
|
+
constructor(storage: CollectionStorage<StoredProgram>);
|
|
13
|
+
get(key: string): Promise<StoredProgram | undefined>;
|
|
14
|
+
list(prefix?: string): Promise<StoredProgram[]>;
|
|
15
|
+
put(program: StoredProgram): Promise<void>;
|
|
16
|
+
delete(key: string): Promise<boolean>;
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=collection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.d.ts","sourceRoot":"","sources":["../../src/program-store/collection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAC5D,OAAO,EAAyC,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1G,qBAAa,sBAAuB,YAAW,YAAY;IAI7C,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,QAAQ,CAAC,IAAI,gBAAgB;IAE7B,qFAAqF;IACrF,YAA6B,OAAO,EAAE,iBAAiB,CAAC,aAAa,CAAC,EAAI;IAEpE,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAIzD;IAEK,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAGpD;IAEK,GAAG,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/C;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE1C;IAEK,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAE3B;CACF"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A program store on a collection storage: `InMemoryStorage` of client-collections, or a remote
|
|
3
|
+
* one, `ApiStorage` (the collection procedures of a server) or `HybridStorage` (a local cache with
|
|
4
|
+
* a remote copy). The key of each record is the key of the program.
|
|
5
|
+
*/
|
|
6
|
+
import { byKey, checkStoredProgram, keyMatches } from "./types.js";
|
|
7
|
+
export class CollectionProgramStore {
|
|
8
|
+
storage;
|
|
9
|
+
kind = "collection";
|
|
10
|
+
/** @param storage - The storage of the records. The store closes it in `close()`. */
|
|
11
|
+
constructor(storage) {
|
|
12
|
+
this.storage = storage;
|
|
13
|
+
}
|
|
14
|
+
async get(key) {
|
|
15
|
+
const record = await this.storage.get(key);
|
|
16
|
+
// A copy: a local storage (InMemoryStorage) gives its own object
|
|
17
|
+
return record === undefined ? undefined : checkStoredProgram(structuredClone(record));
|
|
18
|
+
}
|
|
19
|
+
async list(prefix) {
|
|
20
|
+
const records = await this.storage.getAll();
|
|
21
|
+
return byKey(records.map((record) => checkStoredProgram(structuredClone(record))).filter((p) => keyMatches(p.key, prefix)));
|
|
22
|
+
}
|
|
23
|
+
async put(program) {
|
|
24
|
+
const checked = checkStoredProgram(structuredClone(program));
|
|
25
|
+
await this.storage.set(checked.key, checked);
|
|
26
|
+
}
|
|
27
|
+
async delete(key) {
|
|
28
|
+
return this.storage.delete(key);
|
|
29
|
+
}
|
|
30
|
+
async close() {
|
|
31
|
+
await this.storage.close();
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=collection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collection.js","sourceRoot":"","sources":["../../src/program-store/collection.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAyC,MAAM,YAAY,CAAC;AAE1G,MAAM,OAAO,sBAAsB;IAIJ,OAAO;IAH3B,IAAI,GAAG,YAAY,CAAC;IAE7B,qFAAqF;IACrF,YAA6B,OAAyC;uBAAzC,OAAO;IAAqC,CAAC;IAE1E,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC3C,iEAAiE;QACjE,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,kBAAkB,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC;IACxF,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAe;QACxB,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QAC5C,OAAO,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,kBAAkB,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAC9H,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAsB;QAC9B,MAAM,OAAO,GAAG,kBAAkB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7D,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC/C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,KAAK;QACT,MAAM,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC;IAC7B,CAAC;CACF"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A program store in one JSON file.
|
|
3
|
+
*
|
|
4
|
+
* The file is `{ "schemaVersion": 1, "programs": { "<key>": <StoredProgram> } }`. Each operation
|
|
5
|
+
* reads the file again, so two processes see the programs of each other. A write takes a lock
|
|
6
|
+
* file (`<file>.lock`), reads, changes and writes the file through a temporary file and a rename,
|
|
7
|
+
* so a reader never sees a part of a write and two writers do not lose each other's programs.
|
|
8
|
+
* A file that is not valid JSON is an error: the store does not write over it.
|
|
9
|
+
*/
|
|
10
|
+
import { type ProgramStore, type StoredProgram } from "./types.js";
|
|
11
|
+
export declare class FileProgramStore implements ProgramStore {
|
|
12
|
+
readonly path: string;
|
|
13
|
+
readonly kind = "file";
|
|
14
|
+
/** The writes of this process, one at a time (the lock file orders the processes). */
|
|
15
|
+
private queue;
|
|
16
|
+
/** @param path - The JSON file. The store makes it, and its folder, at the first write. */
|
|
17
|
+
constructor(path: string);
|
|
18
|
+
get(key: string): Promise<StoredProgram | undefined>;
|
|
19
|
+
list(prefix?: string): Promise<StoredProgram[]>;
|
|
20
|
+
put(program: StoredProgram): Promise<void>;
|
|
21
|
+
delete(key: string): Promise<boolean>;
|
|
22
|
+
/** The content of the file. A missing file is an empty store. */
|
|
23
|
+
private read;
|
|
24
|
+
/** Read, change and write the file under the lock. `change` gives false when nothing changed. */
|
|
25
|
+
private change;
|
|
26
|
+
private write;
|
|
27
|
+
private withLock;
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=file.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.d.ts","sourceRoot":"","sources":["../../src/program-store/file.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,EAAyC,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAqC1G,qBAAa,gBAAiB,YAAW,YAAY;IAMvC,QAAQ,CAAC,IAAI,EAAE,MAAM;IALjC,QAAQ,CAAC,IAAI,UAAU;IACvB,sFAAsF;IACtF,OAAO,CAAC,KAAK,CAAuC;IAEpD,2FAA2F;IAC3F,YAAqB,IAAI,EAAE,MAAM,EAAI;IAE/B,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAEzD;IAEK,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAEpD;IAEK,GAAG,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAM/C;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ1C;IAED,iEAAiE;YACnD,IAAI;IAwBlB,iGAAiG;IACjG,OAAO,CAAC,MAAM;YASA,KAAK;YAYL,QAAQ;CA0BvB"}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A program store in one JSON file.
|
|
3
|
+
*
|
|
4
|
+
* The file is `{ "schemaVersion": 1, "programs": { "<key>": <StoredProgram> } }`. Each operation
|
|
5
|
+
* reads the file again, so two processes see the programs of each other. A write takes a lock
|
|
6
|
+
* file (`<file>.lock`), reads, changes and writes the file through a temporary file and a rename,
|
|
7
|
+
* so a reader never sees a part of a write and two writers do not lose each other's programs.
|
|
8
|
+
* A file that is not valid JSON is an error: the store does not write over it.
|
|
9
|
+
*/
|
|
10
|
+
import { mkdir, open, readFile, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
11
|
+
import { dirname } from "node:path";
|
|
12
|
+
import { byKey, checkStoredProgram, keyMatches } from "./types.js";
|
|
13
|
+
/** A lock older than this belongs to a process that stopped: it is removed. */
|
|
14
|
+
const STALE_LOCK_MS = 10_000;
|
|
15
|
+
/** The longest wait for the lock. */
|
|
16
|
+
const LOCK_TIMEOUT_MS = 30_000;
|
|
17
|
+
/**
|
|
18
|
+
* True when another process holds a file for a moment. On Windows, a file that its owner is
|
|
19
|
+
* deleting or renaming cannot be opened until that completes: EPERM, EACCES or EBUSY.
|
|
20
|
+
*/
|
|
21
|
+
function isContention(error) {
|
|
22
|
+
const code = error.code;
|
|
23
|
+
if (code === "EEXIST")
|
|
24
|
+
return true;
|
|
25
|
+
return process.platform === "win32" && (code === "EPERM" || code === "EACCES" || code === "EBUSY");
|
|
26
|
+
}
|
|
27
|
+
const pause = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
28
|
+
/** Run a file operation again while Windows holds the file for a moment. */
|
|
29
|
+
async function retryOnContention(operation) {
|
|
30
|
+
for (let attempt = 1;; attempt++) {
|
|
31
|
+
try {
|
|
32
|
+
return await operation();
|
|
33
|
+
}
|
|
34
|
+
catch (error) {
|
|
35
|
+
const code = error.code;
|
|
36
|
+
if (attempt >= 20 || code === "EEXIST" || !isContention(error))
|
|
37
|
+
throw error;
|
|
38
|
+
await pause(10 * attempt);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
export class FileProgramStore {
|
|
43
|
+
path;
|
|
44
|
+
kind = "file";
|
|
45
|
+
/** The writes of this process, one at a time (the lock file orders the processes). */
|
|
46
|
+
queue = Promise.resolve();
|
|
47
|
+
/** @param path - The JSON file. The store makes it, and its folder, at the first write. */
|
|
48
|
+
constructor(path) {
|
|
49
|
+
this.path = path;
|
|
50
|
+
}
|
|
51
|
+
async get(key) {
|
|
52
|
+
return (await this.read()).programs[key];
|
|
53
|
+
}
|
|
54
|
+
async list(prefix) {
|
|
55
|
+
return byKey(Object.values((await this.read()).programs).filter((p) => keyMatches(p.key, prefix)));
|
|
56
|
+
}
|
|
57
|
+
async put(program) {
|
|
58
|
+
const checked = checkStoredProgram(structuredClone(program));
|
|
59
|
+
await this.change((file) => {
|
|
60
|
+
file.programs[checked.key] = checked;
|
|
61
|
+
return true;
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
async delete(key) {
|
|
65
|
+
let deleted = false;
|
|
66
|
+
await this.change((file) => {
|
|
67
|
+
deleted = Object.hasOwn(file.programs, key);
|
|
68
|
+
if (deleted)
|
|
69
|
+
delete file.programs[key];
|
|
70
|
+
return deleted;
|
|
71
|
+
});
|
|
72
|
+
return deleted;
|
|
73
|
+
}
|
|
74
|
+
/** The content of the file. A missing file is an empty store. */
|
|
75
|
+
async read() {
|
|
76
|
+
let text;
|
|
77
|
+
try {
|
|
78
|
+
text = await retryOnContention(() => readFile(this.path, "utf8"));
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
if (error.code === "ENOENT")
|
|
82
|
+
return { schemaVersion: 1, programs: {} };
|
|
83
|
+
throw error;
|
|
84
|
+
}
|
|
85
|
+
let parsed;
|
|
86
|
+
try {
|
|
87
|
+
parsed = JSON.parse(text);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
throw new Error(`The program store file is not valid JSON: ${this.path}. The store does not write over it.`);
|
|
91
|
+
}
|
|
92
|
+
const file = parsed;
|
|
93
|
+
if (file?.schemaVersion !== 1 || typeof file.programs !== "object" || file.programs === null) {
|
|
94
|
+
throw new Error(`The program store file has an unknown format (schemaVersion 1 expected): ${this.path}`);
|
|
95
|
+
}
|
|
96
|
+
// A null-prototype object: a key such as "__proto__" stays a plain key
|
|
97
|
+
const programs = Object.create(null);
|
|
98
|
+
for (const [key, value] of Object.entries(file.programs))
|
|
99
|
+
programs[key] = checkStoredProgram(value);
|
|
100
|
+
return { schemaVersion: 1, programs };
|
|
101
|
+
}
|
|
102
|
+
/** Read, change and write the file under the lock. `change` gives false when nothing changed. */
|
|
103
|
+
change(change) {
|
|
104
|
+
const run = this.queue.then(() => this.withLock(async () => {
|
|
105
|
+
const file = await this.read();
|
|
106
|
+
if (change(file))
|
|
107
|
+
await this.write(file);
|
|
108
|
+
}));
|
|
109
|
+
this.queue = run.catch(() => undefined);
|
|
110
|
+
return run;
|
|
111
|
+
}
|
|
112
|
+
async write(file) {
|
|
113
|
+
await mkdir(dirname(this.path), { recursive: true });
|
|
114
|
+
const temporary = `${this.path}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
115
|
+
try {
|
|
116
|
+
await writeFile(temporary, `${JSON.stringify(file, null, 2)}\n`, "utf8");
|
|
117
|
+
await retryOnContention(() => rename(temporary, this.path));
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
await rm(temporary, { force: true });
|
|
121
|
+
throw error;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async withLock(work) {
|
|
125
|
+
const lockPath = `${this.path}.lock`;
|
|
126
|
+
await mkdir(dirname(this.path), { recursive: true });
|
|
127
|
+
const deadline = Date.now() + LOCK_TIMEOUT_MS;
|
|
128
|
+
for (;;) {
|
|
129
|
+
try {
|
|
130
|
+
const handle = await open(lockPath, "wx");
|
|
131
|
+
await handle.close();
|
|
132
|
+
break;
|
|
133
|
+
}
|
|
134
|
+
catch (error) {
|
|
135
|
+
if (!isContention(error) || Date.now() > deadline)
|
|
136
|
+
throw error;
|
|
137
|
+
try {
|
|
138
|
+
const { mtimeMs } = await stat(lockPath);
|
|
139
|
+
if (Date.now() - mtimeMs > STALE_LOCK_MS)
|
|
140
|
+
await rm(lockPath, { force: true });
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
143
|
+
// The lock went away: try again
|
|
144
|
+
}
|
|
145
|
+
await pause(10 + Math.random() * 20);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
try {
|
|
149
|
+
await work();
|
|
150
|
+
}
|
|
151
|
+
finally {
|
|
152
|
+
await rm(lockPath, { force: true });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
//# sourceMappingURL=file.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file.js","sourceRoot":"","sources":["../../src/program-store/file.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAyC,MAAM,YAAY,CAAC;AAO1G,+EAA+E;AAC/E,MAAM,aAAa,GAAG,MAAM,CAAC;AAC7B,qCAAqC;AACrC,MAAM,eAAe,GAAG,MAAM,CAAC;AAE/B;;;GAGG;AACH,SAAS,YAAY,CAAC,KAAc;IAClC,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAC;IACnD,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACnC,OAAO,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,CAAC,IAAI,KAAK,OAAO,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,OAAO,CAAC,CAAC;AACrG,CAAC;AAED,MAAM,KAAK,GAAG,CAAC,EAAU,EAAE,EAAE,CAAC,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAC;AAEhF,4EAA4E;AAC5E,KAAK,UAAU,iBAAiB,CAAI,SAA2B;IAC7D,KAAK,IAAI,OAAO,GAAG,CAAC,GAAI,OAAO,EAAE,EAAE,CAAC;QAClC,IAAI,CAAC;YACH,OAAO,MAAM,SAAS,EAAE,CAAC;QAC3B,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,GAAI,KAA+B,CAAC,IAAI,CAAC;YACnD,IAAI,OAAO,IAAI,EAAE,IAAI,IAAI,KAAK,QAAQ,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAC;YAC5E,MAAM,KAAK,CAAC,EAAE,GAAG,OAAO,CAAC,CAAC;QAC5B,CAAC;IACH,CAAC;AACH,CAAC;AAED,MAAM,OAAO,gBAAgB;IAMN,IAAI;IALhB,IAAI,GAAG,MAAM,CAAC;IACvB,sFAAsF;IAC9E,KAAK,GAAqB,OAAO,CAAC,OAAO,EAAE,CAAC;IAEpD,2FAA2F;IAC3F,YAAqB,IAAY;oBAAZ,IAAI;IAAW,CAAC;IAErC,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,OAAO,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IAC3C,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAe;QACxB,OAAO,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IACrG,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAsB;QAC9B,MAAM,OAAO,GAAG,kBAAkB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7D,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YACzB,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,OAAO,CAAC;YACrC,OAAO,IAAI,CAAC;QACd,CAAC,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,IAAI,OAAO,GAAG,KAAK,CAAC;QACpB,MAAM,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE;YACzB,OAAO,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;YAC5C,IAAI,OAAO;gBAAE,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YACvC,OAAO,OAAO,CAAC;QACjB,CAAC,CAAC,CAAC;QACH,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,iEAAiE;IACzD,KAAK,CAAC,IAAI;QAChB,IAAI,IAAY,CAAC;QACjB,IAAI,CAAC;YACH,IAAI,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC;QACpE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAK,KAA+B,CAAC,IAAI,KAAK,QAAQ;gBAAE,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;YAClG,MAAM,KAAK,CAAC;QACd,CAAC;QACD,IAAI,MAAe,CAAC;QACpB,IAAI,CAAC;YACH,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5B,CAAC;QAAC,MAAM,CAAC;YACP,MAAM,IAAI,KAAK,CAAC,6CAA6C,IAAI,CAAC,IAAI,qCAAqC,CAAC,CAAC;QAC/G,CAAC;QACD,MAAM,IAAI,GAAG,MAA8B,CAAC;QAC5C,IAAI,IAAI,EAAE,aAAa,KAAK,CAAC,IAAI,OAAO,IAAI,CAAC,QAAQ,KAAK,QAAQ,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI,EAAE,CAAC;YAC7F,MAAM,IAAI,KAAK,CAAC,4EAA4E,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QAC3G,CAAC;QACD,uEAAuE;QACvE,MAAM,QAAQ,GAAkC,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACpE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC;YAAE,QAAQ,CAAC,GAAG,CAAC,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACpG,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC;IACxC,CAAC;IAED,iGAAiG;IACzF,MAAM,CAAC,MAAsC;QACnD,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;YAC/B,IAAI,MAAM,CAAC,IAAI,CAAC;gBAAE,MAAM,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC,CAAC;QACJ,IAAI,CAAC,KAAK,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QACxC,OAAO,GAAG,CAAC;IACb,CAAC;IAEO,KAAK,CAAC,KAAK,CAAC,IAAiB;QACnC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,SAAS,GAAG,GAAG,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,GAAG,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC;QAC3F,IAAI,CAAC;YACH,MAAM,SAAS,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;YACzE,MAAM,iBAAiB,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;QAC9D,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;YACrC,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,QAAQ,CAAC,IAAyB;QAC9C,MAAM,QAAQ,GAAG,GAAG,IAAI,CAAC,IAAI,OAAO,CAAC;QACrC,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,eAAe,CAAC;QAC9C,SAAS,CAAC;YACR,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;gBAC1C,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;gBACrB,MAAM;YACR,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,QAAQ;oBAAE,MAAM,KAAK,CAAC;gBAC/D,IAAI,CAAC;oBACH,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,CAAC;oBACzC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,GAAG,aAAa;wBAAE,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;gBAChF,CAAC;gBAAC,MAAM,CAAC;oBACP,gCAAgC;gBAClC,CAAC;gBACD,MAAM,KAAK,CAAC,EAAE,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACvC,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC,QAAQ,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The program store (roadmap 3.2): programs of procedure.define that outlive the process.
|
|
3
|
+
*/
|
|
4
|
+
export type { ProgramStore, StoredProgram } from "./types.js";
|
|
5
|
+
export { checkStoredProgram } from "./types.js";
|
|
6
|
+
export { MemoryProgramStore } from "./memory.js";
|
|
7
|
+
export { FileProgramStore } from "./file.js";
|
|
8
|
+
export { CollectionProgramStore } from "./collection.js";
|
|
9
|
+
export { attachProgramStore, detachProgramStore, getProgramStore } from "./attach.js";
|
|
10
|
+
export { procedureStoreProcedure, procedureLoadProcedure, procedureSyncProcedure, procedureRemoteProcedure, programStoreProcedures, } from "./procedures.js";
|
|
11
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/program-store/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,YAAY,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The program store (roadmap 3.2): programs of procedure.define that outlive the process.
|
|
3
|
+
*/
|
|
4
|
+
export { checkStoredProgram } from "./types.js";
|
|
5
|
+
export { MemoryProgramStore } from "./memory.js";
|
|
6
|
+
export { FileProgramStore } from "./file.js";
|
|
7
|
+
export { CollectionProgramStore } from "./collection.js";
|
|
8
|
+
export { attachProgramStore, detachProgramStore, getProgramStore } from "./attach.js";
|
|
9
|
+
export { procedureStoreProcedure, procedureLoadProcedure, procedureSyncProcedure, procedureRemoteProcedure, programStoreProcedures, } from "./procedures.js";
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/program-store/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAChD,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AACjD,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAC7C,OAAO,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AACtF,OAAO,EACL,uBAAuB,EACvB,sBAAsB,EACtB,sBAAsB,EACtB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A program store in memory: for tests, and for a process that keeps its programs only while it
|
|
3
|
+
* runs. Each read and write copies the record, so a caller that changes a result does not change
|
|
4
|
+
* the store.
|
|
5
|
+
*/
|
|
6
|
+
import { type ProgramStore, type StoredProgram } from "./types.js";
|
|
7
|
+
export declare class MemoryProgramStore implements ProgramStore {
|
|
8
|
+
readonly kind = "memory";
|
|
9
|
+
private readonly programs;
|
|
10
|
+
get(key: string): Promise<StoredProgram | undefined>;
|
|
11
|
+
list(prefix?: string): Promise<StoredProgram[]>;
|
|
12
|
+
put(program: StoredProgram): Promise<void>;
|
|
13
|
+
delete(key: string): Promise<boolean>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../src/program-store/memory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAyC,KAAK,YAAY,EAAE,KAAK,aAAa,EAAE,MAAM,YAAY,CAAC;AAE1G,qBAAa,kBAAmB,YAAW,YAAY;IACrD,QAAQ,CAAC,IAAI,YAAY;IACzB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAoC;IAEvD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC,CAGzD;IAEK,IAAI,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC,CAEpD;IAEK,GAAG,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAG/C;IAEK,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAE1C;CACF"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A program store in memory: for tests, and for a process that keeps its programs only while it
|
|
3
|
+
* runs. Each read and write copies the record, so a caller that changes a result does not change
|
|
4
|
+
* the store.
|
|
5
|
+
*/
|
|
6
|
+
import { byKey, checkStoredProgram, keyMatches } from "./types.js";
|
|
7
|
+
export class MemoryProgramStore {
|
|
8
|
+
kind = "memory";
|
|
9
|
+
programs = new Map();
|
|
10
|
+
async get(key) {
|
|
11
|
+
const program = this.programs.get(key);
|
|
12
|
+
return program ? structuredClone(program) : undefined;
|
|
13
|
+
}
|
|
14
|
+
async list(prefix) {
|
|
15
|
+
return byKey([...this.programs.values()].filter((p) => keyMatches(p.key, prefix)).map((p) => structuredClone(p)));
|
|
16
|
+
}
|
|
17
|
+
async put(program) {
|
|
18
|
+
const checked = checkStoredProgram(structuredClone(program));
|
|
19
|
+
this.programs.set(checked.key, checked);
|
|
20
|
+
}
|
|
21
|
+
async delete(key) {
|
|
22
|
+
return this.programs.delete(key);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../src/program-store/memory.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAyC,MAAM,YAAY,CAAC;AAE1G,MAAM,OAAO,kBAAkB;IACpB,IAAI,GAAG,QAAQ,CAAC;IACR,QAAQ,GAAG,IAAI,GAAG,EAAyB,CAAC;IAE7D,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACvC,OAAO,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACxD,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,MAAe;QACxB,OAAO,KAAK,CAAC,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACpH,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,OAAsB;QAC9B,MAAM,OAAO,GAAG,kBAAkB,CAAC,eAAe,CAAC,OAAO,CAAC,CAAC,CAAC;QAC7D,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;IAC1C,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,GAAW;QACtB,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;CACF"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The procedures of the program store (roadmap 3.2):
|
|
3
|
+
*
|
|
4
|
+
* - `procedure.store`: write the program of a runtime-defined procedure to the store.
|
|
5
|
+
* - `procedure.load`: define the stored programs again, through `procedure.define`.
|
|
6
|
+
* - `procedure.sync`: push the programs of the registry, pull the stored ones, or both.
|
|
7
|
+
* - `procedure.remote`: the status of the store. A store's backend is fixed when a host makes
|
|
8
|
+
* it, so `connect` and `disconnect` are NOT_IMPLEMENTED: a procedure that could point the
|
|
9
|
+
* store at a new endpoint would give a caller network reach.
|
|
10
|
+
*
|
|
11
|
+
* They use the program store of the caller's registry (`attachProgramStore`). A loaded program
|
|
12
|
+
* is a runtime-defined procedure again: data-driven, so a server with an expose rule lets it
|
|
13
|
+
* call only the exposed procedures. Load programs only from a store that you trust: a host with
|
|
14
|
+
* no expose rule runs whatever a program calls.
|
|
15
|
+
*/
|
|
16
|
+
import { type AnyProcedure } from "@procedures/client";
|
|
17
|
+
export declare const procedureStoreProcedure: AnyProcedure;
|
|
18
|
+
export declare const procedureLoadProcedure: AnyProcedure;
|
|
19
|
+
export declare const procedureSyncProcedure: AnyProcedure;
|
|
20
|
+
export declare const procedureRemoteProcedure: AnyProcedure;
|
|
21
|
+
/** The procedures of the program store. */
|
|
22
|
+
export declare const programStoreProcedures: AnyProcedure[];
|
|
23
|
+
//# sourceMappingURL=procedures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"procedures.d.ts","sourceRoot":"","sources":["../../src/program-store/procedures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAQL,KAAK,YAAY,EAGlB,MAAM,oBAAoB,CAAC;AAiF5B,eAAO,MAAM,uBAAuB,EAAE,YAuB5B,CAAC;AA0BX,eAAO,MAAM,sBAAsB,EAAE,YAsD3B,CAAC;AA2BX,eAAO,MAAM,sBAAsB,EAAE,YA+C3B,CAAC;AAkBX,eAAO,MAAM,wBAAwB,EAAE,YA+B7B,CAAC;AAEX,2CAA2C;AAC3C,eAAO,MAAM,sBAAsB,EAAE,YAAY,EAKhD,CAAC"}
|