@bandeira-tech/b3nd-save 0.8.1 → 0.13.1
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/esm/clients/save-client.d.ts +63 -29
- package/esm/clients/save-client.d.ts.map +1 -1
- package/esm/clients/save-client.js +148 -68
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/mod.d.ts +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/mod.d.ts.map +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/client-console/client.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/encoding/encoding.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/encrypt/mod.d.ts +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/encrypt/mod.d.ts.map +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/encrypt/mod.js +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/encrypt/utils.d.ts +6 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/encrypt/utils.d.ts.map +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/encrypt/utils.js +12 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/functional-client/functional-client.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/match-pattern/match-pattern.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/decorators.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/network.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/peer.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/policies/flood.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/policies/path-vector.d.ts +3 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/network/policies/path-vector.d.ts.map +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/policies/path-vector.js +3 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/policies/tell-and-read.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/types.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/observe-emitter/observe-emitter.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/connection.d.ts +13 -5
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/rig/connection.d.ts.map +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/connection.js +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/events.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/hooks.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/identity.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/operation-handle.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/reactions.d.ts.map +1 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/rig.d.ts +3 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/rig/rig.d.ts.map +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/rig.js +46 -4
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/types.d.ts +4 -4
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/rig/types.d.ts.map +1 -0
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/types/types.d.ts +119 -80
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/types/types.d.ts.map +1 -0
- package/esm/dispatch.d.ts +116 -3
- package/esm/dispatch.d.ts.map +1 -1
- package/esm/dispatch.js +381 -5
- package/esm/elasticsearch/store.d.ts +124 -25
- package/esm/elasticsearch/store.d.ts.map +1 -1
- package/esm/elasticsearch/store.js +451 -92
- package/esm/entity-store.d.ts +1 -1
- package/esm/errors.d.ts +1 -1
- package/esm/errors.js +1 -1
- package/esm/fs/mod.d.ts +26 -0
- package/esm/fs/mod.d.ts.map +1 -1
- package/esm/fs/store.d.ts +100 -23
- package/esm/fs/store.d.ts.map +1 -1
- package/esm/fs/store.js +250 -66
- package/esm/glob.d.ts +114 -0
- package/esm/glob.d.ts.map +1 -0
- package/esm/glob.js +352 -0
- package/esm/identifiers.d.ts +18 -0
- package/esm/identifiers.d.ts.map +1 -0
- package/esm/identifiers.js +19 -0
- package/esm/indexeddb/fields.d.ts +32 -0
- package/esm/indexeddb/fields.d.ts.map +1 -0
- package/esm/indexeddb/fields.js +51 -0
- package/esm/indexeddb/store.d.ts +72 -17
- package/esm/indexeddb/store.d.ts.map +1 -1
- package/esm/indexeddb/store.js +362 -106
- package/esm/ipfs/mod.d.ts +19 -0
- package/esm/ipfs/mod.d.ts.map +1 -1
- package/esm/ipfs/mod.js +19 -0
- package/esm/ipfs/store.d.ts +82 -21
- package/esm/ipfs/store.d.ts.map +1 -1
- package/esm/ipfs/store.js +269 -68
- package/esm/json-fields.d.ts +61 -0
- package/esm/json-fields.d.ts.map +1 -0
- package/esm/json-fields.js +121 -0
- package/esm/localstorage/store.d.ts +89 -21
- package/esm/localstorage/store.d.ts.map +1 -1
- package/esm/localstorage/store.js +247 -54
- package/esm/memory/store.d.ts +35 -4
- package/esm/memory/store.d.ts.map +1 -1
- package/esm/memory/store.js +69 -47
- package/esm/mod.d.ts +2 -0
- package/esm/mod.d.ts.map +1 -1
- package/esm/mod.js +1 -0
- package/esm/mongo/fields.d.ts +53 -0
- package/esm/mongo/fields.d.ts.map +1 -0
- package/esm/mongo/fields.js +71 -0
- package/esm/mongo/mod.d.ts +19 -4
- package/esm/mongo/mod.d.ts.map +1 -1
- package/esm/mongo/mod.js +4 -2
- package/esm/mongo/store.d.ts +114 -25
- package/esm/mongo/store.d.ts.map +1 -1
- package/esm/mongo/store.js +355 -102
- package/esm/postgres/store.d.ts +50 -1
- package/esm/postgres/store.d.ts.map +1 -1
- package/esm/postgres/store.js +133 -13
- package/esm/read.d.ts +54 -7
- package/esm/read.d.ts.map +1 -1
- package/esm/read.js +141 -16
- package/esm/s3/store.d.ts +125 -23
- package/esm/s3/store.d.ts.map +1 -1
- package/esm/s3/store.js +313 -57
- package/esm/sqlite/columns.d.ts +33 -0
- package/esm/sqlite/columns.d.ts.map +1 -0
- package/esm/sqlite/columns.js +51 -0
- package/esm/sqlite/store.d.ts +73 -25
- package/esm/sqlite/store.d.ts.map +1 -1
- package/esm/sqlite/store.js +411 -107
- package/esm/types.d.ts +1 -1
- package/esm/url.d.ts +122 -19
- package/esm/url.d.ts.map +1 -1
- package/esm/url.js +218 -24
- package/esm/walk-via-ls.d.ts +122 -0
- package/esm/walk-via-ls.d.ts.map +1 -0
- package/esm/walk-via-ls.js +178 -0
- package/package.json +1 -1
- package/esm/byte-entity-shim.d.ts +0 -47
- package/esm/byte-entity-shim.d.ts.map +0 -1
- package/esm/byte-entity-shim.js +0 -138
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/mod.d.ts.map +0 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/encrypt/mod.d.ts.map +0 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/network/policies/path-vector.d.ts.map +0 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/connection.d.ts.map +0 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/rig.d.ts.map +0 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/rig/types.d.ts.map +0 -1
- package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/0.22.0/src/types/types.d.ts.map +0 -1
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/mod.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/client-console/client.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/client-console/client.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/encoding/encoding.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/encoding/encoding.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/functional-client/functional-client.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/functional-client/functional-client.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/match-pattern/match-pattern.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/match-pattern/match-pattern.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/decorators.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/decorators.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/network.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/network.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/peer.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/peer.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/policies/flood.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/policies/flood.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/policies/tell-and-read.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/policies/tell-and-read.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/types.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/network/types.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/observe-emitter/observe-emitter.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/observe-emitter/observe-emitter.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/events.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/events.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/hooks.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/hooks.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/identity.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/identity.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/operation-handle.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/operation-handle.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/reactions.d.ts +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/reactions.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/rig/types.js +0 -0
- /package/esm/deps/jsr.io/@bandeira-tech/b3nd-core/{0.22.0 → 0.25.0}/src/types/types.js +0 -0
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared TYPE_TAGS planning + JSON record encoding used by backends
|
|
3
|
+
* that store custom-entity records as JSON-encoded bytes.
|
|
4
|
+
*
|
|
5
|
+
* The fs / s3 / ipfs / localstorage / elasticsearch backends all
|
|
6
|
+
* serialise records the same way:
|
|
7
|
+
*
|
|
8
|
+
* - bytes → base64 string
|
|
9
|
+
* - bigint → decimal string
|
|
10
|
+
* - timestamp → ISO-8601 string
|
|
11
|
+
* - everything else passes through unchanged (`number`, `boolean`,
|
|
12
|
+
* `string`, `json` objects).
|
|
13
|
+
*
|
|
14
|
+
* Centralising the encoder/decoder here keeps the round-trip
|
|
15
|
+
* guarantee — write on one JSON-encoding backend, read on another,
|
|
16
|
+
* get the same `EntityRecord` shape back. Mongo (BSON-native) and
|
|
17
|
+
* IndexedDB (structured-clone-native) preserve the typed values
|
|
18
|
+
* without JSON encoding, so they don't use this module.
|
|
19
|
+
*
|
|
20
|
+
* `computeSignature` is the canonical collision-detection signature
|
|
21
|
+
* over `{name, [field, tag] ...}` — two metas with identical names
|
|
22
|
+
* + identical per-field canonical tags hash to the same string;
|
|
23
|
+
* any shape difference (including a tag swap on an existing field
|
|
24
|
+
* name) yields a different one. Matches the granularity SQL backends
|
|
25
|
+
* get for free via column-type introspection.
|
|
26
|
+
*/
|
|
27
|
+
import { type EntityField, type EntityRecord } from "./entity.js";
|
|
28
|
+
export interface FieldPlan {
|
|
29
|
+
name: string;
|
|
30
|
+
tag: string;
|
|
31
|
+
}
|
|
32
|
+
export interface FieldPlanResult {
|
|
33
|
+
fields: FieldPlan[];
|
|
34
|
+
unsupported: {
|
|
35
|
+
name: string;
|
|
36
|
+
reason: string;
|
|
37
|
+
}[];
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Walk a schema's fields, picking the first recognised TYPE_TAGS
|
|
41
|
+
* entry per field as the canonical tag. Fields with no recognised
|
|
42
|
+
* tag (or an invalid field name) are reported under `unsupported`.
|
|
43
|
+
*/
|
|
44
|
+
export declare function planFields(fields: EntityField[]): FieldPlanResult;
|
|
45
|
+
/**
|
|
46
|
+
* Encode a record into a JSON-serialisable shape using the per-field
|
|
47
|
+
* canonical tag. Bytes → base64; bigint → string; timestamp →
|
|
48
|
+
* ISO-8601; everything else passes through unchanged.
|
|
49
|
+
*/
|
|
50
|
+
export declare function encodeRecord(fields: readonly FieldPlan[], record: EntityRecord): Record<string, unknown>;
|
|
51
|
+
/** Inverse of `encodeRecord` — restores `Uint8Array`/`BigInt`/`Date`. */
|
|
52
|
+
export declare function decodeRecord(fields: readonly FieldPlan[], source: Record<string, unknown>): EntityRecord;
|
|
53
|
+
/**
|
|
54
|
+
* Canonical signature over `{name, [field, tag] ...}` used by the
|
|
55
|
+
* provisioning bookkeeping to detect same-name different-shape
|
|
56
|
+
* collisions. Identical to Memory's signature scheme — a field whose
|
|
57
|
+
* canonical tag flips counts as a collision even though the field
|
|
58
|
+
* name is unchanged.
|
|
59
|
+
*/
|
|
60
|
+
export declare function computeSignature(name: string, fields: readonly FieldPlan[]): string;
|
|
61
|
+
//# sourceMappingURL=json-fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"json-fields.d.ts","sourceRoot":"","sources":["../src/json-fields.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AAGH,OAAO,EAAE,KAAK,WAAW,EAAE,KAAK,YAAY,EAAa,MAAM,aAAa,CAAC;AAK7E,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjD;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,WAAW,EAAE,GAAG,eAAe,CAyBjE;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,MAAM,EAAE,YAAY,GACnB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAkBzB;AAED,yEAAyE;AACzE,wBAAgB,YAAY,CAC1B,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,YAAY,CAcd;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,SAAS,SAAS,EAAE,GAC3B,MAAM,CAKR"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared TYPE_TAGS planning + JSON record encoding used by backends
|
|
3
|
+
* that store custom-entity records as JSON-encoded bytes.
|
|
4
|
+
*
|
|
5
|
+
* The fs / s3 / ipfs / localstorage / elasticsearch backends all
|
|
6
|
+
* serialise records the same way:
|
|
7
|
+
*
|
|
8
|
+
* - bytes → base64 string
|
|
9
|
+
* - bigint → decimal string
|
|
10
|
+
* - timestamp → ISO-8601 string
|
|
11
|
+
* - everything else passes through unchanged (`number`, `boolean`,
|
|
12
|
+
* `string`, `json` objects).
|
|
13
|
+
*
|
|
14
|
+
* Centralising the encoder/decoder here keeps the round-trip
|
|
15
|
+
* guarantee — write on one JSON-encoding backend, read on another,
|
|
16
|
+
* get the same `EntityRecord` shape back. Mongo (BSON-native) and
|
|
17
|
+
* IndexedDB (structured-clone-native) preserve the typed values
|
|
18
|
+
* without JSON encoding, so they don't use this module.
|
|
19
|
+
*
|
|
20
|
+
* `computeSignature` is the canonical collision-detection signature
|
|
21
|
+
* over `{name, [field, tag] ...}` — two metas with identical names
|
|
22
|
+
* + identical per-field canonical tags hash to the same string;
|
|
23
|
+
* any shape difference (including a tag swap on an existing field
|
|
24
|
+
* name) yields a different one. Matches the granularity SQL backends
|
|
25
|
+
* get for free via column-type introspection.
|
|
26
|
+
*/
|
|
27
|
+
import { decodeBase64, encodeBase64 } from "./deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/mod.js";
|
|
28
|
+
import { TYPE_TAGS } from "./entity.js";
|
|
29
|
+
import { IDENTIFIER_PATTERN, isValidIdentifier } from "./identifiers.js";
|
|
30
|
+
const KNOWN = new Set(Object.values(TYPE_TAGS));
|
|
31
|
+
/**
|
|
32
|
+
* Walk a schema's fields, picking the first recognised TYPE_TAGS
|
|
33
|
+
* entry per field as the canonical tag. Fields with no recognised
|
|
34
|
+
* tag (or an invalid field name) are reported under `unsupported`.
|
|
35
|
+
*/
|
|
36
|
+
export function planFields(fields) {
|
|
37
|
+
const out = [];
|
|
38
|
+
const unsupported = [];
|
|
39
|
+
for (const f of fields) {
|
|
40
|
+
if (!isValidIdentifier(f.name)) {
|
|
41
|
+
unsupported.push({
|
|
42
|
+
name: f.name,
|
|
43
|
+
reason: `field name must match ${IDENTIFIER_PATTERN.source}; got '${f.name}'`,
|
|
44
|
+
});
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
const tag = f.type.find((t) => KNOWN.has(t));
|
|
48
|
+
if (!tag) {
|
|
49
|
+
unsupported.push({
|
|
50
|
+
name: f.name,
|
|
51
|
+
reason: f.type.length === 0
|
|
52
|
+
? "field declares no type tags"
|
|
53
|
+
: `no recognised tag in [${f.type.join(", ")}]`,
|
|
54
|
+
});
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
out.push({ name: f.name, tag });
|
|
58
|
+
}
|
|
59
|
+
return { fields: out, unsupported };
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Encode a record into a JSON-serialisable shape using the per-field
|
|
63
|
+
* canonical tag. Bytes → base64; bigint → string; timestamp →
|
|
64
|
+
* ISO-8601; everything else passes through unchanged.
|
|
65
|
+
*/
|
|
66
|
+
export function encodeRecord(fields, record) {
|
|
67
|
+
const out = {};
|
|
68
|
+
for (const f of fields) {
|
|
69
|
+
const v = record[f.name];
|
|
70
|
+
if (v === undefined || v === null) {
|
|
71
|
+
out[f.name] = null;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
if (f.tag === "bytes")
|
|
75
|
+
out[f.name] = encodeBase64(v);
|
|
76
|
+
else if (f.tag === "bigint") {
|
|
77
|
+
out[f.name] = typeof v === "bigint" ? v.toString() : String(v);
|
|
78
|
+
}
|
|
79
|
+
else if (f.tag === "timestamp") {
|
|
80
|
+
out[f.name] = v instanceof Date
|
|
81
|
+
? v.toISOString()
|
|
82
|
+
: new Date(v).toISOString();
|
|
83
|
+
}
|
|
84
|
+
else
|
|
85
|
+
out[f.name] = v;
|
|
86
|
+
}
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/** Inverse of `encodeRecord` — restores `Uint8Array`/`BigInt`/`Date`. */
|
|
90
|
+
export function decodeRecord(fields, source) {
|
|
91
|
+
const out = {};
|
|
92
|
+
for (const f of fields) {
|
|
93
|
+
const v = source[f.name];
|
|
94
|
+
if (v === null || v === undefined) {
|
|
95
|
+
out[f.name] = undefined;
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
if (f.tag === "bytes")
|
|
99
|
+
out[f.name] = decodeBase64(v);
|
|
100
|
+
else if (f.tag === "bigint")
|
|
101
|
+
out[f.name] = BigInt(v);
|
|
102
|
+
else if (f.tag === "timestamp")
|
|
103
|
+
out[f.name] = new Date(v);
|
|
104
|
+
else
|
|
105
|
+
out[f.name] = v;
|
|
106
|
+
}
|
|
107
|
+
return out;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Canonical signature over `{name, [field, tag] ...}` used by the
|
|
111
|
+
* provisioning bookkeeping to detect same-name different-shape
|
|
112
|
+
* collisions. Identical to Memory's signature scheme — a field whose
|
|
113
|
+
* canonical tag flips counts as a collision even though the field
|
|
114
|
+
* name is unchanged.
|
|
115
|
+
*/
|
|
116
|
+
export function computeSignature(name, fields) {
|
|
117
|
+
const sorted = [...fields]
|
|
118
|
+
.map((f) => [f.name, f.tag])
|
|
119
|
+
.sort(([a], [b]) => a.localeCompare(b));
|
|
120
|
+
return JSON.stringify({ name, fields: sorted });
|
|
121
|
+
}
|
|
@@ -1,45 +1,113 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* LocalStorageStore — browser localStorage implementation of
|
|
2
|
+
* LocalStorageStore — browser localStorage implementation of `EntityStore`.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* One backend, many entities. Layouts:
|
|
5
|
+
*
|
|
6
|
+
* - `BYTES_ENTITY` → key `{keyPrefix}{uri}` with a base64-encoded
|
|
7
|
+
* payload string. Matches the original bytes-only layout, so
|
|
8
|
+
* existing deployments keep working without migration.
|
|
9
|
+
* - any other schema → key `{keyPrefix}entities/{entityName}/{uri}`
|
|
10
|
+
* with a JSON-encoded record string. Per-field canonical tags
|
|
11
|
+
* round-trip the JSON boundary (bytes → base64, bigint → string,
|
|
12
|
+
* timestamp → ISO-8601 — see `./fields.ts`).
|
|
13
|
+
*
|
|
14
|
+
* ## Lifecycle
|
|
15
|
+
*
|
|
16
|
+
* `entitySupport(schema)` is pure — it plans the field set and computes
|
|
17
|
+
* a signature for collision detection. `provisionEntity(meta)` writes a
|
|
18
|
+
* tiny bookkeeping key (`{keyPrefix}__meta__/entities/{name}`) holding
|
|
19
|
+
* the signature; subsequent `entityStatus(meta)` reads it back and
|
|
20
|
+
* compares. Same-name different-shape provisioning throws.
|
|
21
|
+
*
|
|
22
|
+
* Writes/reads/deletes consume the meta directly: there is no per-call
|
|
23
|
+
* cache lookup gate. If `meta`'s bookkeeping is missing on the medium,
|
|
24
|
+
* `write` and `delete` surface a `"not provisioned"` storage failure
|
|
25
|
+
* per entry; `read` returns misses.
|
|
26
|
+
*
|
|
27
|
+
* ## Read params
|
|
28
|
+
*
|
|
29
|
+
* `fn=ls` / `fn=count` are shallow direct-leaves only. localStorage has
|
|
30
|
+
* no indexable cursor, so `_directLeaves` walks all keys once under the
|
|
31
|
+
* entity's `keyRoot` and standard params (`limit`/`page`/`sortBy`
|
|
32
|
+
* `=uri`/`sortOrder`/`format`) are applied in memory via
|
|
33
|
+
* `applyReadParams` from `../read.ts`.
|
|
34
|
+
*
|
|
35
|
+
* `fn=find` (v2 §3.5) walks every key under the entity's `keyRoot`
|
|
36
|
+
* matching the URI prefix at any depth — `_walkDeep` mirrors
|
|
37
|
+
* `_directLeaves` minus the `rest.includes("/")` cutoff. With
|
|
38
|
+
* `pushDownFind: false`, dispatch applies the embedded glob, sort,
|
|
39
|
+
* cursor, limit, format, and appends the cursor-as-trailing-slot.
|
|
40
|
+
* localStorage has no engine to leverage for push-down — iteration IS
|
|
41
|
+
* the work — so the symmetric shape (ls = shallow walk, find = deep
|
|
42
|
+
* walk; dispatch post-processes both) keeps the code minimal.
|
|
43
|
+
*
|
|
44
|
+
* `status().fns` advertises `["read", "ls", "count", "find"]`.
|
|
7
45
|
*/
|
|
8
|
-
import type { DeleteResult, Output, StatusResult } from "../deps/jsr.io/@bandeira-tech/b3nd-core/0.
|
|
9
|
-
import { type BytesOnlyEntityMeta } from "../byte-entity-shim.js";
|
|
46
|
+
import type { DeleteResult, Output, StatusResult } from "../deps/jsr.io/@bandeira-tech/b3nd-core/0.25.0/src/types/types.js";
|
|
10
47
|
import type { EntityStore } from "../entity-store.js";
|
|
11
|
-
import type
|
|
48
|
+
import { type EntityMeta, type EntityRecord, type EntitySchema } from "../entity.js";
|
|
12
49
|
import type { StoreCapabilities, StoreWriteResult } from "../types.js";
|
|
13
|
-
|
|
50
|
+
import { type FieldPlan } from "../json-fields.js";
|
|
51
|
+
/**
|
|
52
|
+
* LocalStorageStore-specific entity handle.
|
|
53
|
+
*
|
|
54
|
+
* `keyRoot` is the full localStorage prefix under which records live
|
|
55
|
+
* (e.g. `b3nd:entities/users/`). `isBytes` selects the legacy
|
|
56
|
+
* base64-bytes value format vs the JSON record format. `fields`,
|
|
57
|
+
* `declared`, and `bytesFields` drive write-time validation and
|
|
58
|
+
* encoding; `signature` is used for collision detection in
|
|
59
|
+
* `entityStatus` / `provisionEntity`.
|
|
60
|
+
*/
|
|
61
|
+
export interface LocalStorageEntityMeta extends EntityMeta {
|
|
62
|
+
readonly keyRoot: string;
|
|
63
|
+
readonly isBytes: boolean;
|
|
64
|
+
readonly fields: readonly FieldPlan[];
|
|
65
|
+
readonly declared: ReadonlySet<string>;
|
|
66
|
+
readonly bytesFields: ReadonlySet<string>;
|
|
67
|
+
readonly signature: string;
|
|
68
|
+
}
|
|
69
|
+
export declare class LocalStorageStore implements EntityStore<LocalStorageEntityMeta> {
|
|
14
70
|
private readonly keyPrefix;
|
|
15
71
|
private readonly storage;
|
|
16
72
|
constructor(config?: {
|
|
17
73
|
keyPrefix?: string;
|
|
18
74
|
storage?: Storage;
|
|
19
75
|
});
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
write(meta: BytesOnlyEntityMeta, entries: {
|
|
76
|
+
entitySupport(schema: EntitySchema): LocalStorageEntityMeta;
|
|
77
|
+
entityStatus(meta: LocalStorageEntityMeta): Promise<"live" | "unprovisioned">;
|
|
78
|
+
provisionEntity(meta: LocalStorageEntityMeta): Promise<void>;
|
|
79
|
+
write(meta: LocalStorageEntityMeta, entries: {
|
|
25
80
|
uri: string;
|
|
26
81
|
record: EntityRecord;
|
|
27
82
|
}[]): Promise<StoreWriteResult[]>;
|
|
28
|
-
read<T = EntityRecord | undefined>(meta:
|
|
29
|
-
delete(meta: BytesOnlyEntityMeta, uris: string[]): Promise<DeleteResult[]>;
|
|
30
|
-
private _writeBytes;
|
|
31
|
-
private _readBytes;
|
|
83
|
+
read<T = EntityRecord | undefined>(meta: LocalStorageEntityMeta, urls: string[]): Promise<Output<T>[]>;
|
|
32
84
|
private _readOne;
|
|
33
85
|
/**
|
|
34
|
-
* Walk localStorage once, returning `
|
|
35
|
-
* whose
|
|
36
|
-
* paths are excluded by the slash check.
|
|
86
|
+
* Walk localStorage once, returning `Output<EntityRecord>` for every
|
|
87
|
+
* key under `meta.keyRoot` whose remainder is `uri + <segment>` with
|
|
88
|
+
* no further `/`. Subtree-only paths are excluded by the slash check.
|
|
37
89
|
*/
|
|
38
90
|
private _directLeaves;
|
|
91
|
+
/**
|
|
92
|
+
* Walk localStorage once, returning `Output<EntityRecord>` for every
|
|
93
|
+
* key under `meta.keyRoot` whose remainder starts with `uri` — at
|
|
94
|
+
* any depth. Powers `fn=find`. Unlike `_directLeaves`, there is no
|
|
95
|
+
* `/` cutoff: keys with further slashes past the prefix are kept.
|
|
96
|
+
* Dispatch applies the glob (and sort/cursor/limit/format) on top.
|
|
97
|
+
*/
|
|
98
|
+
private _walkDeep;
|
|
39
99
|
private _ls;
|
|
40
100
|
private _count;
|
|
41
|
-
|
|
101
|
+
delete(meta: LocalStorageEntityMeta, uris: string[]): Promise<DeleteResult[]>;
|
|
42
102
|
status(): Promise<StatusResult>;
|
|
43
103
|
capabilities(): StoreCapabilities;
|
|
104
|
+
private _readMetaSignature;
|
|
105
|
+
/**
|
|
106
|
+
* Collect any `ReadableStream` values on `bytes`-tagged fields into
|
|
107
|
+
* `Uint8Array` and return a shallow-copied record. Non-stream values
|
|
108
|
+
* pass through; matches the normalisation pattern used by Memory and
|
|
109
|
+
* Postgres.
|
|
110
|
+
*/
|
|
111
|
+
private _normaliseBytesFields;
|
|
44
112
|
}
|
|
45
113
|
//# sourceMappingURL=store.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/localstorage/store.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/localstorage/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,MAAM,EACN,YAAY,EACb,MAAM,mEAAmE,CAAC;AAO3E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,EAEL,KAAK,UAAU,EACf,KAAK,YAAY,EACjB,KAAK,YAAY,EAClB,MAAM,cAAc,CAAC;AAEtB,OAAO,KAAK,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAIL,KAAK,SAAS,EAEf,MAAM,mBAAmB,CAAC;AAO3B;;;;;;;;;GASG;AACH,MAAM,WAAW,sBAAuB,SAAQ,UAAU;IACxD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,SAAS,EAAE,CAAC;IACtC,QAAQ,CAAC,QAAQ,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACvC,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IAC1C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAMD,qBAAa,iBAAkB,YAAW,WAAW,CAAC,sBAAsB,CAAC;IAC3E,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAS;IACnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAU;gBAEtB,MAAM,GAAE;QAClB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,OAAO,CAAC;KACd;IAYN,aAAa,CAAC,MAAM,EAAE,YAAY,GAAG,sBAAsB;IA0C3D,YAAY,CACV,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,MAAM,GAAG,eAAe,CAAC;IAS9B,eAAe,CAAC,IAAI,EAAE,sBAAsB,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB5D,KAAK,CACT,IAAI,EAAE,sBAAsB,EAC5B,OAAO,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,YAAY,CAAA;KAAE,EAAE,GAC/C,OAAO,CAAC,gBAAgB,EAAE,CAAC;IAqE9B,IAAI,CAAC,CAAC,GAAG,YAAY,GAAG,SAAS,EAC/B,IAAI,EAAE,sBAAsB,EAC5B,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IA6BvB,OAAO,CAAC,QAAQ;IAUhB;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAkBrB;;;;;;OAMG;IACH,OAAO,CAAC,SAAS;IAkBjB,OAAO,CAAC,GAAG;IAWX,OAAO,CAAC,MAAM;IASd,MAAM,CACJ,IAAI,EAAE,sBAAsB,EAC5B,IAAI,EAAE,MAAM,EAAE,GACb,OAAO,CAAC,YAAY,EAAE,CAAC;IA+B1B,MAAM,IAAI,OAAO,CAAC,YAAY,CAAC;IA0B/B,YAAY,IAAI,iBAAiB;IAMjC,OAAO,CAAC,kBAAkB;IAM1B;;;;;OAKG;YACW,qBAAqB;CAmBpC"}
|