@braidlabs/contract 0.2.0 → 0.4.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@braidlabs/contract",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
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
5
|
"keywords": [
|
|
6
6
|
"version-skew",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"./package.json": "./package.json"
|
|
23
23
|
},
|
|
24
24
|
"peerDependencies": {
|
|
25
|
-
"@braidlabs/skew": "^0.
|
|
25
|
+
"@braidlabs/skew": "^0.4.0"
|
|
26
26
|
},
|
|
27
27
|
"publishConfig": {
|
|
28
28
|
"access": "public"
|
package/src/lib/resolver.js
CHANGED
|
@@ -1,50 +1,45 @@
|
|
|
1
|
-
import { __awaiter } from "tslib";
|
|
2
1
|
import { registerSchema, versionedList, } from '@braidlabs/skew';
|
|
3
2
|
import { contractFingerprint, parseContractDocument, } from './document.js';
|
|
4
3
|
import { versionedFromContract } from './schema-from-contract.js';
|
|
5
4
|
export function createContractResolver(options = {}) {
|
|
6
|
-
var _a;
|
|
7
5
|
const cache = new Map();
|
|
8
6
|
const inFlight = new Map();
|
|
9
7
|
// Wrapped rather than referenced: a bare `fetch` alias loses its `window`
|
|
10
8
|
// receiver in browsers and throws "Illegal invocation".
|
|
11
|
-
const doFetch =
|
|
12
|
-
function fetchDocument(url) {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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)
|
|
9
|
+
const doFetch = options.fetchImpl ?? ((input, init) => fetch(input, init));
|
|
10
|
+
async function fetchDocument(url) {
|
|
11
|
+
const cached = cache.get(url);
|
|
12
|
+
const headers = { accept: 'application/json' };
|
|
13
|
+
if (cached?.etag)
|
|
14
|
+
headers['if-none-match'] = cached.etag;
|
|
15
|
+
let response;
|
|
16
|
+
try {
|
|
17
|
+
response = await doFetch(url, { headers });
|
|
18
|
+
}
|
|
19
|
+
catch (cause) {
|
|
20
|
+
if (cached)
|
|
31
21
|
return cached.doc;
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
22
|
+
const unreachable = new Error(`skew contract: could not reach ${url}`);
|
|
23
|
+
unreachable.cause = cause;
|
|
24
|
+
throw unreachable;
|
|
25
|
+
}
|
|
26
|
+
if (response.status === 304 && cached)
|
|
27
|
+
return cached.doc;
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
if (cached)
|
|
30
|
+
return cached.doc;
|
|
31
|
+
throw new Error(`skew contract: ${url} answered HTTP ${response.status}`);
|
|
32
|
+
}
|
|
33
|
+
const doc = parseContractDocument(await response.json());
|
|
34
|
+
const pinned = options.pinnedFingerprints?.[url];
|
|
35
|
+
if (pinned !== undefined) {
|
|
36
|
+
const actual = contractFingerprint(doc);
|
|
37
|
+
if (actual !== pinned) {
|
|
38
|
+
throw new Error(`skew contract: ${url} resolved to fingerprint ${actual}, but this build pins ${pinned} — refusing the document`);
|
|
44
39
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
40
|
+
}
|
|
41
|
+
cache.set(url, { doc, etag: response.headers.get('etag') });
|
|
42
|
+
return doc;
|
|
48
43
|
}
|
|
49
44
|
function resolve(url) {
|
|
50
45
|
const pending = inFlight.get(url);
|
|
@@ -56,35 +51,30 @@ export function createContractResolver(options = {}) {
|
|
|
56
51
|
}
|
|
57
52
|
return {
|
|
58
53
|
resolve,
|
|
59
|
-
resolveAndRegister(url) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
}
|
|
70
|
-
return doc;
|
|
71
|
-
});
|
|
54
|
+
async resolveAndRegister(url) {
|
|
55
|
+
const doc = await resolve(url);
|
|
56
|
+
// Building at `current` materializes and registers every runnable step;
|
|
57
|
+
// the schema itself is only kept long enough to lift it to a list.
|
|
58
|
+
const schema = versionedFromContract(doc, { codeSteps: options.codeSteps });
|
|
59
|
+
const listName = options.lists?.[doc.name];
|
|
60
|
+
if (listName !== undefined) {
|
|
61
|
+
registerSchema(versionedList(schema, listName));
|
|
62
|
+
}
|
|
63
|
+
return doc;
|
|
72
64
|
},
|
|
73
|
-
readResolving(schema, raw, contractUrl, readOptions) {
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
return schema.read(raw, readOptions);
|
|
87
|
-
});
|
|
65
|
+
async readResolving(schema, raw, contractUrl, readOptions) {
|
|
66
|
+
const first = schema.read(raw, readOptions);
|
|
67
|
+
if (first.ok || first.reason !== 'ahead')
|
|
68
|
+
return first;
|
|
69
|
+
try {
|
|
70
|
+
await this.resolveAndRegister(contractUrl);
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// The refusal stands, with its original, accurate message — the cure
|
|
74
|
+
// failed, the diagnosis didn't.
|
|
75
|
+
return first;
|
|
76
|
+
}
|
|
77
|
+
return schema.read(raw, readOptions);
|
|
88
78
|
},
|
|
89
79
|
invalidate(url) {
|
|
90
80
|
if (url === undefined)
|
package/src/lib/resolver.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../../../libs/contract/src/lib/resolver.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../../../../libs/contract/src/lib/resolver.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,8 +11,7 @@ import { compileLens, registerSteps, versionedFromSteps, } from '@braidlabs/skew
|
|
|
11
11
|
* can downgrade newer data it could never have shipped knowledge of.
|
|
12
12
|
*/
|
|
13
13
|
export function versionedFromContract(doc, options = {}) {
|
|
14
|
-
|
|
15
|
-
const at = (_a = options.at) !== null && _a !== void 0 ? _a : doc.current;
|
|
14
|
+
const at = options.at ?? doc.current;
|
|
16
15
|
if (!Number.isInteger(at) || at < 1 || at > doc.current) {
|
|
17
16
|
throw new TypeError(`skew contract: cannot pin "${doc.name}" at v${at} — the document covers v1 through v${doc.current}`);
|
|
18
17
|
}
|
|
@@ -41,21 +40,20 @@ export function versionedFromContract(doc, options = {}) {
|
|
|
41
40
|
}
|
|
42
41
|
/** Compiles one contract step, or returns `undefined` for an unrunnable one. */
|
|
43
42
|
function materialize(name, contractStep, codeSteps) {
|
|
44
|
-
var _a;
|
|
45
43
|
if (contractStep.ops) {
|
|
46
44
|
const lens = compileLens(contractStep.ops);
|
|
47
45
|
return {
|
|
48
46
|
to: contractStep.to,
|
|
49
47
|
description: contractStep.description,
|
|
50
48
|
up: lens.up,
|
|
51
|
-
down:
|
|
49
|
+
down: lens.down ?? undefined,
|
|
52
50
|
derives: lens.derivedUp,
|
|
53
51
|
downDerives: lens.derivedDown,
|
|
54
52
|
lossy: lens.lossyDown,
|
|
55
53
|
ops: contractStep.ops,
|
|
56
54
|
};
|
|
57
55
|
}
|
|
58
|
-
const implementation = contractStep.code ? codeSteps
|
|
56
|
+
const implementation = contractStep.code ? codeSteps?.[contractStep.code] : undefined;
|
|
59
57
|
if (!implementation)
|
|
60
58
|
return undefined;
|
|
61
59
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"schema-from-contract.js","sourceRoot":"","sources":["../../../../../libs/contract/src/lib/schema-from-contract.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,WAAW,EACX,aAAa,EACb,kBAAkB,GACnB,MAAM,iBAAiB,CAAC;AAoCzB;;;;;;;;;;GAUG;AACH,MAAM,UAAU,qBAAqB,CACnC,GAAyB,EACzB,UAA2C,EAAE
|
|
1
|
+
{"version":3,"file":"schema-from-contract.js","sourceRoot":"","sources":["../../../../../libs/contract/src/lib/schema-from-contract.ts"],"names":[],"mappings":"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"}
|