@async/api-contract 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.
Files changed (64) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +150 -0
  3. package/dist/catalog.d.ts +4 -0
  4. package/dist/catalog.d.ts.map +1 -0
  5. package/dist/catalog.js +62 -0
  6. package/dist/catalog.js.map +1 -0
  7. package/dist/cli.d.ts +3 -0
  8. package/dist/cli.d.ts.map +1 -0
  9. package/dist/cli.js +106 -0
  10. package/dist/cli.js.map +1 -0
  11. package/dist/compare.d.ts +4 -0
  12. package/dist/compare.d.ts.map +1 -0
  13. package/dist/compare.js +101 -0
  14. package/dist/compare.js.map +1 -0
  15. package/dist/derive.d.ts +3 -0
  16. package/dist/derive.d.ts.map +1 -0
  17. package/dist/derive.js +61 -0
  18. package/dist/derive.js.map +1 -0
  19. package/dist/diff.d.ts +3 -0
  20. package/dist/diff.d.ts.map +1 -0
  21. package/dist/diff.js +38 -0
  22. package/dist/diff.js.map +1 -0
  23. package/dist/hash.d.ts +8 -0
  24. package/dist/hash.d.ts.map +1 -0
  25. package/dist/hash.js +15 -0
  26. package/dist/hash.js.map +1 -0
  27. package/dist/impact.d.ts +7 -0
  28. package/dist/impact.d.ts.map +1 -0
  29. package/dist/impact.js +25 -0
  30. package/dist/impact.js.map +1 -0
  31. package/dist/index.d.ts +12 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +12 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/ledger.d.ts +3 -0
  36. package/dist/ledger.d.ts.map +1 -0
  37. package/dist/ledger.js +78 -0
  38. package/dist/ledger.js.map +1 -0
  39. package/dist/manifest.d.ts +3 -0
  40. package/dist/manifest.d.ts.map +1 -0
  41. package/dist/manifest.js +62 -0
  42. package/dist/manifest.js.map +1 -0
  43. package/dist/model.d.ts +125 -0
  44. package/dist/model.d.ts.map +1 -0
  45. package/dist/model.js +2 -0
  46. package/dist/model.js.map +1 -0
  47. package/dist/surface.d.ts +8 -0
  48. package/dist/surface.d.ts.map +1 -0
  49. package/dist/surface.js +26 -0
  50. package/dist/surface.js.map +1 -0
  51. package/dist/types.d.ts +20 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +2 -0
  54. package/dist/types.js.map +1 -0
  55. package/dist/usage-scan.d.ts +16 -0
  56. package/dist/usage-scan.d.ts.map +1 -0
  57. package/dist/usage-scan.js +66 -0
  58. package/dist/usage-scan.js.map +1 -0
  59. package/examples/claims/claim.json +13 -0
  60. package/examples/db-ledger/API_SURFACE.md +26 -0
  61. package/examples/db-ledger/api-contract.json +42 -0
  62. package/examples/pipeline/API_SURFACE.md +37 -0
  63. package/examples/pipeline/api-contract.json +53 -0
  64. package/package.json +58 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial development version.
