@braidlabs/contract 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 +95 -0
- package/package.json +40 -0
- package/src/index.d.ts +3 -0
- package/src/index.js +4 -0
- package/src/index.js.map +1 -0
- package/src/lib/document.d.ts +62 -0
- package/src/lib/document.js +76 -0
- package/src/lib/document.js.map +1 -0
- package/src/lib/resolver.d.ts +60 -0
- package/src/lib/resolver.js +97 -0
- package/src/lib/resolver.js.map +1 -0
- package/src/lib/schema-from-contract.d.ts +45 -0
- package/src/lib/schema-from-contract.js +71 -0
- package/src/lib/schema-from-contract.js.map +1 -0
package/README.md
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# `@braidlabs/contract`
|
|
2
|
+
|
|
3
|
+
Data-driven schema migrations: the API that owns a contract **publishes its
|
|
4
|
+
history as a document**, and consumers — including consumers built before the
|
|
5
|
+
newest version existed — fetch it, interpret it, and migrate in either
|
|
6
|
+
direction. No client redeploy required.
|
|
7
|
+
|
|
8
|
+
```jsonc
|
|
9
|
+
// GET /.well-known/skew/contracts/portfolio-fund
|
|
10
|
+
{
|
|
11
|
+
"skewContract": "1",
|
|
12
|
+
"name": "portfolio-fund",
|
|
13
|
+
"current": 2,
|
|
14
|
+
"steps": [
|
|
15
|
+
{
|
|
16
|
+
"from": 1, "to": 2,
|
|
17
|
+
"description": "promote scalars to structure; add liquidity fields",
|
|
18
|
+
"ops": [
|
|
19
|
+
{ "rename": { "from": "currency", "to": "baseCurrency" } },
|
|
20
|
+
{ "wrap": { "path": "nav", "key": "amount", "also": { "asOf": { "$now": true } } } },
|
|
21
|
+
{ "move": { "from": "cashPct", "to": "liquidity.cashPct" } },
|
|
22
|
+
{ "default": { "path": "liquidity.hqlaPct", "value": 0 } }
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
],
|
|
26
|
+
"schemas": { "1": { /* JSON Schema */ }, "2": { /* JSON Schema */ } }
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Nothing in a document is executable. Ops are interpreted against a closed
|
|
31
|
+
whitelist (`rename`, `move`, `wrap`, `hoist`, `map`, `default`, `drop`,
|
|
32
|
+
`convert`, `const`), and each op knows its inverse — so declaring the up
|
|
33
|
+
migration buys the down migration, with the guessed fields (`derivedPaths`)
|
|
34
|
+
and the discarded ones (`lossyPaths`) computed rather than hand-annotated.
|
|
35
|
+
|
|
36
|
+
## Why this exists
|
|
37
|
+
|
|
38
|
+
The `ahead` failure — data written by a newer build than the reader — is the
|
|
39
|
+
one failure a code-shipped migration chain can never fix: the reader was built
|
|
40
|
+
before the knowledge existed. But **an API is always at least as new as the
|
|
41
|
+
newest data it serves.** So the origin that produced the too-new data can also
|
|
42
|
+
serve the document that explains it:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import { createContractResolver, wellKnownContractUrl } from '@braidlabs/contract';
|
|
46
|
+
|
|
47
|
+
const resolver = createContractResolver();
|
|
48
|
+
const url = wellKnownContractUrl(API_BASE, 'portfolio-fund');
|
|
49
|
+
|
|
50
|
+
// Reads exactly like schema.read(); the difference only appears on `ahead`:
|
|
51
|
+
// fetch the contract, learn the newer steps, read a downgraded projection.
|
|
52
|
+
const result = await resolver.readResolving(FundSchemaV1, body, url);
|
|
53
|
+
if (result.ok && result.downgradedFrom) {
|
|
54
|
+
// honest, lossy, and labeled: result.lossyPaths names what v1 cannot carry
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
## Building schemas from a document
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { versionedFromContract } from '@braidlabs/contract';
|
|
62
|
+
|
|
63
|
+
// Drop-in replacement for a hand-maintained versioned().next() chain:
|
|
64
|
+
const FundSchema = versionedFromContract<FundV2>(fundContract);
|
|
65
|
+
|
|
66
|
+
// A build pinned below current still learns the newer steps — they feed the
|
|
67
|
+
// shared registry, which is what read() consults to downgrade newer data:
|
|
68
|
+
const FundSchemaV1 = versionedFromContract<FundV1>(fundContract, { at: 1 });
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Steps a document cannot express (semantic transforms) are declared as
|
|
72
|
+
`"code": "derive-summary"` — the *name* of an implementation the consuming
|
|
73
|
+
bundle ships via `codeSteps`. A consumer without the implementation degrades
|
|
74
|
+
loudly with `gap`, never with a guess. A contract accumulating `code` steps is
|
|
75
|
+
a signal the change wanted a new resource, not a new version.
|
|
76
|
+
|
|
77
|
+
## Frozen types, generated
|
|
78
|
+
|
|
79
|
+
`skew-contract gen` (in `@braidlabs/build`) emits one frozen interface per
|
|
80
|
+
documented version plus the document as a typed const, which retires the
|
|
81
|
+
hardest rule in schema versioning — "never edit a past version's interface" —
|
|
82
|
+
by making the document the only source:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
skew-contract gen --in contracts/portfolio-fund.json \
|
|
86
|
+
--out src/generated/portfolio-fund.contract.ts
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Trust model
|
|
90
|
+
|
|
91
|
+
A contract document is fetched from the same origin whose data you already
|
|
92
|
+
trust, carries no executable code, is cached with ETag revalidation (a stale
|
|
93
|
+
copy still beats none when the origin is unreachable), and can be pinned by
|
|
94
|
+
content fingerprint (`pinnedFingerprints`) when the deployment pipeline knows
|
|
95
|
+
exactly which contract it was built against.
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@braidlabs/contract",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Data-driven schema migrations: publish a contract document from your API, resolve it at runtime, and migrate in both directions without shipping new client code.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"version-skew",
|
|
7
|
+
"migration",
|
|
8
|
+
"schema-versioning",
|
|
9
|
+
"api-contract",
|
|
10
|
+
"lens"
|
|
11
|
+
],
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"type": "module",
|
|
15
|
+
"main": "./src/index.js",
|
|
16
|
+
"types": "./src/index.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./src/index.d.ts",
|
|
20
|
+
"default": "./src/index.js"
|
|
21
|
+
},
|
|
22
|
+
"./package.json": "./package.json"
|
|
23
|
+
},
|
|
24
|
+
"peerDependencies": {
|
|
25
|
+
"@braidlabs/skew": "^0.1.1"
|
|
26
|
+
},
|
|
27
|
+
"publishConfig": {
|
|
28
|
+
"access": "public"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/braidjs/braid.git",
|
|
33
|
+
"directory": "libs/contract"
|
|
34
|
+
},
|
|
35
|
+
"homepage": "https://github.com/braidjs/braid#readme",
|
|
36
|
+
"bugs": {
|
|
37
|
+
"url": "https://github.com/braidjs/braid/issues"
|
|
38
|
+
},
|
|
39
|
+
"module": "./src/index.js"
|
|
40
|
+
}
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
package/src/index.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../libs/contract/src/index.ts"],"names":[],"mappings":"AAAA,cAAc,mBAAmB,CAAC;AAClC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mBAAmB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { LensOp } from '@braidlabs/skew';
|
|
2
|
+
/**
|
|
3
|
+
* The contract document — a schema's history as *data*.
|
|
4
|
+
*
|
|
5
|
+
* A closure-based migration chain can only travel inside the bundle that
|
|
6
|
+
* compiled it, which is why every consumer of a contract ends up hand-copying
|
|
7
|
+
* frozen interfaces and hoping they stay in agreement. A contract document is
|
|
8
|
+
* the same knowledge made portable: published by the API that owns the
|
|
9
|
+
* contract, fetched (or codegen'd) by consumers, interpreted — never
|
|
10
|
+
* executed — on arrival.
|
|
11
|
+
*
|
|
12
|
+
* The property that makes runtime resolution worth having: **an API is always
|
|
13
|
+
* at least as new as the newest data it serves.** A client that hits `ahead`
|
|
14
|
+
* can fetch the contract from the very origin that produced the too-new data,
|
|
15
|
+
* obtain the steps it was born too early to know, and migrate down. The
|
|
16
|
+
* `ahead` dead end resolves itself without redeploying the client.
|
|
17
|
+
*
|
|
18
|
+
* Each step is either:
|
|
19
|
+
*
|
|
20
|
+
* - `ops` — declarative, invertible-by-construction where the ops allow, the
|
|
21
|
+
* default for structural evolution; or
|
|
22
|
+
* - `code` — the name of a migration this document *cannot* express (a
|
|
23
|
+
* semantic transform). Consumers that carry an implementation under that
|
|
24
|
+
* name run it; consumers that don't degrade loudly with `gap`. A contract
|
|
25
|
+
* accumulating `code` steps is a signal the change wanted a new resource,
|
|
26
|
+
* not a new version.
|
|
27
|
+
*/
|
|
28
|
+
export interface SkewContractStep {
|
|
29
|
+
readonly from: number;
|
|
30
|
+
readonly to: number;
|
|
31
|
+
readonly description: string;
|
|
32
|
+
readonly ops?: readonly LensOp[];
|
|
33
|
+
readonly code?: string;
|
|
34
|
+
}
|
|
35
|
+
export interface SkewContractDocument {
|
|
36
|
+
/** Format version of the document itself. */
|
|
37
|
+
readonly skewContract: '1';
|
|
38
|
+
/** The contract's name — the same string consumers pass to `versioned()`. */
|
|
39
|
+
readonly name: string;
|
|
40
|
+
/** The newest version the publisher serves. */
|
|
41
|
+
readonly current: number;
|
|
42
|
+
/** Every transition, oldest first, covering v1 → `current` contiguously. */
|
|
43
|
+
readonly steps: readonly SkewContractStep[];
|
|
44
|
+
/** Optional JSON Schema per version, keyed by version number as a string. */
|
|
45
|
+
readonly schemas?: Readonly<Record<string, unknown>>;
|
|
46
|
+
}
|
|
47
|
+
/** The well-known path a Skew-aware origin publishes contracts under. */
|
|
48
|
+
export declare function wellKnownContractPath(name: string): string;
|
|
49
|
+
/** Absolute or origin-relative URL for a contract on a given base. */
|
|
50
|
+
export declare function wellKnownContractUrl(base: string, name: string): string;
|
|
51
|
+
/**
|
|
52
|
+
* Content fingerprint of a document — order-insensitive for object keys, so
|
|
53
|
+
* logically identical documents always agree. Used for pinning and as an
|
|
54
|
+
* ETag.
|
|
55
|
+
*/
|
|
56
|
+
export declare function contractFingerprint(doc: SkewContractDocument): string;
|
|
57
|
+
/**
|
|
58
|
+
* Validates an untrusted value as a contract document. Throws `TypeError`
|
|
59
|
+
* with a message naming the first problem — a malformed contract must fail at
|
|
60
|
+
* the boundary where it arrived, not later inside a migration.
|
|
61
|
+
*/
|
|
62
|
+
export declare function parseContractDocument(raw: unknown): SkewContractDocument;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { canonicalJson, fnv1a } from '@braidlabs/skew';
|
|
2
|
+
/** The well-known path a Skew-aware origin publishes contracts under. */
|
|
3
|
+
export function wellKnownContractPath(name) {
|
|
4
|
+
return `/.well-known/skew/contracts/${encodeURIComponent(name)}`;
|
|
5
|
+
}
|
|
6
|
+
/** Absolute or origin-relative URL for a contract on a given base. */
|
|
7
|
+
export function wellKnownContractUrl(base, name) {
|
|
8
|
+
return `${base.replace(/\/$/, '')}${wellKnownContractPath(name)}`;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Content fingerprint of a document — order-insensitive for object keys, so
|
|
12
|
+
* logically identical documents always agree. Used for pinning and as an
|
|
13
|
+
* ETag.
|
|
14
|
+
*/
|
|
15
|
+
export function contractFingerprint(doc) {
|
|
16
|
+
return fnv1a(canonicalJson(doc));
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Validates an untrusted value as a contract document. Throws `TypeError`
|
|
20
|
+
* with a message naming the first problem — a malformed contract must fail at
|
|
21
|
+
* the boundary where it arrived, not later inside a migration.
|
|
22
|
+
*/
|
|
23
|
+
export function parseContractDocument(raw) {
|
|
24
|
+
const fail = (message) => {
|
|
25
|
+
throw new TypeError(`skew contract: ${message}`);
|
|
26
|
+
};
|
|
27
|
+
if (typeof raw !== 'object' || raw === null)
|
|
28
|
+
fail('document must be an object');
|
|
29
|
+
const doc = raw;
|
|
30
|
+
if (doc['skewContract'] !== '1') {
|
|
31
|
+
fail(`unsupported document format "${String(doc['skewContract'])}" — this reader understands "1"`);
|
|
32
|
+
}
|
|
33
|
+
if (typeof doc['name'] !== 'string' || doc['name'].length === 0) {
|
|
34
|
+
fail('name must be a non-empty string');
|
|
35
|
+
}
|
|
36
|
+
const current = doc['current'];
|
|
37
|
+
if (!Number.isInteger(current) || current < 1) {
|
|
38
|
+
fail(`current must be a positive integer, got ${String(current)}`);
|
|
39
|
+
}
|
|
40
|
+
if (!Array.isArray(doc['steps']))
|
|
41
|
+
fail('steps must be an array');
|
|
42
|
+
const steps = doc['steps'];
|
|
43
|
+
if (steps.length !== current - 1) {
|
|
44
|
+
fail(`steps must cover v1 → v${String(current)} contiguously: expected ${current - 1} steps, got ${steps.length}`);
|
|
45
|
+
}
|
|
46
|
+
steps.forEach((entry, index) => {
|
|
47
|
+
if (typeof entry !== 'object' || entry === null)
|
|
48
|
+
fail(`step ${index} must be an object`);
|
|
49
|
+
const step = entry;
|
|
50
|
+
const expectedTo = index + 2;
|
|
51
|
+
if (step['to'] !== expectedTo) {
|
|
52
|
+
fail(`step ${index} must target v${expectedTo}, got ${String(step['to'])}`);
|
|
53
|
+
}
|
|
54
|
+
if (step['from'] !== expectedTo - 1) {
|
|
55
|
+
fail(`step ${index} must come from v${expectedTo - 1}, got ${String(step['from'])}`);
|
|
56
|
+
}
|
|
57
|
+
if (typeof step['description'] !== 'string' || step['description'].length === 0) {
|
|
58
|
+
fail(`step ${index} needs a description`);
|
|
59
|
+
}
|
|
60
|
+
const hasOps = step['ops'] !== undefined;
|
|
61
|
+
const hasCode = step['code'] !== undefined;
|
|
62
|
+
if (hasOps === hasCode) {
|
|
63
|
+
fail(`step ${index} must declare exactly one of "ops" or "code"`);
|
|
64
|
+
}
|
|
65
|
+
if (hasOps && !Array.isArray(step['ops']))
|
|
66
|
+
fail(`step ${index} ops must be an array`);
|
|
67
|
+
if (hasCode && (typeof step['code'] !== 'string' || step['code'].length === 0)) {
|
|
68
|
+
fail(`step ${index} code must be a non-empty migration name`);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
if (doc['schemas'] !== undefined && (typeof doc['schemas'] !== 'object' || doc['schemas'] === null)) {
|
|
72
|
+
fail('schemas, when present, must be an object keyed by version');
|
|
73
|
+
}
|
|
74
|
+
return raw;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=document.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"document.js","sourceRoot":"","sources":["../../../../../libs/contract/src/lib/document.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { ReadOptions, SkewResult, VersionedSchema } from '@braidlabs/skew';
|
|
2
|
+
import { SkewContractDocument } from './document.js';
|
|
3
|
+
import { ContractCodeStep } from './schema-from-contract.js';
|
|
4
|
+
/**
|
|
5
|
+
* Runtime contract resolution — the async layer over `@braidlabs/skew`'s
|
|
6
|
+
* synchronous reads.
|
|
7
|
+
*
|
|
8
|
+
* Trust model: a contract document is fetched from the same origin whose
|
|
9
|
+
* *data* you already trust, and nothing in it is executable — ops are
|
|
10
|
+
* interpreted against a closed whitelist, and `code` steps only name
|
|
11
|
+
* implementations the consuming bundle itself ships. For belt-and-braces
|
|
12
|
+
* deployments, `pinnedFingerprints` refuses a document whose content hash
|
|
13
|
+
* moved.
|
|
14
|
+
*/
|
|
15
|
+
export interface ContractResolverOptions {
|
|
16
|
+
/** Defaults to the global `fetch`. Injected for tests and odd runtimes. */
|
|
17
|
+
readonly fetchImpl?: typeof fetch;
|
|
18
|
+
/** Implementations for named `code` steps, passed through to every load. */
|
|
19
|
+
readonly codeSteps?: Readonly<Record<string, ContractCodeStep<any, any>>>;
|
|
20
|
+
/**
|
|
21
|
+
* Expected content fingerprint per contract URL. A resolved document whose
|
|
22
|
+
* fingerprint differs is refused — use when the deployment pipeline knows
|
|
23
|
+
* exactly which contract it was built against.
|
|
24
|
+
*/
|
|
25
|
+
readonly pinnedFingerprints?: Readonly<Record<string, string>>;
|
|
26
|
+
/**
|
|
27
|
+
* List-schema names to derive per contract name. An API publishes the
|
|
28
|
+
* *item* contract (`portfolio-fund`); a client often reads the collection
|
|
29
|
+
* through its own list schema (`portfolio-funds`). Declaring
|
|
30
|
+
* `{ 'portfolio-fund': 'portfolio-funds' }` here makes every resolve also
|
|
31
|
+
* register the item steps lifted element-wise under the list name.
|
|
32
|
+
*/
|
|
33
|
+
readonly lists?: Readonly<Record<string, string>>;
|
|
34
|
+
}
|
|
35
|
+
export interface ContractResolver {
|
|
36
|
+
/**
|
|
37
|
+
* Fetches, validates, and caches a contract document. Subsequent calls for
|
|
38
|
+
* the same URL revalidate with `If-None-Match` when the origin sent an
|
|
39
|
+
* ETag, and fall back to the cached copy when the origin is unreachable —
|
|
40
|
+
* a stale contract is still a better guide than none.
|
|
41
|
+
*/
|
|
42
|
+
resolve(url: string): Promise<SkewContractDocument>;
|
|
43
|
+
/**
|
|
44
|
+
* Resolves a document and registers every runnable step with the shared
|
|
45
|
+
* registry — after which any schema of the same contract name, in any
|
|
46
|
+
* bundle of the page, can read across versions it never shipped.
|
|
47
|
+
*/
|
|
48
|
+
resolveAndRegister(url: string): Promise<SkewContractDocument>;
|
|
49
|
+
/**
|
|
50
|
+
* The `ahead` cure, packaged: read; if the result is `ahead`, resolve the
|
|
51
|
+
* contract (which the origin serves, and the origin is always at least as
|
|
52
|
+
* new as the data it produced), then read again with the newly registered
|
|
53
|
+
* down-steps. Any other outcome — success or failure — passes through
|
|
54
|
+
* untouched.
|
|
55
|
+
*/
|
|
56
|
+
readResolving<T>(schema: VersionedSchema<T>, raw: unknown, contractUrl: string, options?: ReadOptions): Promise<SkewResult<T>>;
|
|
57
|
+
/** Drops the cache — the next resolve refetches unconditionally. */
|
|
58
|
+
invalidate(url?: string): void;
|
|
59
|
+
}
|
|
60
|
+
export declare function createContractResolver(options?: ContractResolverOptions): ContractResolver;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { __awaiter } from "tslib";
|
|
2
|
+
import { registerSchema, versionedList, } from '@braidlabs/skew';
|
|
3
|
+
import { contractFingerprint, parseContractDocument, } from './document.js';
|
|
4
|
+
import { versionedFromContract } from './schema-from-contract.js';
|
|
5
|
+
export function createContractResolver(options = {}) {
|
|
6
|
+
var _a;
|
|
7
|
+
const cache = new Map();
|
|
8
|
+
const inFlight = new Map();
|
|
9
|
+
// Wrapped rather than referenced: a bare `fetch` alias loses its `window`
|
|
10
|
+
// receiver in browsers and throws "Illegal invocation".
|
|
11
|
+
const doFetch = (_a = options.fetchImpl) !== null && _a !== void 0 ? _a : ((input, init) => fetch(input, init));
|
|
12
|
+
function fetchDocument(url) {
|
|
13
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
14
|
+
var _a;
|
|
15
|
+
const cached = cache.get(url);
|
|
16
|
+
const headers = { accept: 'application/json' };
|
|
17
|
+
if (cached === null || cached === void 0 ? void 0 : cached.etag)
|
|
18
|
+
headers['if-none-match'] = cached.etag;
|
|
19
|
+
let response;
|
|
20
|
+
try {
|
|
21
|
+
response = yield doFetch(url, { headers });
|
|
22
|
+
}
|
|
23
|
+
catch (cause) {
|
|
24
|
+
if (cached)
|
|
25
|
+
return cached.doc;
|
|
26
|
+
const unreachable = new Error(`skew contract: could not reach ${url}`);
|
|
27
|
+
unreachable.cause = cause;
|
|
28
|
+
throw unreachable;
|
|
29
|
+
}
|
|
30
|
+
if (response.status === 304 && cached)
|
|
31
|
+
return cached.doc;
|
|
32
|
+
if (!response.ok) {
|
|
33
|
+
if (cached)
|
|
34
|
+
return cached.doc;
|
|
35
|
+
throw new Error(`skew contract: ${url} answered HTTP ${response.status}`);
|
|
36
|
+
}
|
|
37
|
+
const doc = parseContractDocument(yield response.json());
|
|
38
|
+
const pinned = (_a = options.pinnedFingerprints) === null || _a === void 0 ? void 0 : _a[url];
|
|
39
|
+
if (pinned !== undefined) {
|
|
40
|
+
const actual = contractFingerprint(doc);
|
|
41
|
+
if (actual !== pinned) {
|
|
42
|
+
throw new Error(`skew contract: ${url} resolved to fingerprint ${actual}, but this build pins ${pinned} — refusing the document`);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
cache.set(url, { doc, etag: response.headers.get('etag') });
|
|
46
|
+
return doc;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function resolve(url) {
|
|
50
|
+
const pending = inFlight.get(url);
|
|
51
|
+
if (pending)
|
|
52
|
+
return pending;
|
|
53
|
+
const request = fetchDocument(url).finally(() => inFlight.delete(url));
|
|
54
|
+
inFlight.set(url, request);
|
|
55
|
+
return request;
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
resolve,
|
|
59
|
+
resolveAndRegister(url) {
|
|
60
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
61
|
+
var _a;
|
|
62
|
+
const doc = yield resolve(url);
|
|
63
|
+
// Building at `current` materializes and registers every runnable step;
|
|
64
|
+
// the schema itself is only kept long enough to lift it to a list.
|
|
65
|
+
const schema = versionedFromContract(doc, { codeSteps: options.codeSteps });
|
|
66
|
+
const listName = (_a = options.lists) === null || _a === void 0 ? void 0 : _a[doc.name];
|
|
67
|
+
if (listName !== undefined) {
|
|
68
|
+
registerSchema(versionedList(schema, listName));
|
|
69
|
+
}
|
|
70
|
+
return doc;
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
readResolving(schema, raw, contractUrl, readOptions) {
|
|
74
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
75
|
+
const first = schema.read(raw, readOptions);
|
|
76
|
+
if (first.ok || first.reason !== 'ahead')
|
|
77
|
+
return first;
|
|
78
|
+
try {
|
|
79
|
+
yield this.resolveAndRegister(contractUrl);
|
|
80
|
+
}
|
|
81
|
+
catch (_a) {
|
|
82
|
+
// The refusal stands, with its original, accurate message — the cure
|
|
83
|
+
// failed, the diagnosis didn't.
|
|
84
|
+
return first;
|
|
85
|
+
}
|
|
86
|
+
return schema.read(raw, readOptions);
|
|
87
|
+
});
|
|
88
|
+
},
|
|
89
|
+
invalidate(url) {
|
|
90
|
+
if (url === undefined)
|
|
91
|
+
cache.clear();
|
|
92
|
+
else
|
|
93
|
+
cache.delete(url);
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../../../libs/contract/src/lib/resolver.ts"],"names":[],"mappings":";AAAA,OAAO,EAIL,cAAc,EACd,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAEL,mBAAmB,EACnB,qBAAqB,GACtB,MAAM,eAAe,CAAC;AACvB,OAAO,EAAoB,qBAAqB,EAAE,MAAM,2BAA2B,CAAC;AAuEpF,MAAM,UAAU,sBAAsB,CAAC,UAAmC,EAAE;;IAC1E,MAAM,KAAK,GAAG,IAAI,GAAG,EAAsB,CAAC;IAC5C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAyC,CAAC;IAElE,0EAA0E;IAC1E,wDAAwD;IACxD,MAAM,OAAO,GAAiB,MAAA,OAAO,CAAC,SAAS,mCAAI,CAAC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;IAEzF,SAAe,aAAa,CAAC,GAAW;;;YACtC,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;YAC9B,MAAM,OAAO,GAA2B,EAAE,MAAM,EAAE,kBAAkB,EAAE,CAAC;YACvE,IAAI,MAAM,aAAN,MAAM,uBAAN,MAAM,CAAE,IAAI;gBAAE,OAAO,CAAC,eAAe,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC;YAEzD,IAAI,QAAkB,CAAC;YACvB,IAAI,CAAC;gBACH,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,MAAM;oBAAE,OAAO,MAAM,CAAC,GAAG,CAAC;gBAC9B,MAAM,WAAW,GAAG,IAAI,KAAK,CAAC,kCAAkC,GAAG,EAAE,CAAC,CAAC;gBACtE,WAAmC,CAAC,KAAK,GAAG,KAAK,CAAC;gBACnD,MAAM,WAAW,CAAC;YACpB,CAAC;YAED,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,IAAI,MAAM;gBAAE,OAAO,MAAM,CAAC,GAAG,CAAC;YACzD,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,IAAI,MAAM;oBAAE,OAAO,MAAM,CAAC,GAAG,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,kBAAkB,GAAG,kBAAkB,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;YAC5E,CAAC;YAED,MAAM,GAAG,GAAG,qBAAqB,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;YAEzD,MAAM,MAAM,GAAG,MAAA,OAAO,CAAC,kBAAkB,0CAAG,GAAG,CAAC,CAAC;YACjD,IAAI,MAAM,KAAK,SAAS,EAAE,CAAC;gBACzB,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;gBACxC,IAAI,MAAM,KAAK,MAAM,EAAE,CAAC;oBACtB,MAAM,IAAI,KAAK,CACb,kBAAkB,GAAG,4BAA4B,MAAM,yBAAyB,MAAM,0BAA0B,CACjH,CAAC;gBACJ,CAAC;YACH,CAAC;YAED,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;YAC5D,OAAO,GAAG,CAAC;QACb,CAAC;KAAA;IAED,SAAS,OAAO,CAAC,GAAW;QAC1B,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAClC,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;QAE5B,MAAM,OAAO,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;QACvE,QAAQ,CAAC,GAAG,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QAC3B,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,OAAO;QACL,OAAO;QAED,kBAAkB,CAAC,GAAW;;;gBAClC,MAAM,GAAG,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC/B,wEAAwE;gBACxE,mEAAmE;gBACnE,MAAM,MAAM,GAAG,qBAAqB,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;gBAC5E,MAAM,QAAQ,GAAG,MAAA,OAAO,CAAC,KAAK,0CAAG,GAAG,CAAC,IAAI,CAAC,CAAC;gBAC3C,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;oBAC3B,cAAc,CAAC,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;gBAClD,CAAC;gBACD,OAAO,GAAG,CAAC;YACb,CAAC;SAAA;QAEK,aAAa,CACjB,MAA0B,EAC1B,GAAY,EACZ,WAAmB,EACnB,WAAyB;;gBAEzB,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;gBAC5C,IAAI,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,MAAM,KAAK,OAAO;oBAAE,OAAO,KAAK,CAAC;gBAEvD,IAAI,CAAC;oBACH,MAAM,IAAI,CAAC,kBAAkB,CAAC,WAAW,CAAC,CAAC;gBAC7C,CAAC;gBAAC,WAAM,CAAC;oBACP,qEAAqE;oBACrE,gCAAgC;oBAChC,OAAO,KAAK,CAAC;gBACf,CAAC;gBACD,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;YACvC,CAAC;SAAA;QAED,UAAU,CAAC,GAAY;YACrB,IAAI,GAAG,KAAK,SAAS;gBAAE,KAAK,CAAC,KAAK,EAAE,CAAC;;gBAChC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACzB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { MigrationContext, VersionedSchema } from '@braidlabs/skew';
|
|
2
|
+
import { SkewContractDocument } from './document.js';
|
|
3
|
+
/**
|
|
4
|
+
* A named implementation for a contract step declared as `code` — the escape
|
|
5
|
+
* hatch for semantic migrations the op set cannot express. Shipped inside the
|
|
6
|
+
* consuming bundle, referenced by name from the document.
|
|
7
|
+
*/
|
|
8
|
+
export interface ContractCodeStep<TFrom = unknown, TTo = unknown> {
|
|
9
|
+
readonly up: (previous: TFrom, ctx: MigrationContext) => TTo;
|
|
10
|
+
readonly down?: (next: TTo, ctx: MigrationContext) => TFrom;
|
|
11
|
+
readonly derives?: readonly string[];
|
|
12
|
+
readonly downDerives?: readonly string[];
|
|
13
|
+
readonly lossy?: readonly string[];
|
|
14
|
+
}
|
|
15
|
+
export interface ContractSchemaOptions<TCurrent> {
|
|
16
|
+
/**
|
|
17
|
+
* Version this build is pinned to. Defaults to the document's `current`.
|
|
18
|
+
* A build pinned below `current` still learns the newer steps: they are
|
|
19
|
+
* contributed to the shared registry (unless `register` is false), which is
|
|
20
|
+
* what lets `read()` downgrade data from the future.
|
|
21
|
+
*/
|
|
22
|
+
readonly at?: number;
|
|
23
|
+
/** Implementations for the document's named `code` steps. */
|
|
24
|
+
readonly codeSteps?: Readonly<Record<string, ContractCodeStep<any, any>>>;
|
|
25
|
+
/**
|
|
26
|
+
* Contribute every runnable step to the shared registry. Defaults to
|
|
27
|
+
* `true` — adopting a published contract *is* the explicit act of sharing
|
|
28
|
+
* that plain `versioned()` declarations require `registerSchema` for.
|
|
29
|
+
*/
|
|
30
|
+
readonly register?: boolean;
|
|
31
|
+
readonly validate?: (value: unknown) => value is TCurrent;
|
|
32
|
+
readonly assumeLegacyVersion?: number;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Builds a {@link VersionedSchema} from a contract document — the drop-in,
|
|
36
|
+
* data-driven replacement for a hand-maintained `versioned().next()` chain.
|
|
37
|
+
*
|
|
38
|
+
* - `ops` steps compile to up *and* (where invertible) down migrations, with
|
|
39
|
+
* derived and lossy paths computed rather than hand-annotated.
|
|
40
|
+
* - `code` steps use the implementation supplied in `codeSteps`; a missing
|
|
41
|
+
* implementation leaves a hole that reads as `gap` — loud, not guessed.
|
|
42
|
+
* - Steps beyond the pinned version feed the shared registry, so this build
|
|
43
|
+
* can downgrade newer data it could never have shipped knowledge of.
|
|
44
|
+
*/
|
|
45
|
+
export declare function versionedFromContract<TCurrent>(doc: SkewContractDocument, options?: ContractSchemaOptions<TCurrent>): VersionedSchema<TCurrent>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { compileLens, registerSteps, versionedFromSteps, } from '@braidlabs/skew';
|
|
2
|
+
/**
|
|
3
|
+
* Builds a {@link VersionedSchema} from a contract document — the drop-in,
|
|
4
|
+
* data-driven replacement for a hand-maintained `versioned().next()` chain.
|
|
5
|
+
*
|
|
6
|
+
* - `ops` steps compile to up *and* (where invertible) down migrations, with
|
|
7
|
+
* derived and lossy paths computed rather than hand-annotated.
|
|
8
|
+
* - `code` steps use the implementation supplied in `codeSteps`; a missing
|
|
9
|
+
* implementation leaves a hole that reads as `gap` — loud, not guessed.
|
|
10
|
+
* - Steps beyond the pinned version feed the shared registry, so this build
|
|
11
|
+
* can downgrade newer data it could never have shipped knowledge of.
|
|
12
|
+
*/
|
|
13
|
+
export function versionedFromContract(doc, options = {}) {
|
|
14
|
+
var _a;
|
|
15
|
+
const at = (_a = options.at) !== null && _a !== void 0 ? _a : doc.current;
|
|
16
|
+
if (!Number.isInteger(at) || at < 1 || at > doc.current) {
|
|
17
|
+
throw new TypeError(`skew contract: cannot pin "${doc.name}" at v${at} — the document covers v1 through v${doc.current}`);
|
|
18
|
+
}
|
|
19
|
+
const localSteps = [];
|
|
20
|
+
const futureSteps = [];
|
|
21
|
+
for (const contractStep of doc.steps) {
|
|
22
|
+
const step = materialize(doc.name, contractStep, options.codeSteps);
|
|
23
|
+
if (contractStep.to <= at) {
|
|
24
|
+
localSteps[contractStep.to - 2] = step;
|
|
25
|
+
}
|
|
26
|
+
else if (step) {
|
|
27
|
+
futureSteps.push(step);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
// Preserve chain length even when the last local step is a hole.
|
|
31
|
+
localSteps.length = at - 1;
|
|
32
|
+
const schema = versionedFromSteps(doc.name, localSteps, {
|
|
33
|
+
validate: options.validate,
|
|
34
|
+
assumeLegacyVersion: options.assumeLegacyVersion,
|
|
35
|
+
});
|
|
36
|
+
if (options.register !== false) {
|
|
37
|
+
registerSteps(doc.name, localSteps);
|
|
38
|
+
registerSteps(doc.name, futureSteps);
|
|
39
|
+
}
|
|
40
|
+
return schema;
|
|
41
|
+
}
|
|
42
|
+
/** Compiles one contract step, or returns `undefined` for an unrunnable one. */
|
|
43
|
+
function materialize(name, contractStep, codeSteps) {
|
|
44
|
+
var _a;
|
|
45
|
+
if (contractStep.ops) {
|
|
46
|
+
const lens = compileLens(contractStep.ops);
|
|
47
|
+
return {
|
|
48
|
+
to: contractStep.to,
|
|
49
|
+
description: contractStep.description,
|
|
50
|
+
up: lens.up,
|
|
51
|
+
down: (_a = lens.down) !== null && _a !== void 0 ? _a : undefined,
|
|
52
|
+
derives: lens.derivedUp,
|
|
53
|
+
downDerives: lens.derivedDown,
|
|
54
|
+
lossy: lens.lossyDown,
|
|
55
|
+
ops: contractStep.ops,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
const implementation = contractStep.code ? codeSteps === null || codeSteps === void 0 ? void 0 : codeSteps[contractStep.code] : undefined;
|
|
59
|
+
if (!implementation)
|
|
60
|
+
return undefined;
|
|
61
|
+
return {
|
|
62
|
+
to: contractStep.to,
|
|
63
|
+
description: contractStep.description,
|
|
64
|
+
up: implementation.up,
|
|
65
|
+
down: implementation.down,
|
|
66
|
+
derives: implementation.derives,
|
|
67
|
+
downDerives: implementation.downDerives,
|
|
68
|
+
lossy: implementation.lossy,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
//# sourceMappingURL=schema-from-contract.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-from-contract.js","sourceRoot":"","sources":["../../../../../libs/contract/src/lib/schema-from-contract.ts"],"names":[],"mappings":"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"}
|