@microdelta/history 0.0.0 → 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/README.md +19 -4
- package/dist/api/history.alpha.d.ts +3 -0
- package/dist/api/history.beta.d.ts +3 -0
- package/dist/api/history.conformance.store.alpha.d.ts +3 -0
- package/dist/api/history.conformance.store.beta.d.ts +3 -0
- package/dist/api/history.conformance.store.public.d.ts +3 -0
- package/dist/api/history.conformance.store.raw.alpha.d.ts +215 -0
- package/dist/api/history.conformance.store.raw.beta.d.ts +215 -0
- package/dist/api/history.conformance.store.raw.public.d.ts +215 -0
- package/dist/api/history.conformance.store.raw.untrimmed.d.ts +215 -0
- package/dist/api/history.conformance.store.untrimmed.d.ts +3 -0
- package/dist/api/history.public.d.ts +3 -0
- package/dist/api/history.root.raw.alpha.d.ts +1058 -0
- package/dist/api/history.root.raw.beta.d.ts +393 -0
- package/dist/api/history.root.raw.public.d.ts +393 -0
- package/dist/api/history.root.raw.untrimmed.d.ts +1059 -0
- package/dist/api/history.shared.alpha.d.ts +1091 -0
- package/dist/api/history.shared.beta.d.ts +426 -0
- package/dist/api/history.shared.public.d.ts +426 -0
- package/dist/api/history.shared.untrimmed.d.ts +1092 -0
- package/dist/api/history.untrimmed.d.ts +3 -0
- package/dist/api/tsdoc-metadata.json +11 -0
- package/dist/src/completed-results.d.ts +99 -0
- package/dist/src/completed-results.d.ts.map +1 -0
- package/dist/src/completed-results.js +2 -0
- package/dist/src/completed-results.js.map +1 -0
- package/dist/src/durable/contracts.d.ts +339 -0
- package/dist/src/durable/contracts.d.ts.map +1 -0
- package/dist/src/durable/contracts.js +2 -0
- package/dist/src/durable/contracts.js.map +1 -0
- package/dist/src/durable/errors.d.ts +69 -0
- package/dist/src/durable/errors.d.ts.map +1 -0
- package/dist/src/durable/errors.js +87 -0
- package/dist/src/durable/errors.js.map +1 -0
- package/dist/src/durable/index.d.ts +9 -0
- package/dist/src/durable/index.d.ts.map +1 -0
- package/dist/src/durable/index.js +590 -0
- package/dist/src/durable/index.js.map +1 -0
- package/dist/src/durable/locator.d.ts +31 -0
- package/dist/src/durable/locator.d.ts.map +1 -0
- package/dist/src/durable/locator.js +50 -0
- package/dist/src/durable/locator.js.map +1 -0
- package/dist/src/durable/schema.d.ts +28 -0
- package/dist/src/durable/schema.d.ts.map +1 -0
- package/dist/src/durable/schema.js +266 -0
- package/dist/src/durable/schema.js.map +1 -0
- package/dist/src/durable/selected-index.d.ts +94 -0
- package/dist/src/durable/selected-index.d.ts.map +1 -0
- package/dist/src/durable/selected-index.js +592 -0
- package/dist/src/durable/selected-index.js.map +1 -0
- package/dist/src/index.d.ts +26 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +13 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/store/index.d.ts +128 -0
- package/dist/src/store/index.d.ts.map +1 -0
- package/dist/src/store/index.js +33 -0
- package/dist/src/store/index.js.map +1 -0
- package/dist/src/store/memory/index.d.ts +24 -0
- package/dist/src/store/memory/index.d.ts.map +1 -0
- package/dist/src/store/memory/index.js +162 -0
- package/dist/src/store/memory/index.js.map +1 -0
- package/dist/src/types.d.ts +100 -0
- package/dist/src/types.d.ts.map +1 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/test/conformance/store/index.d.ts +34 -0
- package/dist/test/conformance/store/index.d.ts.map +1 -0
- package/dist/test/conformance/store/index.js +368 -0
- package/dist/test/conformance/store/index.js.map +1 -0
- package/package.json +48 -8
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exact completed-result locators of the durable authority (RES-002). A
|
|
3
|
+
* locator names one immutable result together with the logical store,
|
|
4
|
+
* analysis and environment that scope it, so resolution never guesses the
|
|
5
|
+
* current environment and never consults a current pointer. The grammar is
|
|
6
|
+
* versioned and canonical: `mdh1|` followed by the JSON array
|
|
7
|
+
* `[logicalStore, analysis, environment, resultId]`. Any other text, including
|
|
8
|
+
* a noncanonical spelling of the same components, is an integrity failure.
|
|
9
|
+
* @packageDocumentation
|
|
10
|
+
*/
|
|
11
|
+
import type { ICompletedResultReference } from '../completed-results.js';
|
|
12
|
+
/** The decoded components of one exact locator. */
|
|
13
|
+
export interface ILocatorParts {
|
|
14
|
+
/** The logical store that issued the result. */
|
|
15
|
+
readonly logicalStore: string;
|
|
16
|
+
/** The analysis scoping the result. */
|
|
17
|
+
readonly analysis: string;
|
|
18
|
+
/** The environment scoping the result. */
|
|
19
|
+
readonly environment: string;
|
|
20
|
+
/** The store-wide result identity, equal to its producing attempt identity. */
|
|
21
|
+
readonly resultId: number;
|
|
22
|
+
}
|
|
23
|
+
/** Issue the frozen exact reference for one result. */
|
|
24
|
+
export declare function referenceFor(parts: ILocatorParts): ICompletedResultReference;
|
|
25
|
+
/**
|
|
26
|
+
* Decode an exact reference. The reference must be a completed-result
|
|
27
|
+
* reference whose locator uses this grammar exactly; the caller then checks
|
|
28
|
+
* the components against the store and its rows.
|
|
29
|
+
*/
|
|
30
|
+
export declare function parseReference(reference: unknown): ILocatorParts;
|
|
31
|
+
//# sourceMappingURL=locator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.d.ts","sourceRoot":"","sources":["../../../src/durable/locator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yBAAyB,CAAC;AAMzE,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,gDAAgD;IAChD,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,uCAAuC;IACvC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,0CAA0C;IAC1C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,+EAA+E;IAC/E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,uDAAuD;AACvD,wBAAgB,YAAY,CAAC,KAAK,EAAE,aAAa,GAAG,yBAAyB,CAG5E;AAOD;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,SAAS,EAAE,OAAO,GAAG,aAAa,CA8BhE"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { HistoryIntegrityError } from './errors.js';
|
|
2
|
+
/** The only locator grammar version this implementation issues or accepts. */
|
|
3
|
+
const locatorPrefix = 'mdh1|';
|
|
4
|
+
/** Issue the frozen exact reference for one result. */
|
|
5
|
+
export function referenceFor(parts) {
|
|
6
|
+
const locator = `${locatorPrefix}${JSON.stringify([parts.logicalStore, parts.analysis, parts.environment, parts.resultId])}`;
|
|
7
|
+
return Object.freeze({ kind: 'completed-result', locator });
|
|
8
|
+
}
|
|
9
|
+
/** Whether a decoded component is a non-empty string. */
|
|
10
|
+
function isName(value) {
|
|
11
|
+
return typeof value === 'string' && value.length > 0;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Decode an exact reference. The reference must be a completed-result
|
|
15
|
+
* reference whose locator uses this grammar exactly; the caller then checks
|
|
16
|
+
* the components against the store and its rows.
|
|
17
|
+
*/
|
|
18
|
+
export function parseReference(reference) {
|
|
19
|
+
if (typeof reference !== 'object' || reference === null) {
|
|
20
|
+
throw new HistoryIntegrityError('A completed-result reference must be an object');
|
|
21
|
+
}
|
|
22
|
+
const kind = Reflect.get(reference, 'kind');
|
|
23
|
+
const locator = Reflect.get(reference, 'locator');
|
|
24
|
+
if (kind !== 'completed-result' || typeof locator !== 'string') {
|
|
25
|
+
throw new HistoryIntegrityError('A completed-result reference needs kind "completed-result" and a string locator');
|
|
26
|
+
}
|
|
27
|
+
if (!locator.startsWith(locatorPrefix)) {
|
|
28
|
+
throw new HistoryIntegrityError(`Unsupported completed-result locator grammar: ${JSON.stringify(locator.slice(0, 8))}`);
|
|
29
|
+
}
|
|
30
|
+
let decoded;
|
|
31
|
+
try {
|
|
32
|
+
decoded = JSON.parse(locator.slice(locatorPrefix.length));
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
throw new HistoryIntegrityError('Malformed completed-result locator');
|
|
36
|
+
}
|
|
37
|
+
if (!Array.isArray(decoded) || decoded.length !== 4) {
|
|
38
|
+
throw new HistoryIntegrityError('Malformed completed-result locator');
|
|
39
|
+
}
|
|
40
|
+
const [logicalStore, analysis, environment, resultId] = decoded;
|
|
41
|
+
if (!isName(logicalStore) || !isName(analysis) || !isName(environment) || typeof resultId !== 'number' || !Number.isSafeInteger(resultId) || resultId <= 0) {
|
|
42
|
+
throw new HistoryIntegrityError('Malformed completed-result locator components');
|
|
43
|
+
}
|
|
44
|
+
const parts = { logicalStore, analysis, environment, resultId };
|
|
45
|
+
if (referenceFor(parts).locator !== locator) {
|
|
46
|
+
throw new HistoryIntegrityError('Noncanonical completed-result locator');
|
|
47
|
+
}
|
|
48
|
+
return parts;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=locator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locator.js","sourceRoot":"","sources":["../../../src/durable/locator.ts"],"names":[],"mappings":"AAWA,OAAO,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpD,8EAA8E;AAC9E,MAAM,aAAa,GAAG,OAAO,CAAC;AAc9B,uDAAuD;AACvD,MAAM,UAAU,YAAY,CAAC,KAAoB;IAC/C,MAAM,OAAO,GAAG,GAAG,aAAa,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,KAAK,CAAC,YAAY,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,WAAW,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC;IAC7H,OAAO,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,kBAAkB,EAAE,OAAO,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,yDAAyD;AACzD,SAAS,MAAM,CAAC,KAAc;IAC5B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;AACvD,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,SAAkB;IAC/C,IAAI,OAAO,SAAS,KAAK,QAAQ,IAAI,SAAS,KAAK,IAAI,EAAE,CAAC;QACxD,MAAM,IAAI,qBAAqB,CAAC,gDAAgD,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,IAAI,GAAY,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACrD,MAAM,OAAO,GAAY,OAAO,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;IAC3D,IAAI,IAAI,KAAK,kBAAkB,IAAI,OAAO,OAAO,KAAK,QAAQ,EAAE,CAAC;QAC/D,MAAM,IAAI,qBAAqB,CAAC,iFAAiF,CAAC,CAAC;IACrH,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,qBAAqB,CAAC,iDAAiD,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAC1H,CAAC;IACD,IAAI,OAAgB,CAAC;IACrB,IAAI,CAAC;QACH,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAC5D,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,qBAAqB,CAAC,oCAAoC,CAAC,CAAC;IACxE,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,MAAM,IAAI,qBAAqB,CAAC,oCAAoC,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,CAAC,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,CAAC,GAAG,OAA6B,CAAC;IACtF,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,IAAI,OAAO,QAAQ,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,QAAQ,CAAC,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;QAC3J,MAAM,IAAI,qBAAqB,CAAC,+CAA+C,CAAC,CAAC;IACnF,CAAC;IACD,MAAM,KAAK,GAAkB,EAAE,YAAY,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,CAAC;IAC/E,IAAI,YAAY,CAAC,KAAK,CAAC,CAAC,OAAO,KAAK,OAAO,EAAE,CAAC;QAC5C,MAAM,IAAI,qBAAqB,CAAC,uCAAuC,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The versioned production schema of History's durable authority. It is
|
|
3
|
+
* distinct from the legacy row Store's compatibility schema and from the
|
|
4
|
+
* EXP-3 and nested-read experiment layouts. The schema is defined as an exact
|
|
5
|
+
* list of SQL objects; a database is usable only when it contains exactly
|
|
6
|
+
* these objects and one identity row naming this schema, version and logical
|
|
7
|
+
* store. Anything else (empty aside) is rejected before work: History never
|
|
8
|
+
* guesses a migration from unknown, incomplete or foreign storage.
|
|
9
|
+
*
|
|
10
|
+
* Immutability of completed history is enforced in storage by triggers:
|
|
11
|
+
* results, their generated index, dependencies, acceptance records and the
|
|
12
|
+
* identity row cannot be updated or deleted, and attempts, current pointers
|
|
13
|
+
* and writer/sequence rows cannot be deleted. No reclamation exists (RES-006).
|
|
14
|
+
* @packageDocumentation
|
|
15
|
+
*/
|
|
16
|
+
import type { ISqliteConnection } from '@microdelta/machine';
|
|
17
|
+
/** The schema identity recorded in every durable History file. */
|
|
18
|
+
export declare const schemaName = "microdelta.history.durable";
|
|
19
|
+
/** The only schema version this implementation reads or writes. */
|
|
20
|
+
export declare const schemaVersion = 1;
|
|
21
|
+
/**
|
|
22
|
+
* Create the schema in one transaction when the database has no objects,
|
|
23
|
+
* then validate it. Validation requires exactly the expected objects with
|
|
24
|
+
* their exact definitions and a single identity row naming this schema,
|
|
25
|
+
* version and the requested logical store.
|
|
26
|
+
*/
|
|
27
|
+
export declare function initializeSchema(connection: ISqliteConnection, logicalStore: string): void;
|
|
28
|
+
//# sourceMappingURL=schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/durable/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAc,MAAM,qBAAqB,CAAC;AAOzE,kEAAkE;AAClE,eAAO,MAAM,UAAU,+BAA+B,CAAC;AAEvD,mEAAmE;AACnE,eAAO,MAAM,aAAa,IAAI,CAAC;AAiN/B;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,GAAG,IAAI,CAqB1F"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { HistorySchemaError } from './errors.js';
|
|
2
|
+
/** Identity tag for embedded SQL; the text is passed through unchanged. */
|
|
3
|
+
const sql = String.raw;
|
|
4
|
+
/** The schema identity recorded in every durable History file. */
|
|
5
|
+
export const schemaName = 'microdelta.history.durable';
|
|
6
|
+
/** The only schema version this implementation reads or writes. */
|
|
7
|
+
export const schemaVersion = 1;
|
|
8
|
+
/** Tables whose rows may never be updated or deleted once written. */
|
|
9
|
+
const immutableTables = [
|
|
10
|
+
'history_identity',
|
|
11
|
+
'history_results',
|
|
12
|
+
'history_dependencies',
|
|
13
|
+
'history_nodes',
|
|
14
|
+
'history_edges',
|
|
15
|
+
'history_addresses',
|
|
16
|
+
'history_acceptances',
|
|
17
|
+
'history_acceptance_dependencies',
|
|
18
|
+
];
|
|
19
|
+
/** Tables whose rows may change state but are never deleted. */
|
|
20
|
+
const retainedTables = ['history_writer', 'history_sequences', 'history_attempts', 'history_current'];
|
|
21
|
+
/**
|
|
22
|
+
* Every schema object, one statement each, in creation order. Stored
|
|
23
|
+
* definitions are compared with these texts after whitespace normalization.
|
|
24
|
+
*/
|
|
25
|
+
const schemaObjects = [
|
|
26
|
+
// One row naming the schema, its version and the logical store this file holds.
|
|
27
|
+
sql `CREATE TABLE history_identity (
|
|
28
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
29
|
+
schema_name TEXT NOT NULL,
|
|
30
|
+
schema_version INTEGER NOT NULL,
|
|
31
|
+
logical_store TEXT NOT NULL CHECK (length(logical_store) > 0)
|
|
32
|
+
) STRICT`,
|
|
33
|
+
// The single logical writer: holder, monotonically increasing fence, lease
|
|
34
|
+
// expiry and the persisted clock high-water, all in History's clock domain.
|
|
35
|
+
sql `CREATE TABLE history_writer (
|
|
36
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
37
|
+
last_fence INTEGER NOT NULL CHECK (last_fence >= 0),
|
|
38
|
+
holder TEXT CHECK (holder IS NULL OR length(holder) > 0),
|
|
39
|
+
expires_at INTEGER NOT NULL CHECK (expires_at >= 0),
|
|
40
|
+
time_high_water INTEGER NOT NULL CHECK (time_high_water >= 0)
|
|
41
|
+
) STRICT`,
|
|
42
|
+
// Store-wide counters; issued values are never reissued (PUB-003).
|
|
43
|
+
sql `CREATE TABLE history_sequences (
|
|
44
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
45
|
+
last_attempt INTEGER NOT NULL CHECK (last_attempt >= 0),
|
|
46
|
+
last_publication INTEGER NOT NULL CHECK (last_publication >= 0),
|
|
47
|
+
last_acceptance INTEGER NOT NULL CHECK (last_acceptance >= 0)
|
|
48
|
+
) STRICT`,
|
|
49
|
+
// Durable attempts with their stable key, intent digest, staged candidate
|
|
50
|
+
// content and failure evidence. Staged content is never a completed result.
|
|
51
|
+
sql `CREATE TABLE history_attempts (
|
|
52
|
+
attempt_id INTEGER PRIMARY KEY CHECK (attempt_id > 0),
|
|
53
|
+
analysis TEXT NOT NULL CHECK (length(analysis) > 0),
|
|
54
|
+
environment TEXT NOT NULL CHECK (length(environment) > 0),
|
|
55
|
+
subject TEXT NOT NULL CHECK (length(subject) > 0),
|
|
56
|
+
version INTEGER NOT NULL CHECK (version > 0),
|
|
57
|
+
attempt_key TEXT NOT NULL CHECK (length(attempt_key) > 0),
|
|
58
|
+
intent_digest TEXT NOT NULL CHECK (length(intent_digest) > 0),
|
|
59
|
+
state TEXT NOT NULL CHECK (state IN ('allocated', 'staged', 'completed', 'failed', 'interrupted')),
|
|
60
|
+
allocated_fence INTEGER NOT NULL CHECK (allocated_fence > 0),
|
|
61
|
+
ended_fence INTEGER CHECK (ended_fence IS NULL OR ended_fence > 0),
|
|
62
|
+
staged_payload TEXT,
|
|
63
|
+
staged_provenance_format TEXT,
|
|
64
|
+
staged_provenance_version INTEGER,
|
|
65
|
+
staged_provenance TEXT,
|
|
66
|
+
outcome_format TEXT,
|
|
67
|
+
outcome_version INTEGER,
|
|
68
|
+
outcome TEXT,
|
|
69
|
+
UNIQUE (analysis, environment, subject, attempt_key),
|
|
70
|
+
UNIQUE (attempt_id, analysis, environment, subject, version),
|
|
71
|
+
CHECK ((staged_payload IS NULL) = (staged_provenance IS NULL)
|
|
72
|
+
AND (staged_payload IS NULL) = (staged_provenance_format IS NULL)
|
|
73
|
+
AND (staged_payload IS NULL) = (staged_provenance_version IS NULL)),
|
|
74
|
+
CHECK ((outcome IS NULL) = (outcome_format IS NULL) AND (outcome IS NULL) = (outcome_version IS NULL)),
|
|
75
|
+
CHECK ((state = 'allocated' AND staged_payload IS NULL AND ended_fence IS NULL AND outcome IS NULL)
|
|
76
|
+
OR (state = 'staged' AND staged_payload IS NOT NULL AND ended_fence IS NULL AND outcome IS NULL)
|
|
77
|
+
OR (state = 'completed' AND staged_payload IS NULL AND ended_fence IS NOT NULL AND outcome IS NULL)
|
|
78
|
+
OR (state IN ('failed', 'interrupted') AND ended_fence IS NOT NULL AND outcome IS NOT NULL))
|
|
79
|
+
) STRICT`,
|
|
80
|
+
// Immutable completed results. A result's identity is the attempt that
|
|
81
|
+
// produced it; its scope and version must equal that attempt's.
|
|
82
|
+
sql `CREATE TABLE history_results (
|
|
83
|
+
result_id INTEGER PRIMARY KEY,
|
|
84
|
+
analysis TEXT NOT NULL,
|
|
85
|
+
environment TEXT NOT NULL,
|
|
86
|
+
subject TEXT NOT NULL,
|
|
87
|
+
version INTEGER NOT NULL,
|
|
88
|
+
publication INTEGER NOT NULL UNIQUE CHECK (publication > 0),
|
|
89
|
+
published_fence INTEGER NOT NULL CHECK (published_fence > 0),
|
|
90
|
+
encoding TEXT NOT NULL,
|
|
91
|
+
index_version INTEGER NOT NULL,
|
|
92
|
+
payload TEXT NOT NULL,
|
|
93
|
+
provenance_format TEXT NOT NULL,
|
|
94
|
+
provenance_version INTEGER NOT NULL,
|
|
95
|
+
provenance TEXT NOT NULL,
|
|
96
|
+
UNIQUE (result_id, analysis, environment, subject),
|
|
97
|
+
FOREIGN KEY (result_id, analysis, environment, subject, version)
|
|
98
|
+
REFERENCES history_attempts (attempt_id, analysis, environment, subject, version)
|
|
99
|
+
) STRICT`,
|
|
100
|
+
sql `CREATE INDEX history_results_by_subject ON history_results (analysis, environment, subject, version, publication)`,
|
|
101
|
+
// Explicit exact dependencies recorded with staged content, in caller order.
|
|
102
|
+
sql `CREATE TABLE history_dependencies (
|
|
103
|
+
attempt_id INTEGER NOT NULL REFERENCES history_attempts (attempt_id),
|
|
104
|
+
position INTEGER NOT NULL CHECK (position >= 0),
|
|
105
|
+
dependency_id INTEGER NOT NULL REFERENCES history_results (result_id),
|
|
106
|
+
PRIMARY KEY (attempt_id, position)
|
|
107
|
+
) STRICT`,
|
|
108
|
+
// The generated selected index: one row per node of the canonical payload.
|
|
109
|
+
// Only `scalar` holds author data; everything else is shape or digest.
|
|
110
|
+
sql `CREATE TABLE history_nodes (
|
|
111
|
+
result_id INTEGER NOT NULL REFERENCES history_results (result_id),
|
|
112
|
+
node_id INTEGER NOT NULL CHECK (node_id >= 0),
|
|
113
|
+
kind TEXT NOT NULL CHECK (kind IN ('scalar', 'record', 'array')),
|
|
114
|
+
scalar TEXT,
|
|
115
|
+
array_length INTEGER,
|
|
116
|
+
own_keys TEXT,
|
|
117
|
+
prototype TEXT CHECK (prototype IS NULL OR prototype IN ('null', 'object', 'custom')),
|
|
118
|
+
prototype_node INTEGER,
|
|
119
|
+
terminal TEXT CHECK (terminal IS NULL OR terminal IN ('null', 'object')),
|
|
120
|
+
snapshot_fingerprint TEXT NOT NULL,
|
|
121
|
+
PRIMARY KEY (result_id, node_id),
|
|
122
|
+
CHECK ((kind = 'scalar' AND scalar IS NOT NULL AND array_length IS NULL AND own_keys IS NULL
|
|
123
|
+
AND prototype IS NULL AND prototype_node IS NULL AND terminal IS NULL)
|
|
124
|
+
OR (kind = 'array' AND scalar IS NULL AND array_length IS NOT NULL AND array_length >= 0 AND own_keys IS NULL
|
|
125
|
+
AND prototype IS NULL AND prototype_node IS NULL AND terminal IS NULL)
|
|
126
|
+
OR (kind = 'record' AND scalar IS NULL AND array_length IS NULL AND own_keys IS NOT NULL
|
|
127
|
+
AND prototype IS NOT NULL AND terminal IS NOT NULL AND (prototype = 'custom') = (prototype_node IS NOT NULL)))
|
|
128
|
+
) STRICT`,
|
|
129
|
+
// Structural slots: own record members and present array elements.
|
|
130
|
+
sql `CREATE TABLE history_edges (
|
|
131
|
+
result_id INTEGER NOT NULL REFERENCES history_results (result_id),
|
|
132
|
+
parent_node INTEGER NOT NULL,
|
|
133
|
+
edge_kind TEXT NOT NULL CHECK (edge_kind IN ('property', 'index')),
|
|
134
|
+
edge_key TEXT NOT NULL,
|
|
135
|
+
position INTEGER NOT NULL,
|
|
136
|
+
child_node INTEGER NOT NULL,
|
|
137
|
+
PRIMARY KEY (result_id, parent_node, edge_kind, edge_key)
|
|
138
|
+
) STRICT`,
|
|
139
|
+
// Every reachable structured address, with its node and MDO1 value digest.
|
|
140
|
+
sql `CREATE TABLE history_addresses (
|
|
141
|
+
result_id INTEGER NOT NULL REFERENCES history_results (result_id),
|
|
142
|
+
address TEXT NOT NULL,
|
|
143
|
+
node_id INTEGER NOT NULL,
|
|
144
|
+
own INTEGER NOT NULL CHECK (own IN (0, 1)),
|
|
145
|
+
value_fingerprint TEXT,
|
|
146
|
+
PRIMARY KEY (result_id, address),
|
|
147
|
+
CHECK ((address = '[]') = (value_fingerprint IS NULL))
|
|
148
|
+
) STRICT`,
|
|
149
|
+
// The latest publication of each scoped subject; acceptance never moves it.
|
|
150
|
+
sql `CREATE TABLE history_current (
|
|
151
|
+
analysis TEXT NOT NULL,
|
|
152
|
+
environment TEXT NOT NULL,
|
|
153
|
+
subject TEXT NOT NULL,
|
|
154
|
+
result_id INTEGER NOT NULL,
|
|
155
|
+
PRIMARY KEY (analysis, environment, subject),
|
|
156
|
+
FOREIGN KEY (result_id, analysis, environment, subject)
|
|
157
|
+
REFERENCES history_results (result_id, analysis, environment, subject)
|
|
158
|
+
) STRICT`,
|
|
159
|
+
// Current acceptance evidence, separate from the result's provenance.
|
|
160
|
+
sql `CREATE TABLE history_acceptances (
|
|
161
|
+
acceptance_id INTEGER PRIMARY KEY CHECK (acceptance_id > 0),
|
|
162
|
+
result_id INTEGER NOT NULL REFERENCES history_results (result_id),
|
|
163
|
+
fence INTEGER NOT NULL CHECK (fence > 0),
|
|
164
|
+
evidence_format TEXT NOT NULL,
|
|
165
|
+
evidence_version INTEGER NOT NULL,
|
|
166
|
+
evidence TEXT NOT NULL
|
|
167
|
+
) STRICT`,
|
|
168
|
+
sql `CREATE INDEX history_acceptances_by_result ON history_acceptances (result_id, acceptance_id)`,
|
|
169
|
+
sql `CREATE TABLE history_acceptance_dependencies (
|
|
170
|
+
acceptance_id INTEGER NOT NULL REFERENCES history_acceptances (acceptance_id),
|
|
171
|
+
position INTEGER NOT NULL CHECK (position >= 0),
|
|
172
|
+
dependency_id INTEGER NOT NULL REFERENCES history_results (result_id),
|
|
173
|
+
PRIMARY KEY (acceptance_id, position)
|
|
174
|
+
) STRICT`,
|
|
175
|
+
...immutableTables.flatMap((table) => [
|
|
176
|
+
sql `CREATE TRIGGER ${table}_immutable_update BEFORE UPDATE ON ${table}
|
|
177
|
+
BEGIN SELECT RAISE(ABORT, '${table} rows are immutable history'); END`,
|
|
178
|
+
sql `CREATE TRIGGER ${table}_immutable_delete BEFORE DELETE ON ${table}
|
|
179
|
+
BEGIN SELECT RAISE(ABORT, '${table} rows are immutable history'); END`,
|
|
180
|
+
]),
|
|
181
|
+
...retainedTables.map((table) => sql `CREATE TRIGGER ${table}_immutable_delete BEFORE DELETE ON ${table}
|
|
182
|
+
BEGIN SELECT RAISE(ABORT, '${table} rows are immutable history and are never deleted'); END`),
|
|
183
|
+
// An attempt's identity, scope, key, intent and allocating fence never change.
|
|
184
|
+
sql `CREATE TRIGGER history_attempts_identity_immutable
|
|
185
|
+
BEFORE UPDATE OF attempt_id, analysis, environment, subject, version, attempt_key, intent_digest, allocated_fence
|
|
186
|
+
ON history_attempts
|
|
187
|
+
BEGIN SELECT RAISE(ABORT, 'history_attempts identity is immutable history'); END`,
|
|
188
|
+
];
|
|
189
|
+
/** Collapse whitespace so stored definitions compare by meaning-preserving text. */
|
|
190
|
+
function normalize(text) {
|
|
191
|
+
return text.replace(/\s+/gu, ' ').trim();
|
|
192
|
+
}
|
|
193
|
+
/** The object name declared by one schema statement. */
|
|
194
|
+
function objectName(statement) {
|
|
195
|
+
const match = /^CREATE (?:TABLE|INDEX|TRIGGER) (\S+)/u.exec(normalize(statement));
|
|
196
|
+
if (match?.[1] === undefined) {
|
|
197
|
+
throw new Error('History schema statement has no object name');
|
|
198
|
+
}
|
|
199
|
+
return match[1];
|
|
200
|
+
}
|
|
201
|
+
/** Expected normalized definitions keyed by object name. */
|
|
202
|
+
const expectedObjects = new Map(schemaObjects.map((statement) => [objectName(statement), normalize(statement)]));
|
|
203
|
+
/** Every user-visible object currently in the database, keyed by name. */
|
|
204
|
+
function presentObjects(connection) {
|
|
205
|
+
const rows = connection.prepare(sql `/* schema */ SELECT name, sql FROM sqlite_master WHERE name NOT LIKE 'sqlite\_%' ESCAPE '\'`).all();
|
|
206
|
+
return new Map(rows.map((row) => [String(row.name), typeof row.sql === 'string' ? normalize(row.sql) : null]));
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Create the schema in one transaction when the database has no objects,
|
|
210
|
+
* then validate it. Validation requires exactly the expected objects with
|
|
211
|
+
* their exact definitions and a single identity row naming this schema,
|
|
212
|
+
* version and the requested logical store.
|
|
213
|
+
*/
|
|
214
|
+
export function initializeSchema(connection, logicalStore) {
|
|
215
|
+
// An existing store is validated without taking the write lock; only an
|
|
216
|
+
// apparently empty file enters the IMMEDIATE transaction, which rechecks.
|
|
217
|
+
if (presentObjects(connection).size > 0) {
|
|
218
|
+
validateSchema(connection, logicalStore);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
connection.transaction(() => {
|
|
222
|
+
if (presentObjects(connection).size > 0) {
|
|
223
|
+
return undefined;
|
|
224
|
+
}
|
|
225
|
+
for (const statement of schemaObjects) {
|
|
226
|
+
connection.exec(statement);
|
|
227
|
+
}
|
|
228
|
+
connection.prepare(sql `/* schema */ INSERT INTO history_identity (singleton, schema_name, schema_version, logical_store) VALUES (1, ?, ?, ?)`)
|
|
229
|
+
.run(schemaName, schemaVersion, logicalStore);
|
|
230
|
+
connection.prepare(sql `/* schema */ INSERT INTO history_writer (singleton, last_fence, holder, expires_at, time_high_water) VALUES (1, 0, NULL, 0, 0)`).run();
|
|
231
|
+
connection.prepare(sql `/* schema */ INSERT INTO history_sequences (singleton, last_attempt, last_publication, last_acceptance) VALUES (1, 0, 0, 0)`).run();
|
|
232
|
+
return undefined;
|
|
233
|
+
});
|
|
234
|
+
validateSchema(connection, logicalStore);
|
|
235
|
+
}
|
|
236
|
+
/** Reject any database that is not exactly this schema for this logical store. */
|
|
237
|
+
function validateSchema(connection, logicalStore) {
|
|
238
|
+
const present = presentObjects(connection);
|
|
239
|
+
const missing = [...expectedObjects.keys()].filter((name) => !present.has(name));
|
|
240
|
+
const unexpected = [...present.keys()].filter((name) => !expectedObjects.has(name));
|
|
241
|
+
if (missing.length > 0 || unexpected.length > 0) {
|
|
242
|
+
throw new HistorySchemaError(`Unsupported or incomplete History schema: missing [${missing.join(', ')}], unexpected [${unexpected.join(', ')}]`);
|
|
243
|
+
}
|
|
244
|
+
const altered = [...expectedObjects].filter(([name, definition]) => present.get(name) !== definition).map(([name]) => name);
|
|
245
|
+
if (altered.length > 0) {
|
|
246
|
+
throw new HistorySchemaError(`History schema objects differ from version ${String(schemaVersion)}: ${altered.join(', ')}`);
|
|
247
|
+
}
|
|
248
|
+
const identities = connection.prepare(sql `/* schema */ SELECT schema_name, schema_version, logical_store FROM history_identity`).all();
|
|
249
|
+
const identity = identities[0];
|
|
250
|
+
if (identities.length !== 1 || identity === undefined) {
|
|
251
|
+
throw new HistorySchemaError('History schema has no single identity row');
|
|
252
|
+
}
|
|
253
|
+
if (identity.schema_name !== schemaName || identity.schema_version !== schemaVersion) {
|
|
254
|
+
throw new HistorySchemaError(`Unsupported History schema ${String(identity.schema_name)} version ${String(identity.schema_version)}`);
|
|
255
|
+
}
|
|
256
|
+
if (identity.logical_store !== logicalStore) {
|
|
257
|
+
throw new HistorySchemaError('The SQLite file holds a different logical History store');
|
|
258
|
+
}
|
|
259
|
+
for (const table of ['history_writer', 'history_sequences']) {
|
|
260
|
+
const count = connection.prepare(sql `/* schema */ SELECT count(*) AS n FROM ${table}`).get();
|
|
261
|
+
if (count?.n !== 1) {
|
|
262
|
+
throw new HistorySchemaError(`History schema table ${table} does not have exactly one row`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/durable/schema.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* History's generated selected index over one immutable completed result,
|
|
3
|
+
* adopted from the accepted nested selected-read gate (issue #54, index
|
|
4
|
+
* requirements 1–7). History alone generates the index, inside the publish
|
|
5
|
+
* transaction, from the canonical MDS1 payload it stores; callers never supply
|
|
6
|
+
* index metadata. The index answers navigation, selected leaves and
|
|
7
|
+
* fingerprint metadata without decoding the root payload:
|
|
8
|
+
*
|
|
9
|
+
* - one node row per payload node (kind, MDV1 scalar leaf, array length, own
|
|
10
|
+
* keys in snapshot order, prototype link and chain terminal, MDS1 digest);
|
|
11
|
+
* - one edge row per own record member or present array element;
|
|
12
|
+
* - one address row per reachable structured address (including members
|
|
13
|
+
* inherited through supported prototypes) with its MDO1 `value` digest.
|
|
14
|
+
*
|
|
15
|
+
* Absent members are answered by evaluating Value on a payload-free stand-in of
|
|
16
|
+
* the longest indexed prefix. Reads verify what they return: a scalar leaf must
|
|
17
|
+
* match its address's `value` digest and a subtree its node's MDS1 digest, so
|
|
18
|
+
* corrupted storage fails as an integrity error instead of returning different
|
|
19
|
+
* data. Storage-shape failures never surface as `TypeError`, which is reserved
|
|
20
|
+
* for Value rejecting a selection's shape.
|
|
21
|
+
* @packageDocumentation
|
|
22
|
+
*/
|
|
23
|
+
import type { ISha256Capability, ISqliteConnection } from '@microdelta/machine';
|
|
24
|
+
import type { ICompletedNavigationReader, ICompletedResultReader, ICompletedResultReference } from '../completed-results.js';
|
|
25
|
+
import type { IResultVerification } from './contracts.js';
|
|
26
|
+
/** The index layout version recorded with every result. */
|
|
27
|
+
export declare const indexVersion = 1;
|
|
28
|
+
/** One indexed node; only `scalar` holds author payload. */
|
|
29
|
+
interface INodeRow {
|
|
30
|
+
readonly nodeId: number;
|
|
31
|
+
readonly kind: 'scalar' | 'record' | 'array';
|
|
32
|
+
/** MDV1 encoding of a scalar leaf. */
|
|
33
|
+
readonly scalar: string | null;
|
|
34
|
+
readonly arrayLength: number | null;
|
|
35
|
+
/** A record's own enumerable keys in snapshot order, as JSON. */
|
|
36
|
+
readonly ownKeys: string | null;
|
|
37
|
+
readonly prototype: 'null' | 'object' | 'custom' | null;
|
|
38
|
+
readonly prototypeNode: number | null;
|
|
39
|
+
/** Where a record's lookup chain ends; decides intrinsic-member semantics for absent keys. */
|
|
40
|
+
readonly terminal: 'null' | 'object' | null;
|
|
41
|
+
/** SHA-256 of the node's MDS1 snapshot. */
|
|
42
|
+
readonly snapshotFingerprint: string;
|
|
43
|
+
}
|
|
44
|
+
/** One structural slot: an own record member or a present array element. */
|
|
45
|
+
interface IEdgeRow {
|
|
46
|
+
readonly parentNode: number;
|
|
47
|
+
readonly edgeKind: 'property' | 'index';
|
|
48
|
+
readonly edgeKey: string;
|
|
49
|
+
/** Own enumeration position for records; the index for arrays. */
|
|
50
|
+
readonly position: number;
|
|
51
|
+
readonly childNode: number;
|
|
52
|
+
}
|
|
53
|
+
/** One reachable structured address. */
|
|
54
|
+
interface IAddressRow {
|
|
55
|
+
readonly address: string;
|
|
56
|
+
readonly nodeId: number;
|
|
57
|
+
/** Whether the final Property segment names an own member rather than an inherited one. */
|
|
58
|
+
readonly own: 0 | 1;
|
|
59
|
+
/** SHA-256 of the MDO1 `value` fact here; null for the root, which has no member address. */
|
|
60
|
+
readonly valueFingerprint: string | null;
|
|
61
|
+
}
|
|
62
|
+
/** The complete generated index for one result. */
|
|
63
|
+
export interface IIndexRows {
|
|
64
|
+
readonly nodes: readonly INodeRow[];
|
|
65
|
+
readonly edges: readonly IEdgeRow[];
|
|
66
|
+
readonly addresses: readonly IAddressRow[];
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Generate the index for one decoded canonical snapshot with a record or
|
|
70
|
+
* array root. Every container has exactly one structural parent because Value
|
|
71
|
+
* rejects shared references. Reachable addresses follow Value's lookup order:
|
|
72
|
+
* own members first, then members inherited through each custom prototype
|
|
73
|
+
* that an earlier level does not shadow.
|
|
74
|
+
*/
|
|
75
|
+
export declare function buildIndex(root: object, host: ISha256Capability): IIndexRows;
|
|
76
|
+
/** Resolves an exact reference to a verified result identity in this store and scope. */
|
|
77
|
+
export type IResultResolver = (reference: ICompletedResultReference) => number;
|
|
78
|
+
/** The generated index's write, read and verification operations for one connection. */
|
|
79
|
+
export interface ISelectedIndex {
|
|
80
|
+
/** Insert a result's generated index rows inside the caller's publish transaction. */
|
|
81
|
+
insert(resultId: number, rows: IIndexRows): void;
|
|
82
|
+
/** Exact scalar and navigation reader over completed results. */
|
|
83
|
+
readonly reader: ICompletedResultReader & ICompletedNavigationReader;
|
|
84
|
+
/** Regenerate a result's index from its stored canonical payload and compare. */
|
|
85
|
+
verify(resultId: number): IResultVerification;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Create the index operations over an owned connection. Exact references
|
|
89
|
+
* are resolved by the authority's `resolve`, which validates locator, store,
|
|
90
|
+
* scope, completion and stored encoding before any index row is read.
|
|
91
|
+
*/
|
|
92
|
+
export declare function createSelectedIndex(connection: ISqliteConnection, host: ISha256Capability, resolve: IResultResolver): ISelectedIndex;
|
|
93
|
+
export {};
|
|
94
|
+
//# sourceMappingURL=selected-index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"selected-index.d.ts","sourceRoot":"","sources":["../../../src/durable/selected-index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAA4B,MAAM,qBAAqB,CAAC;AAc1G,OAAO,KAAK,EACV,0BAA0B,EAC1B,sBAAsB,EACtB,yBAAyB,EAI1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAM1D,2DAA2D;AAC3D,eAAO,MAAM,YAAY,IAAI,CAAC;AAE9B,4DAA4D;AAC5D,UAAU,QAAQ;IAChB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,OAAO,CAAC;IAC7C,sCAAsC;IACtC,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,QAAQ,GAAG,IAAI,CAAC;IACxD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IACtC,8FAA8F;IAC9F,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC5C,2CAA2C;IAC3C,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;CACtC;AAED,4EAA4E;AAC5E,UAAU,QAAQ;IAChB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,OAAO,CAAC;IACxC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,wCAAwC;AACxC,UAAU,WAAW;IACnB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,2FAA2F;IAC3F,QAAQ,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC;IACpB,6FAA6F;IAC7F,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1C;AAED,mDAAmD;AACnD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,QAAQ,CAAC,KAAK,EAAE,SAAS,QAAQ,EAAE,CAAC;IACpC,QAAQ,CAAC,SAAS,EAAE,SAAS,WAAW,EAAE,CAAC;CAC5C;AAwBD;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,GAAG,UAAU,CA+F5E;AAgGD,yFAAyF;AACzF,MAAM,MAAM,eAAe,GAAG,CAAC,SAAS,EAAE,yBAAyB,KAAK,MAAM,CAAC;AAE/E,wFAAwF;AACxF,MAAM,WAAW,cAAc;IAC7B,sFAAsF;IACtF,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAC;IACjD,iEAAiE;IACjE,QAAQ,CAAC,MAAM,EAAE,sBAAsB,GAAG,0BAA0B,CAAC;IACrE,iFAAiF;IACjF,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,CAAC;CAC/C;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,eAAe,GAAG,cAAc,CAqYpI"}
|