package/README.md ADDED
@@ -0,0 +1,150 @@
1
+ # @async/api-contract
2
+
3
+ `@async/api-contract` makes API compatibility explicit at the feature level. Instead of asking whether two package versions look compatible, a provider publishes the features it supports, a consumer records the features it requires, and CI checks whether the required surface is still a subset of the supported surface.
4
+
5
+ **30-word value prop:** `@async/api-contract` turns API compatibility into feature-level contracts: publish what packages support, derive what consumers require, and catch breaking changes before versions, docs, or tests drift across Async workspaces during releases.
6
+
7
+ Compatibility is feature-based, not package-version-based:
8
+
9
+ ```txt
10
+ required.features subset_of supported.features
11
+ ```
12
+
13
+ Package versions, release tags, docs text, and metadata do not decide compatibility.
14
+
15
+ ## What it produces
16
+
17
+ - `api-contract.json`: the machine-readable contract a package publishes, including catalogs, supported surfaces, required surfaces, emitted surfaces, or usage evidence.
18
+ - `Surface`: normalized runtime data with sorted unique feature ids and a stable hash.
19
+ - `API_SURFACE.md`: a deterministic review ledger for maintainers and docs.
20
+ - Impact reports: a quick way to see which consumers use removed, changed, or deprecated features before running expensive many-repo checks.
21
+
22
+ ## Quick start
23
+
24
+ ```ts
25
+ import {
26
+ compareSurface,
27
+ createSurface,
28
+ defineFeatureCatalog,
29
+ deriveSurface
30
+ } from "@async/api-contract";
31
+
32
+ const catalog = defineFeatureCatalog({
33
+ format: "api-contract.catalog.v1",
34
+ contractId: "@async/user-api.response",
35
+ features: [
36
+ { id: "user.id", title: "User id field", releaseTag: "public", stability: "stable" },
37
+ { id: "user.email", title: "User email field", releaseTag: "public", stability: "stable" }
38
+ ]
39
+ });
40
+
41
+ const userResponse = {
42
+ id: "usr_123",
43
+ email: "ada@example.com"
44
+ };
45
+
46
+ const required = deriveSurface(userResponse, {
47
+ contractId: "@async/user-api.response",
48
+ catalog,
49
+ strictCatalog: true,
50
+ rules: [
51
+ {
52
+ name: "user-response-fields",
53
+ visit(value, context) {
54
+ if (!value || typeof value !== "object" || Array.isArray(value)) return;
55
+ if ("id" in value) context.add("user.id");
56
+ if ("email" in value) context.add("user.email");
57
+ }
58
+ }
59
+ ]
60
+ });
61
+
62
+ const supported = createSurface({
63
+ contractId: "@async/user-api.response",
64
+ features: ["user.id", "user.email"]
65
+ });
66
+
67
+ const result = compareSurface(required, supported);
68
+
69
+ console.log(required.features); // ["user.email", "user.id"]
70
+ console.log(result.ok); // true
71
+ ```
72
+
73
+ Host packages own shape validation and derivation rules. For example, `@async/pipeline` should validate branded declaration nodes before deriving a surface from them.
74
+
75
+ ## Policy checks
76
+
77
+ Subset compatibility is the default. When a catalog is available, comparisons can also enforce release and lifecycle policy:
78
+
79
+ ```ts
80
+ const result = compareSurface(required, supported, {
81
+ catalog,
82
+ allowedReleaseTags: ["public"],
83
+ deprecated: "warn",
84
+ removed: "error",
85
+ unknownFeatures: "error"
86
+ });
87
+
88
+ if (!result.ok) {
89
+ console.error(result.warnings);
90
+ }
91
+ ```
92
+
93
+ ## Type-only contracts
94
+
95
+ Use `@async/api-contract/types` for app code and virtual imports that should express a contract without emitting runtime JavaScript.
96
+
97
+ ```ts
98
+ import type {
99
+ AssertCompatible,
100
+ Expect,
101
+ RequiresContract,
102
+ SupportsContract
103
+ } from "@async/api-contract/types";
104
+
105
+ type UsesPipeline = RequiresContract<"@async/pipeline.declaration">;
106
+
107
+ type UsesShellStep = RequiresContract<
108
+ "@async/pipeline.declaration",
109
+ "task.run" | "step.shell"
110
+ >;
111
+
112
+ type HostSupport = SupportsContract<
113
+ "@async/pipeline.declaration",
114
+ "task.run" | "step.shell" | "agent.stdoutTo"
115
+ >;
116
+
117
+ type Check = Expect<AssertCompatible<UsesShellStep, HostSupport>>;
118
+ ```
119
+
120
+ The default is forward-compatible rather than untyped: known fields stay precise, exact feature unions are supported, and option maps allow future `x-*` extension fields.
121
+
122
+ ## Release and stability tags
123
+
124
+ Catalog features can carry API Extractor-style maturity metadata:
125
+
126
+ - `public`
127
+ - `beta`
128
+ - `alpha`
129
+ - `internal`
130
+
131
+ Project-facing stability labels such as `stable`, `preview`, `experimental`, `generated`, `dev-only`, and `internal` are docs and policy metadata. They do not affect `surfaceHash()`.
132
+
133
+ ## CLI
134
+
135
+ ```sh
136
+ api-contract check --manifest api-contract.json
137
+ api-contract ledger --manifest api-contract.json --out API_SURFACE.md
138
+ api-contract ledger --manifest api-contract.json --check API_SURFACE.md
139
+ api-contract diff --before old-api-contract.json --after api-contract.json
140
+ api-contract impact --before old-api-contract.json --after api-contract.json --consumers consumers.json
141
+ api-contract usage scan --target src --package-name @async/consumer --dependency @async/pipeline --catalog api-contract.json --out api-usage.json
142
+ ```
143
+
144
+ `impact` is intended as a cheap preflight for explicit many-repo impact runs. Read the latest consumer manifests or usage files first; then run full dependent repo checks only for consumers that actually use changed features.
145
+
146
+ `usage scan` is a line-oriented source preflight. It records dependency and feature-string evidence, but it is not a full parser or proof of semantic usage.
147
+
148
+ ## Relationship to @async/claims
149
+
150
+ `@async/claims` can wrap surfaces with issuer, evidence, trust, policy, and signatures. It should not redefine feature ids, hashes, derivation, or compatibility rules.
@@ -0,0 +1,4 @@
1
+ import type { FeatureCatalog } from "./model.js";
2
+ export declare function defineFeatureCatalog(catalog: FeatureCatalog): FeatureCatalog;
3
+ export declare function catalogFeatureIds(catalog: FeatureCatalog): Set<string>;
4
+ //# sourceMappingURL=catalog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.d.ts","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAe,MAAM,YAAY,CAAC;AAK9D,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,cAAc,CA0B5E;AAED,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,cAAc,GAAG,GAAG,CAAC,MAAM,CAAC,CAEtE"}
@@ -0,0 +1,62 @@
1
+ const releaseTags = new Set(["public", "beta", "alpha", "internal"]);
2
+ const lifecycles = new Set(["active", "deprecated", "removed"]);
3
+ export function defineFeatureCatalog(catalog) {
4
+ assertObject(catalog, "catalog");
5
+ if (catalog.format !== "api-contract.catalog.v1") {
6
+ throw new Error("FeatureCatalog.format must be api-contract.catalog.v1");
7
+ }
8
+ assertNonEmptyString(catalog.contractId, "FeatureCatalog.contractId");
9
+ if (!Array.isArray(catalog.features)) {
10
+ throw new Error("FeatureCatalog.features must be an array");
11
+ }
12
+ const seen = new Set();
13
+ const features = catalog.features.map((feature) => normalizeFeatureSpec(feature));
14
+ for (const feature of features) {
15
+ if (seen.has(feature.id)) {
16
+ throw new Error(`Duplicate feature id: ${feature.id}`);
17
+ }
18
+ seen.add(feature.id);
19
+ }
20
+ return {
21
+ ...copyExtensionFields(catalog),
22
+ format: "api-contract.catalog.v1",
23
+ contractId: catalog.contractId,
24
+ ...(catalog.title === undefined ? {} : { title: catalog.title }),
25
+ features: features.sort((a, b) => a.id.localeCompare(b.id))
26
+ };
27
+ }
28
+ export function catalogFeatureIds(catalog) {
29
+ return new Set(catalog.features.map((feature) => feature.id));
30
+ }
31
+ function normalizeFeatureSpec(feature) {
32
+ assertObject(feature, "FeatureSpec");
33
+ assertNonEmptyString(feature.id, "FeatureSpec.id");
34
+ assertNonEmptyString(feature.title, "FeatureSpec.title");
35
+ if (!releaseTags.has(feature.releaseTag)) {
36
+ throw new Error(`FeatureSpec.releaseTag must be one of public, beta, alpha, internal for ${feature.id}`);
37
+ }
38
+ if (feature.lifecycle !== undefined && !lifecycles.has(feature.lifecycle)) {
39
+ throw new Error(`FeatureSpec.lifecycle must be active, deprecated, or removed for ${feature.id}`);
40
+ }
41
+ return { ...feature };
42
+ }
43
+ function assertObject(value, label) {
44
+ if (typeof value !== "object" || value === null || Array.isArray(value)) {
45
+ throw new Error(`${label} must be an object`);
46
+ }
47
+ }
48
+ function assertNonEmptyString(value, label) {
49
+ if (typeof value !== "string" || value.length === 0) {
50
+ throw new Error(`${label} must be a non-empty string`);
51
+ }
52
+ }
53
+ function copyExtensionFields(value) {
54
+ const out = {};
55
+ for (const [key, entry] of Object.entries(value)) {
56
+ if (key.startsWith("x-")) {
57
+ out[key] = entry;
58
+ }
59
+ }
60
+ return out;
61
+ }
62
+ //# sourceMappingURL=catalog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"catalog.js","sourceRoot":"","sources":["../src/catalog.ts"],"names":[],"mappings":"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"}
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+ import { readFileSync, writeFileSync } from "node:fs";
3
+ import { parsePackageContractManifest } from "./manifest.js";
4
+ import { renderApiSurfaceMarkdown } from "./ledger.js";
5
+ import { diffPackageContracts } from "./diff.js";
6
+ import { createImpactReport } from "./impact.js";
7
+ import { scanUsageTarget } from "./usage-scan.js";
8
+ function readJson(path) {
9
+ return JSON.parse(readFileSync(path, "utf8"));
10
+ }
11
+ function argValue(args, name) {
12
+ const index = args.indexOf(name);
13
+ return index >= 0 ? args[index + 1] : undefined;
14
+ }
15
+ function readManifest(path) {
16
+ return parsePackageContractManifest(readJson(path));
17
+ }
18
+ function writeJsonOrStdout(value, outPath) {
19
+ const text = `${JSON.stringify(value, null, 2)}
20
+ `;
21
+ if (outPath)
22
+ writeFileSync(outPath, text);
23
+ else
24
+ process.stdout.write(text);
25
+ }
26
+ function readConsumers(path) {
27
+ const value = readJson(path);
28
+ if (Array.isArray(value))
29
+ return value.map(parsePackageContractManifest);
30
+ if (typeof value === "object" && value !== null && Array.isArray(value.manifests)) {
31
+ return value.manifests.map(parsePackageContractManifest);
32
+ }
33
+ return [parsePackageContractManifest(value)];
34
+ }
35
+ const args = process.argv.slice(2);
36
+ const command = args[0];
37
+ try {
38
+ if (command === "check") {
39
+ const manifestPath = argValue(args, "--manifest");
40
+ if (!manifestPath)
41
+ throw new Error("Missing --manifest <file>");
42
+ parsePackageContractManifest(readJson(manifestPath));
43
+ console.log(`ok ${manifestPath}`);
44
+ }
45
+ else if (command === "ledger") {
46
+ const manifestPath = argValue(args, "--manifest");
47
+ if (!manifestPath)
48
+ throw new Error("Missing --manifest <file>");
49
+ const manifest = readManifest(manifestPath);
50
+ const markdown = renderApiSurfaceMarkdown({ manifest });
51
+ const checkPath = argValue(args, "--check");
52
+ if (checkPath) {
53
+ const existing = readFileSync(checkPath, "utf8");
54
+ if (existing !== markdown)
55
+ throw new Error(`${checkPath} is out of date`);
56
+ console.log(`ok ${checkPath}`);
57
+ }
58
+ else {
59
+ const outPath = argValue(args, "--out");
60
+ if (outPath)
61
+ writeFileSync(outPath, markdown);
62
+ else
63
+ process.stdout.write(markdown);
64
+ }
65
+ }
66
+ else if (command === "diff") {
67
+ const beforePath = argValue(args, "--before");
68
+ const afterPath = argValue(args, "--after");
69
+ if (!beforePath || !afterPath)
70
+ throw new Error("Missing --before <file> or --after <file>");
71
+ writeJsonOrStdout(diffPackageContracts(readManifest(beforePath), readManifest(afterPath)), argValue(args, "--out"));
72
+ }
73
+ else if (command === "impact") {
74
+ const beforePath = argValue(args, "--before");
75
+ const afterPath = argValue(args, "--after");
76
+ const consumersPath = argValue(args, "--consumers");
77
+ if (!beforePath || !afterPath || !consumersPath)
78
+ throw new Error("Missing --before <file>, --after <file>, or --consumers <file>");
79
+ writeJsonOrStdout(createImpactReport({ before: readManifest(beforePath), after: readManifest(afterPath), consumers: readConsumers(consumersPath) }), argValue(args, "--out"));
80
+ }
81
+ else if (command === "usage" && args[1] === "scan") {
82
+ const target = argValue(args, "--target");
83
+ const dependencyName = argValue(args, "--dependency");
84
+ const catalogPath = argValue(args, "--catalog");
85
+ const packageName = argValue(args, "--package-name") ?? "unknown";
86
+ if (!target || !dependencyName || !catalogPath)
87
+ throw new Error("Missing --target <path>, --dependency <name>, or --catalog <manifest>");
88
+ const manifest = readManifest(catalogPath);
89
+ const catalog = manifest.catalogs?.[0];
90
+ if (!catalog)
91
+ throw new Error(`${catalogPath} does not contain a catalog`);
92
+ writeJsonOrStdout({
93
+ format: "api-contract.package.v1",
94
+ packageName,
95
+ usage: [scanUsageTarget({ target, dependencyName, packageName, catalog })]
96
+ }, argValue(args, "--out"));
97
+ }
98
+ else {
99
+ throw new Error("Usage: api-contract check|ledger|diff|impact|usage scan");
100
+ }
101
+ }
102
+ catch (error) {
103
+ console.error(error instanceof Error ? error.message : String(error));
104
+ process.exitCode = 1;
105
+ }
106
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ import type { ComparePolicy, ContractSet, Surface, SurfaceComparison } from "./model.js";
2
+ export declare function compareSurface(required: Surface, supported: Surface, policy?: ComparePolicy): SurfaceComparison;
3
+ export declare function compareContractSets(required: ContractSet, supported: ContractSet, policy?: ComparePolicy): SurfaceComparison;
4
+ //# sourceMappingURL=compare.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,WAAW,EAA0C,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAIjI,wBAAgB,cAAc,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,GAAE,aAAkB,GAAG,iBAAiB,CAsBnH;AAED,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,WAAW,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,GAAE,aAAkB,GAAG,iBAAiB,CA4BhI"}
@@ -0,0 +1,101 @@
1
+ export function compareSurface(required, supported, policy = {}) {
2
+ if (required.contractId !== supported.contractId) {
3
+ return {
4
+ ok: false,
5
+ missing: [...required.features],
6
+ unsupported: [...supported.features],
7
+ warnings: [`Contract id mismatch: required ${required.contractId}, supported ${supported.contractId}`]
8
+ };
9
+ }
10
+ const supportedFeatures = new Set(supported.features);
11
+ const requiredFeatures = new Set(required.features);
12
+ const missing = required.features.filter((feature) => !supportedFeatures.has(feature));
13
+ const unsupported = supported.features.filter((feature) => !requiredFeatures.has(feature));
14
+ const policyResult = evaluatePolicy(required, policy);
15
+ return {
16
+ ok: missing.length === 0 && policyResult.ok,
17
+ missing,
18
+ unsupported,
19
+ warnings: policyResult.warnings
20
+ };
21
+ }
22
+ export function compareContractSets(required, supported, policy = {}) {
23
+ const supportedByContract = new Map(supported.surfaces.map((surface) => [surface.contractId, surface]));
24
+ const missing = [];
25
+ const unsupported = [];
26
+ const warnings = [];
27
+ let ok = true;
28
+ for (const requiredSurface of required.surfaces) {
29
+ const supportedSurface = supportedByContract.get(requiredSurface.contractId);
30
+ if (!supportedSurface) {
31
+ ok = false;
32
+ missing.push(...requiredSurface.features.map((feature) => `${requiredSurface.contractId}:${feature}`));
33
+ warnings.push(`Missing supported surface for ${requiredSurface.contractId}`);
34
+ continue;
35
+ }
36
+ const comparison = compareSurface(requiredSurface, supportedSurface, policy);
37
+ if (!comparison.ok)
38
+ ok = false;
39
+ missing.push(...comparison.missing);
40
+ unsupported.push(...comparison.unsupported);
41
+ warnings.push(...comparison.warnings);
42
+ }
43
+ return {
44
+ ok: ok && missing.length === 0,
45
+ missing,
46
+ unsupported,
47
+ warnings
48
+ };
49
+ }
50
+ function evaluatePolicy(required, policy) {
51
+ const features = policyFeatureMap(required.contractId, policy);
52
+ if (!features)
53
+ return { ok: true, warnings: [] };
54
+ const warnings = [];
55
+ const errors = [];
56
+ const report = (action, message) => {
57
+ if (action === "warn")
58
+ warnings.push(message);
59
+ if (action === "error")
60
+ errors.push(message);
61
+ };
62
+ for (const featureId of required.features) {
63
+ const feature = features.get(featureId);
64
+ if (!feature) {
65
+ report(policy.unknownFeatures, `Unknown feature in ${required.contractId}: ${featureId}`);
66
+ continue;
67
+ }
68
+ if (policy.allowedReleaseTags && !policy.allowedReleaseTags.includes(feature.releaseTag)) {
69
+ errors.push(`Feature ${featureId} has release tag ${feature.releaseTag}, which is not allowed by policy`);
70
+ }
71
+ if (feature.lifecycle === "deprecated") {
72
+ report(policy.deprecated, `Feature ${featureId} is deprecated`);
73
+ }
74
+ if (feature.lifecycle === "removed") {
75
+ report(policy.removed, `Feature ${featureId} is removed`);
76
+ }
77
+ }
78
+ return {
79
+ ok: errors.length === 0,
80
+ warnings: [...warnings, ...errors.map((error) => `Policy error: ${error}`)]
81
+ };
82
+ }
83
+ function policyFeatureMap(contractId, policy) {
84
+ const catalogs = policyCatalogs(policy).filter((catalog) => catalog.contractId === contractId);
85
+ if (catalogs.length === 0)
86
+ return undefined;
87
+ const features = new Map();
88
+ for (const catalog of catalogs) {
89
+ for (const feature of catalog.features) {
90
+ features.set(feature.id, feature);
91
+ }
92
+ }
93
+ return features;
94
+ }
95
+ function policyCatalogs(policy) {
96
+ return [
97
+ ...(policy.catalog ? [policy.catalog] : []),
98
+ ...(policy.catalogs ?? [])
99
+ ];
100
+ }
101
+ //# sourceMappingURL=compare.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compare.js","sourceRoot":"","sources":["../src/compare.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { DeriveSurfaceOptions, Surface } from "./model.js";
2
+ export declare function deriveSurface(value: unknown, options: DeriveSurfaceOptions): Surface;
3
+ //# sourceMappingURL=derive.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,oBAAoB,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAE/E,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,oBAAoB,GAAG,OAAO,CA+CpF"}
package/dist/derive.js ADDED
@@ -0,0 +1,61 @@
1
+ import { catalogFeatureIds } from "./catalog.js";
2
+ import { createSurface } from "./surface.js";
3
+ export function deriveSurface(value, options) {
4
+ const features = new Set();
5
+ const knownFeatures = options.catalog ? catalogFeatureIds(options.catalog) : undefined;
6
+ const visited = new WeakSet();
7
+ const addFeature = (feature) => {
8
+ if (options.strictCatalog && knownFeatures && !knownFeatures.has(feature)) {
9
+ throw new Error(`Unknown feature emitted by derivation rule: ${feature}`);
10
+ }
11
+ features.add(feature);
12
+ };
13
+ const walk = (node, path) => {
14
+ if (isObject(node)) {
15
+ if (visited.has(node))
16
+ return;
17
+ visited.add(node);
18
+ }
19
+ const context = { path, add: addFeature };
20
+ const customChildren = [];
21
+ let hasCustomChildren = false;
22
+ for (const rule of options.rules) {
23
+ rule.visit(node, context);
24
+ if (rule.children) {
25
+ hasCustomChildren = true;
26
+ for (const child of rule.children(node, context)) {
27
+ customChildren.push(child);
28
+ }
29
+ }
30
+ }
31
+ if (hasCustomChildren) {
32
+ for (const [index, child] of customChildren.entries()) {
33
+ walk(child, [...path, index]);
34
+ }
35
+ return;
36
+ }
37
+ for (const [key, child] of defaultChildren(node)) {
38
+ walk(child, [...path, key]);
39
+ }
40
+ };
41
+ walk(value, []);
42
+ return createSurface({ contractId: options.contractId, features });
43
+ }
44
+ function isObject(value) {
45
+ return typeof value === "object" && value !== null;
46
+ }
47
+ function defaultChildren(value) {
48
+ if (!isObject(value))
49
+ return [];
50
+ if (Array.isArray(value)) {
51
+ return value.map((entry, index) => [index, entry]);
52
+ }
53
+ if (value instanceof Map) {
54
+ return [...value.entries()].map(([key, entry]) => [String(key), entry]);
55
+ }
56
+ if (value instanceof Set) {
57
+ return [...value.values()].map((entry, index) => [index, entry]);
58
+ }
59
+ return Object.entries(value);
60
+ }
61
+ //# sourceMappingURL=derive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"derive.js","sourceRoot":"","sources":["../src/derive.ts"],"names":[],"mappings":"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"}
package/dist/diff.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ import type { ContractDiff, PackageContractManifest } from "./model.js";
2
+ export declare function diffPackageContracts(before: PackageContractManifest, after: PackageContractManifest): ContractDiff;
3
+ //# sourceMappingURL=diff.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAa,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAEnF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,uBAAuB,EAAE,KAAK,EAAE,uBAAuB,GAAG,YAAY,CA0BlH"}
package/dist/diff.js ADDED
@@ -0,0 +1,38 @@
1
+ export function diffPackageContracts(before, after) {
2
+ const beforeFeatures = featureMap(before);
3
+ const afterFeatures = featureMap(after);
4
+ const addedFeatures = [];
5
+ const removedFeatures = [];
6
+ const changedFeatures = [];
7
+ const deprecatedFeatures = [];
8
+ for (const id of afterFeatures.keys()) {
9
+ if (!beforeFeatures.has(id))
10
+ addedFeatures.push(id);
11
+ }
12
+ for (const id of beforeFeatures.keys()) {
13
+ if (!afterFeatures.has(id))
14
+ removedFeatures.push(id);
15
+ }
16
+ for (const [id, feature] of afterFeatures) {
17
+ const previous = beforeFeatures.get(id);
18
+ if (previous && JSON.stringify(previous) !== JSON.stringify(feature))
19
+ changedFeatures.push(id);
20
+ if (feature.lifecycle === "deprecated")
21
+ deprecatedFeatures.push(id);
22
+ }
23
+ return {
24
+ addedFeatures: addedFeatures.sort(),
25
+ removedFeatures: removedFeatures.sort(),
26
+ changedFeatures: changedFeatures.sort(),
27
+ deprecatedFeatures: deprecatedFeatures.sort()
28
+ };
29
+ }
30
+ function featureMap(manifest) {
31
+ const out = new Map();
32
+ for (const catalog of manifest.catalogs ?? []) {
33
+ for (const feature of catalog.features)
34
+ out.set(feature.id, feature);
35
+ }
36
+ return out;
37
+ }
38
+ //# sourceMappingURL=diff.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,oBAAoB,CAAC,MAA+B,EAAE,KAA8B;IAClG,MAAM,cAAc,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC;IAC1C,MAAM,aAAa,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,aAAa,GAAgB,EAAE,CAAC;IACtC,MAAM,eAAe,GAAgB,EAAE,CAAC;IACxC,MAAM,eAAe,GAAgB,EAAE,CAAC;IACxC,MAAM,kBAAkB,GAAgB,EAAE,CAAC;IAE3C,KAAK,MAAM,EAAE,IAAI,aAAa,CAAC,IAAI,EAAE,EAAE,CAAC;QACtC,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,KAAK,MAAM,EAAE,IAAI,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC;QACvC,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACvD,CAAC;IACD,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,aAAa,EAAE,CAAC;QAC1C,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QACxC,IAAI,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC;YAAE,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC/F,IAAK,OAAkC,CAAC,SAAS,KAAK,YAAY;YAAE,kBAAkB,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAClG,CAAC;IAED,OAAO;QACL,aAAa,EAAE,aAAa,CAAC,IAAI,EAAE;QACnC,eAAe,EAAE,eAAe,CAAC,IAAI,EAAE;QACvC,eAAe,EAAE,eAAe,CAAC,IAAI,EAAE;QACvC,kBAAkB,EAAE,kBAAkB,CAAC,IAAI,EAAE;KAC9C,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,QAAiC;IACnD,MAAM,GAAG,GAAG,IAAI,GAAG,EAAmB,CAAC;IACvC,KAAK,MAAM,OAAO,IAAI,QAAQ,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,OAAO,CAAC,QAAQ;YAAE,GAAG,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IACvE,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC"}
package/dist/hash.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ import type { ContractId, FeatureId, SurfaceHash } from "./model.js";
2
+ export declare function normalizeFeatureIds(features: Iterable<FeatureId>): FeatureId[];
3
+ export declare function surfaceHash(input: {
4
+ contractId: ContractId;
5
+ features: Iterable<FeatureId>;
6
+ }): SurfaceHash;
7
+ export declare function hashJson(value: unknown): SurfaceHash;
8
+ //# sourceMappingURL=hash.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAErE,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,GAAG,SAAS,EAAE,CAE9E;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE;IAAE,UAAU,EAAE,UAAU,CAAC;IAAC,QAAQ,EAAE,QAAQ,CAAC,SAAS,CAAC,CAAA;CAAE,GAAG,WAAW,CAMzG;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,WAAW,CAEpD"}
package/dist/hash.js ADDED
@@ -0,0 +1,15 @@
1
+ import { createHash } from "node:crypto";
2
+ export function normalizeFeatureIds(features) {
3
+ return [...new Set([...features])].sort((a, b) => a.localeCompare(b));
4
+ }
5
+ export function surfaceHash(input) {
6
+ const payload = JSON.stringify({
7
+ contractId: input.contractId,
8
+ features: normalizeFeatureIds(input.features)
9
+ });
10
+ return `sha256:${createHash("sha256").update(payload).digest("hex")}`;
11
+ }
12
+ export function hashJson(value) {
13
+ return `sha256:${createHash("sha256").update(JSON.stringify(value)).digest("hex")}`;
14
+ }
15
+ //# sourceMappingURL=hash.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.js","sourceRoot":"","sources":["../src/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAGzC,MAAM,UAAU,mBAAmB,CAAC,QAA6B;IAC/D,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAgE;IAC1F,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC;QAC7B,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,QAAQ,EAAE,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC;KAC9C,CAAC,CAAC;IACH,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAiB,CAAC;AACvF,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,KAAc;IACrC,OAAO,UAAU,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAiB,CAAC;AACrG,CAAC"}