@braidlabs/skew 0.1.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/README.md +247 -0
- package/package.json +37 -0
- package/src/index.d.ts +12 -0
- package/src/index.js +14 -0
- package/src/index.js.map +1 -0
- package/src/lib/context.d.ts +26 -0
- package/src/lib/context.js +5 -0
- package/src/lib/context.js.map +1 -0
- package/src/lib/devtools.d.ts +51 -0
- package/src/lib/devtools.js +31 -0
- package/src/lib/devtools.js.map +1 -0
- package/src/lib/disabled.d.ts +48 -0
- package/src/lib/disabled.js +59 -0
- package/src/lib/disabled.js.map +1 -0
- package/src/lib/fingerprint.d.ts +32 -0
- package/src/lib/fingerprint.js +62 -0
- package/src/lib/fingerprint.js.map +1 -0
- package/src/lib/http.d.ts +70 -0
- package/src/lib/http.js +76 -0
- package/src/lib/http.js.map +1 -0
- package/src/lib/identity.d.ts +120 -0
- package/src/lib/identity.js +126 -0
- package/src/lib/identity.js.map +1 -0
- package/src/lib/lens.d.ts +133 -0
- package/src/lib/lens.js +337 -0
- package/src/lib/lens.js.map +1 -0
- package/src/lib/list.d.ts +18 -0
- package/src/lib/list.js +49 -0
- package/src/lib/list.js.map +1 -0
- package/src/lib/registry.d.ts +77 -0
- package/src/lib/registry.js +80 -0
- package/src/lib/registry.js.map +1 -0
- package/src/lib/result.d.ts +103 -0
- package/src/lib/result.js +78 -0
- package/src/lib/result.js.map +1 -0
- package/src/lib/storage.d.ts +86 -0
- package/src/lib/storage.js +190 -0
- package/src/lib/storage.js.map +1 -0
- package/src/lib/versioned.d.ts +242 -0
- package/src/lib/versioned.js +275 -0
- package/src/lib/versioned.js.map +1 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a result type rather than exceptions or `null`.
|
|
3
|
+
*
|
|
4
|
+
* Reading versioned data has several distinct failure modes, and they demand
|
|
5
|
+
* *different* remedies:
|
|
6
|
+
*
|
|
7
|
+
* - `ahead` — the data was written by a newer build than the one reading it,
|
|
8
|
+
* and no down-migration path is available. Refetch, resolve the
|
|
9
|
+
* contract, or force the client to update.
|
|
10
|
+
* - `gap` — a migration step is missing from the chain. A programming error,
|
|
11
|
+
* and one that must be loud rather than silently dropping data.
|
|
12
|
+
* - `invalid` — the payload does not match the shape its version claims, or the
|
|
13
|
+
* envelope belongs to a different contract entirely.
|
|
14
|
+
* - `threw` — a migration function itself failed.
|
|
15
|
+
* - `retired` — the data is older than the schema's declared base version
|
|
16
|
+
* (see `VersionedOptions.base`) and no loaded bundle or resolved
|
|
17
|
+
* contract supplies the missing steps. Unlike `gap`, this is a
|
|
18
|
+
* *policy* outcome, not a bug: the steps were deliberately
|
|
19
|
+
* deleted after telemetry showed the version idle. Remedy:
|
|
20
|
+
* discard and refetch, or offer the user a reset.
|
|
21
|
+
*
|
|
22
|
+
* Collapsing these into `null` (or a thrown `Error`) forces every caller to
|
|
23
|
+
* guess, and the guess is usually "discard it" — which is data loss for the
|
|
24
|
+
* `ahead` case, where the data is perfectly good and merely from the future.
|
|
25
|
+
*/
|
|
26
|
+
/** Discriminated failure reasons for a versioned read. */
|
|
27
|
+
export type SkewFailureReason = 'ahead' | 'gap' | 'invalid' | 'threw' | 'retired';
|
|
28
|
+
export interface SkewOk<T> {
|
|
29
|
+
readonly ok: true;
|
|
30
|
+
readonly value: T;
|
|
31
|
+
/**
|
|
32
|
+
* The version the data was written under when it was *older* than the
|
|
33
|
+
* reader and was migrated forward. `null` when the data was already current
|
|
34
|
+
* or was downgraded instead.
|
|
35
|
+
*/
|
|
36
|
+
readonly migratedFrom: number | null;
|
|
37
|
+
/**
|
|
38
|
+
* The version the data was written under when it was *newer* than the
|
|
39
|
+
* reader and was migrated **down** to the reader's version. `null` when the
|
|
40
|
+
* data was current or older. A non-null value means `value` is an honest
|
|
41
|
+
* but lossy projection — see {@link SkewOk.lossyPaths}.
|
|
42
|
+
*/
|
|
43
|
+
readonly downgradedFrom: number | null;
|
|
44
|
+
/**
|
|
45
|
+
* Dot-paths whose values are the migration's *guesses* rather than data the
|
|
46
|
+
* writer actually recorded — fields filled with defaults, placeholder
|
|
47
|
+
* constants, or clock-derived values. Anything downstream that treats a
|
|
48
|
+
* derived value as a reported one is trusting a guess; this list is how it
|
|
49
|
+
* can tell.
|
|
50
|
+
*/
|
|
51
|
+
readonly derivedPaths: readonly string[];
|
|
52
|
+
/**
|
|
53
|
+
* Dot-paths the newer shape carried that a down-migration had to discard.
|
|
54
|
+
* Only ever non-empty when {@link SkewOk.downgradedFrom} is set.
|
|
55
|
+
*/
|
|
56
|
+
readonly lossyPaths: readonly string[];
|
|
57
|
+
}
|
|
58
|
+
export interface SkewErr {
|
|
59
|
+
readonly ok: false;
|
|
60
|
+
readonly reason: SkewFailureReason;
|
|
61
|
+
/** Version found on the data (0 when un-enveloped legacy data). */
|
|
62
|
+
readonly found: number;
|
|
63
|
+
/** Version the reader expected. */
|
|
64
|
+
readonly expected: number;
|
|
65
|
+
/**
|
|
66
|
+
* The schema's base version — set only on `retired` failures, so telemetry
|
|
67
|
+
* can report how far below the floor the data was.
|
|
68
|
+
*/
|
|
69
|
+
readonly floor?: number;
|
|
70
|
+
readonly message: string;
|
|
71
|
+
readonly cause?: unknown;
|
|
72
|
+
}
|
|
73
|
+
export type SkewResult<T> = SkewOk<T> | SkewErr;
|
|
74
|
+
/** Optional success metadata — see the matching fields on {@link SkewOk}. */
|
|
75
|
+
export interface SkewOkMeta {
|
|
76
|
+
readonly migratedFrom?: number | null;
|
|
77
|
+
readonly downgradedFrom?: number | null;
|
|
78
|
+
readonly derivedPaths?: readonly string[];
|
|
79
|
+
readonly lossyPaths?: readonly string[];
|
|
80
|
+
}
|
|
81
|
+
export declare function ok<T>(value: T, meta?: SkewOkMeta | number | null): SkewOk<T>;
|
|
82
|
+
export declare function err(reason: SkewFailureReason, found: number, expected: number, message: string, cause?: unknown): SkewErr;
|
|
83
|
+
/**
|
|
84
|
+
* Narrowing helper, useful at call sites that only care about success.
|
|
85
|
+
*
|
|
86
|
+
* ```ts
|
|
87
|
+
* if (isOk(result)) use(result.value);
|
|
88
|
+
* ```
|
|
89
|
+
*/
|
|
90
|
+
export declare function isOk<T>(r: SkewResult<T>): r is SkewOk<T>;
|
|
91
|
+
export declare function isErr<T>(r: SkewResult<T>): r is SkewErr;
|
|
92
|
+
/**
|
|
93
|
+
* Unwraps a result, substituting a fallback on failure.
|
|
94
|
+
*
|
|
95
|
+
* Deliberately requires the fallback to be explicit — there is no `unwrap()`
|
|
96
|
+
* that throws, because throwing on stale data is almost never what an
|
|
97
|
+
* application wants at a read site.
|
|
98
|
+
*/
|
|
99
|
+
export declare function valueOr<T>(r: SkewResult<T>, fallback: T): T;
|
|
100
|
+
/**
|
|
101
|
+
* Maps the success value, preserving all migration metadata.
|
|
102
|
+
*/
|
|
103
|
+
export declare function mapResult<T, U>(r: SkewResult<T>, fn: (value: T) => U): SkewResult<U>;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Why a result type rather than exceptions or `null`.
|
|
3
|
+
*
|
|
4
|
+
* Reading versioned data has several distinct failure modes, and they demand
|
|
5
|
+
* *different* remedies:
|
|
6
|
+
*
|
|
7
|
+
* - `ahead` — the data was written by a newer build than the one reading it,
|
|
8
|
+
* and no down-migration path is available. Refetch, resolve the
|
|
9
|
+
* contract, or force the client to update.
|
|
10
|
+
* - `gap` — a migration step is missing from the chain. A programming error,
|
|
11
|
+
* and one that must be loud rather than silently dropping data.
|
|
12
|
+
* - `invalid` — the payload does not match the shape its version claims, or the
|
|
13
|
+
* envelope belongs to a different contract entirely.
|
|
14
|
+
* - `threw` — a migration function itself failed.
|
|
15
|
+
* - `retired` — the data is older than the schema's declared base version
|
|
16
|
+
* (see `VersionedOptions.base`) and no loaded bundle or resolved
|
|
17
|
+
* contract supplies the missing steps. Unlike `gap`, this is a
|
|
18
|
+
* *policy* outcome, not a bug: the steps were deliberately
|
|
19
|
+
* deleted after telemetry showed the version idle. Remedy:
|
|
20
|
+
* discard and refetch, or offer the user a reset.
|
|
21
|
+
*
|
|
22
|
+
* Collapsing these into `null` (or a thrown `Error`) forces every caller to
|
|
23
|
+
* guess, and the guess is usually "discard it" — which is data loss for the
|
|
24
|
+
* `ahead` case, where the data is perfectly good and merely from the future.
|
|
25
|
+
*/
|
|
26
|
+
const NO_PATHS = Object.freeze([]);
|
|
27
|
+
export function ok(value, meta = null) {
|
|
28
|
+
var _a, _b, _c, _d;
|
|
29
|
+
const normalized = typeof meta === 'number' || meta === null ? { migratedFrom: meta } : meta;
|
|
30
|
+
return {
|
|
31
|
+
ok: true,
|
|
32
|
+
value,
|
|
33
|
+
migratedFrom: (_a = normalized.migratedFrom) !== null && _a !== void 0 ? _a : null,
|
|
34
|
+
downgradedFrom: (_b = normalized.downgradedFrom) !== null && _b !== void 0 ? _b : null,
|
|
35
|
+
derivedPaths: (_c = normalized.derivedPaths) !== null && _c !== void 0 ? _c : NO_PATHS,
|
|
36
|
+
lossyPaths: (_d = normalized.lossyPaths) !== null && _d !== void 0 ? _d : NO_PATHS,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function err(reason, found, expected, message, cause) {
|
|
40
|
+
return { ok: false, reason, found, expected, message, cause };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Narrowing helper, useful at call sites that only care about success.
|
|
44
|
+
*
|
|
45
|
+
* ```ts
|
|
46
|
+
* if (isOk(result)) use(result.value);
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export function isOk(r) {
|
|
50
|
+
return r.ok;
|
|
51
|
+
}
|
|
52
|
+
export function isErr(r) {
|
|
53
|
+
return !r.ok;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Unwraps a result, substituting a fallback on failure.
|
|
57
|
+
*
|
|
58
|
+
* Deliberately requires the fallback to be explicit — there is no `unwrap()`
|
|
59
|
+
* that throws, because throwing on stale data is almost never what an
|
|
60
|
+
* application wants at a read site.
|
|
61
|
+
*/
|
|
62
|
+
export function valueOr(r, fallback) {
|
|
63
|
+
return r.ok ? r.value : fallback;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Maps the success value, preserving all migration metadata.
|
|
67
|
+
*/
|
|
68
|
+
export function mapResult(r, fn) {
|
|
69
|
+
return r.ok
|
|
70
|
+
? ok(fn(r.value), {
|
|
71
|
+
migratedFrom: r.migratedFrom,
|
|
72
|
+
downgradedFrom: r.downgradedFrom,
|
|
73
|
+
derivedPaths: r.derivedPaths,
|
|
74
|
+
lossyPaths: r.lossyPaths,
|
|
75
|
+
})
|
|
76
|
+
: r;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../../../../../libs/skew/src/lib/result.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAmEH,MAAM,QAAQ,GAAsB,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;AAEtD,MAAM,UAAU,EAAE,CAChB,KAAQ,EACR,OAAmC,IAAI;;IAEvC,MAAM,UAAU,GACd,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;IAC5E,OAAO;QACL,EAAE,EAAE,IAAI;QACR,KAAK;QACL,YAAY,EAAE,MAAA,UAAU,CAAC,YAAY,mCAAI,IAAI;QAC7C,cAAc,EAAE,MAAA,UAAU,CAAC,cAAc,mCAAI,IAAI;QACjD,YAAY,EAAE,MAAA,UAAU,CAAC,YAAY,mCAAI,QAAQ;QACjD,UAAU,EAAE,MAAA,UAAU,CAAC,UAAU,mCAAI,QAAQ;KAC9C,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,GAAG,CACjB,MAAyB,EACzB,KAAa,EACb,QAAgB,EAChB,OAAe,EACf,KAAe;IAEf,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;AAChE,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,IAAI,CAAI,CAAgB;IACtC,OAAO,CAAC,CAAC,EAAE,CAAC;AACd,CAAC;AAED,MAAM,UAAU,KAAK,CAAI,CAAgB;IACvC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;AACf,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,OAAO,CAAI,CAAgB,EAAE,QAAW;IACtD,OAAO,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC;AACnC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CACvB,CAAgB,EAChB,EAAmB;IAEnB,OAAO,CAAC,CAAC,EAAE;QACT,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,EAAE;YACd,YAAY,EAAE,CAAC,CAAC,YAAY;YAC5B,cAAc,EAAE,CAAC,CAAC,cAAc;YAChC,YAAY,EAAE,CAAC,CAAC,YAAY;YAC5B,UAAU,EAAE,CAAC,CAAC,UAAU;SACzB,CAAC;QACJ,CAAC,CAAC,CAAC,CAAC;AACR,CAAC"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { SkewResult } from './result.js';
|
|
2
|
+
import { VersionedSchema } from './versioned.js';
|
|
3
|
+
/**
|
|
4
|
+
* Versioned persistence.
|
|
5
|
+
*
|
|
6
|
+
* The failure this exists to prevent is the quiet one. A service that does
|
|
7
|
+
* `JSON.parse(raw) as T` is asserting a shape it has not checked; the moment
|
|
8
|
+
* the model changes, every cached record on every user's machine has the old
|
|
9
|
+
* shape while being typed as the new one. You get `undefined` deep inside a
|
|
10
|
+
* renderer rather than a clean failure at the boundary.
|
|
11
|
+
*/
|
|
12
|
+
/** Minimal key/value contract. Sync drivers may return values directly. */
|
|
13
|
+
export interface StorageDriver {
|
|
14
|
+
readonly sync: boolean;
|
|
15
|
+
get(key: string): string | null | Promise<string | null>;
|
|
16
|
+
set(key: string, value: string): void | Promise<void>;
|
|
17
|
+
remove(key: string): void | Promise<void>;
|
|
18
|
+
keys?(): string[] | Promise<string[]>;
|
|
19
|
+
}
|
|
20
|
+
export interface VersionedStoreOptions {
|
|
21
|
+
readonly driver: StorageDriver;
|
|
22
|
+
/** Namespace for keys. Defaults to the schema name. */
|
|
23
|
+
readonly keyPrefix?: string;
|
|
24
|
+
/** Stamped onto envelopes so a bad record can be attributed to a deployment. */
|
|
25
|
+
readonly buildId?: string;
|
|
26
|
+
/**
|
|
27
|
+
* Called whenever a read fails. The default is silence — callers inspect the
|
|
28
|
+
* returned result — but wiring this to telemetry is how you discover that a
|
|
29
|
+
* migration is wrong before users report it.
|
|
30
|
+
*/
|
|
31
|
+
readonly onReadFailure?: (key: string, failure: Extract<SkewResult<never>, {
|
|
32
|
+
ok: false;
|
|
33
|
+
}>) => void;
|
|
34
|
+
/**
|
|
35
|
+
* Write records back at the current version after a successful *migrated*
|
|
36
|
+
* read ("read-repair"). Each old record then pays its migration once
|
|
37
|
+
* instead of on every read — and stops appearing in `migratedFrom`
|
|
38
|
+
* telemetry, which is what makes eventually retiring its steps (see
|
|
39
|
+
* `VersionedOptions.base`) provable rather than hopeful.
|
|
40
|
+
*
|
|
41
|
+
* Downgraded reads (`downgradedFrom` set) are never written back:
|
|
42
|
+
* persisting the lossy projection would overwrite the newer record a
|
|
43
|
+
* newer build still needs.
|
|
44
|
+
*/
|
|
45
|
+
readonly rewriteOnRead?: boolean;
|
|
46
|
+
}
|
|
47
|
+
export interface VersionedStore<T> {
|
|
48
|
+
/** Reads and migrates. Returns a result — never throws on stale data. */
|
|
49
|
+
get(key: string): Promise<SkewResult<T>>;
|
|
50
|
+
/**
|
|
51
|
+
* Synchronous read, available only on sync drivers (localStorage, memory).
|
|
52
|
+
* Returns `null` on an async driver rather than lying about it.
|
|
53
|
+
*
|
|
54
|
+
* Exists because a signal or hook initialiser reading from localStorage
|
|
55
|
+
* should not have to flash empty state through a microtask.
|
|
56
|
+
*/
|
|
57
|
+
peek(key: string): SkewResult<T> | null;
|
|
58
|
+
set(key: string, value: T): Promise<void>;
|
|
59
|
+
remove(key: string): Promise<void>;
|
|
60
|
+
/** Fully-qualified storage key, exposed for debugging and cache-busting. */
|
|
61
|
+
keyFor(key: string): string;
|
|
62
|
+
}
|
|
63
|
+
export declare function createVersionedStore<T>(schema: VersionedSchema<T>, options: VersionedStoreOptions): VersionedStore<T>;
|
|
64
|
+
/** In-memory driver. The default in tests and on the server. */
|
|
65
|
+
export declare function memoryDriver(seed?: Map<string, string>): StorageDriver;
|
|
66
|
+
/**
|
|
67
|
+
* Web Storage driver. Degrades to memory when storage is unavailable —
|
|
68
|
+
* Safari private mode, disabled cookies, or SSR — so a read never throws in a
|
|
69
|
+
* place the caller cannot reasonably handle it.
|
|
70
|
+
*/
|
|
71
|
+
export declare function webStorageDriver(kind?: 'local' | 'session', win?: {
|
|
72
|
+
localStorage?: Storage;
|
|
73
|
+
sessionStorage?: Storage;
|
|
74
|
+
} | undefined): StorageDriver;
|
|
75
|
+
/** Configuration options for the IndexedDB driver. */
|
|
76
|
+
export interface IndexedDbDriverOptions {
|
|
77
|
+
/** The name of the IndexedDB database. Defaults to "skew-store". */
|
|
78
|
+
readonly dbName?: string;
|
|
79
|
+
/** The name of the object store. Defaults to "keyval". */
|
|
80
|
+
readonly storeName?: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* IndexedDB driver for async, large-capacity storage.
|
|
84
|
+
* Degrades gracefully on failure (e.g. if IndexedDB is blocked).
|
|
85
|
+
*/
|
|
86
|
+
export declare function indexedDbDriver(options?: IndexedDbDriverOptions): StorageDriver;
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { err } from './result.js';
|
|
3
|
+
import { isSkewDisabled } from './disabled.js';
|
|
4
|
+
export function createVersionedStore(schema, options) {
|
|
5
|
+
const { driver, keyPrefix = schema.name, buildId, onReadFailure, rewriteOnRead = false, } = options;
|
|
6
|
+
const keyFor = (key) => `${keyPrefix}:${key}`;
|
|
7
|
+
function parse(key, raw) {
|
|
8
|
+
if (raw === null) {
|
|
9
|
+
return err('invalid', 0, schema.version, `[${schema.name}] nothing stored at "${key}"`);
|
|
10
|
+
}
|
|
11
|
+
let decoded;
|
|
12
|
+
try {
|
|
13
|
+
decoded = JSON.parse(raw);
|
|
14
|
+
}
|
|
15
|
+
catch (cause) {
|
|
16
|
+
return err('invalid', 0, schema.version, `[${schema.name}] stored value is not JSON`, cause);
|
|
17
|
+
}
|
|
18
|
+
const result = schema.read(decoded);
|
|
19
|
+
if (!result.ok)
|
|
20
|
+
onReadFailure === null || onReadFailure === void 0 ? void 0 : onReadFailure(key, result);
|
|
21
|
+
else if (rewriteOnRead &&
|
|
22
|
+
result.migratedFrom !== null &&
|
|
23
|
+
!isSkewDisabled()) {
|
|
24
|
+
// Read-repair: persist the migrated record at the current version.
|
|
25
|
+
// Only for *upward* migrations — a downgraded read is a lossy
|
|
26
|
+
// projection of a newer record, and writing it back would destroy data.
|
|
27
|
+
try {
|
|
28
|
+
void driver.set(keyFor(key), JSON.stringify(schema.write(result.value, buildId)));
|
|
29
|
+
}
|
|
30
|
+
catch (_a) {
|
|
31
|
+
// A failing repair is a cache-write failure; the read still succeeded.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return result;
|
|
35
|
+
}
|
|
36
|
+
return {
|
|
37
|
+
keyFor,
|
|
38
|
+
get(key) {
|
|
39
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
40
|
+
const raw = yield driver.get(keyFor(key));
|
|
41
|
+
return parse(key, raw);
|
|
42
|
+
});
|
|
43
|
+
},
|
|
44
|
+
peek(key) {
|
|
45
|
+
if (!driver.sync)
|
|
46
|
+
return null;
|
|
47
|
+
const raw = driver.get(keyFor(key));
|
|
48
|
+
return parse(key, raw);
|
|
49
|
+
},
|
|
50
|
+
set(key, value) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
// Not public API — see `disabled.ts`. Writing the bare value is what a
|
|
53
|
+
// codebase without envelopes does, and it is the write side of the
|
|
54
|
+
// problem: nothing in the record says which build authored it, so no
|
|
55
|
+
// future reader can tell.
|
|
56
|
+
const stored = isSkewDisabled() ? value : schema.write(value, buildId);
|
|
57
|
+
yield driver.set(keyFor(key), JSON.stringify(stored));
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
remove(key) {
|
|
61
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
62
|
+
yield driver.remove(keyFor(key));
|
|
63
|
+
});
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
// --- drivers ---------------------------------------------------------------
|
|
68
|
+
/** In-memory driver. The default in tests and on the server. */
|
|
69
|
+
export function memoryDriver(seed) {
|
|
70
|
+
const map = seed !== null && seed !== void 0 ? seed : new Map();
|
|
71
|
+
return {
|
|
72
|
+
sync: true,
|
|
73
|
+
get: (k) => { var _a; return (_a = map.get(k)) !== null && _a !== void 0 ? _a : null; },
|
|
74
|
+
set: (k, v) => void map.set(k, v),
|
|
75
|
+
remove: (k) => void map.delete(k),
|
|
76
|
+
keys: () => [...map.keys()],
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Web Storage driver. Degrades to memory when storage is unavailable —
|
|
81
|
+
* Safari private mode, disabled cookies, or SSR — so a read never throws in a
|
|
82
|
+
* place the caller cannot reasonably handle it.
|
|
83
|
+
*/
|
|
84
|
+
export function webStorageDriver(kind = 'local', win = globalThis) {
|
|
85
|
+
const store = kind === 'local' ? win === null || win === void 0 ? void 0 : win.localStorage : win === null || win === void 0 ? void 0 : win.sessionStorage;
|
|
86
|
+
if (!store || !isUsable(store))
|
|
87
|
+
return memoryDriver();
|
|
88
|
+
return {
|
|
89
|
+
sync: true,
|
|
90
|
+
get: (k) => store.getItem(k),
|
|
91
|
+
set: (k, v) => {
|
|
92
|
+
try {
|
|
93
|
+
store.setItem(k, v);
|
|
94
|
+
}
|
|
95
|
+
catch (_a) {
|
|
96
|
+
// Quota exceeded. A cache write failing is not worth breaking a save
|
|
97
|
+
// the user asked for; the next read simply misses.
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
remove: (k) => store.removeItem(k),
|
|
101
|
+
keys: () => Object.keys(store),
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
function isUsable(store) {
|
|
105
|
+
const probe = '__skew_probe__';
|
|
106
|
+
try {
|
|
107
|
+
store.setItem(probe, '1');
|
|
108
|
+
store.removeItem(probe);
|
|
109
|
+
return true;
|
|
110
|
+
}
|
|
111
|
+
catch (_a) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* IndexedDB driver for async, large-capacity storage.
|
|
117
|
+
* Degrades gracefully on failure (e.g. if IndexedDB is blocked).
|
|
118
|
+
*/
|
|
119
|
+
export function indexedDbDriver(options = {}) {
|
|
120
|
+
const { dbName = 'skew-store', storeName = 'keyval' } = options;
|
|
121
|
+
let dbPromise = null;
|
|
122
|
+
function getDb() {
|
|
123
|
+
if (dbPromise)
|
|
124
|
+
return dbPromise;
|
|
125
|
+
if (typeof indexedDB === 'undefined') {
|
|
126
|
+
return Promise.reject(new Error('IndexedDB is not available'));
|
|
127
|
+
}
|
|
128
|
+
dbPromise = new Promise((resolve, reject) => {
|
|
129
|
+
const req = indexedDB.open(dbName, 1);
|
|
130
|
+
req.onupgradeneeded = () => {
|
|
131
|
+
const db = req.result;
|
|
132
|
+
if (!db.objectStoreNames.contains(storeName)) {
|
|
133
|
+
db.createObjectStore(storeName);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
req.onsuccess = () => resolve(req.result);
|
|
137
|
+
req.onerror = () => {
|
|
138
|
+
dbPromise = null;
|
|
139
|
+
reject(req.error);
|
|
140
|
+
};
|
|
141
|
+
});
|
|
142
|
+
return dbPromise;
|
|
143
|
+
}
|
|
144
|
+
function run(mode, callback) {
|
|
145
|
+
return getDb().then((db) => new Promise((resolve, reject) => {
|
|
146
|
+
const tx = db.transaction(storeName, mode);
|
|
147
|
+
const store = tx.objectStore(storeName);
|
|
148
|
+
const req = callback(store);
|
|
149
|
+
req.onsuccess = () => resolve(req.result);
|
|
150
|
+
req.onerror = () => reject(req.error);
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
return {
|
|
154
|
+
sync: false,
|
|
155
|
+
get: (key) => __awaiter(this, void 0, void 0, function* () {
|
|
156
|
+
try {
|
|
157
|
+
const val = yield run('readonly', (store) => store.get(key));
|
|
158
|
+
return val !== null && val !== void 0 ? val : null;
|
|
159
|
+
}
|
|
160
|
+
catch (_a) {
|
|
161
|
+
return null;
|
|
162
|
+
}
|
|
163
|
+
}),
|
|
164
|
+
set: (key, value) => __awaiter(this, void 0, void 0, function* () {
|
|
165
|
+
try {
|
|
166
|
+
yield run('readwrite', (store) => store.put(value, key));
|
|
167
|
+
}
|
|
168
|
+
catch (_a) {
|
|
169
|
+
// Fail silently on quota exceeded or other errors
|
|
170
|
+
}
|
|
171
|
+
}),
|
|
172
|
+
remove: (key) => __awaiter(this, void 0, void 0, function* () {
|
|
173
|
+
try {
|
|
174
|
+
yield run('readwrite', (store) => store.delete(key));
|
|
175
|
+
}
|
|
176
|
+
catch (_a) {
|
|
177
|
+
// A failed cache delete is a smaller problem than the throw would be.
|
|
178
|
+
}
|
|
179
|
+
}),
|
|
180
|
+
keys: () => __awaiter(this, void 0, void 0, function* () {
|
|
181
|
+
try {
|
|
182
|
+
return yield run('readonly', (store) => store.getAllKeys());
|
|
183
|
+
}
|
|
184
|
+
catch (_a) {
|
|
185
|
+
return [];
|
|
186
|
+
}
|
|
187
|
+
}),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
//# sourceMappingURL=storage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage.js","sourceRoot":"","sources":["../../../../../libs/skew/src/lib/storage.ts"],"names":[],"mappings":"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"}
|