@fabricorg/sdui-release 0.3.0 → 0.5.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/CHANGELOG.md +176 -0
- package/README.md +10 -3
- package/dist/chunk-BTWHVBZ3.js +255 -0
- package/dist/chunk-BTWHVBZ3.js.map +1 -0
- package/dist/contracts-4fL-Yh38.d.cts +399 -0
- package/dist/contracts-4fL-Yh38.d.ts +399 -0
- package/dist/contracts.cjs +286 -0
- package/dist/contracts.cjs.map +1 -0
- package/dist/contracts.d.cts +4 -0
- package/dist/contracts.d.ts +4 -0
- package/dist/contracts.js +21 -0
- package/dist/contracts.js.map +1 -0
- package/dist/index.cjs +579 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -261
- package/dist/index.d.ts +120 -261
- package/dist/index.js +331 -20
- package/dist/index.js.map +1 -1
- package/dist/node-crypto.cjs +51 -0
- package/dist/node-crypto.cjs.map +1 -0
- package/dist/node-crypto.d.cts +9 -0
- package/dist/node-crypto.d.ts +9 -0
- package/dist/node-crypto.js +26 -0
- package/dist/node-crypto.js.map +1 -0
- package/package.json +30 -6
package/dist/index.js
CHANGED
|
@@ -1,3 +1,14 @@
|
|
|
1
|
+
import {
|
|
2
|
+
RELEASE_CHANNEL_POINTER_FORMAT_VERSION,
|
|
3
|
+
SDUI_RELEASE_V3_FORMAT_VERSION,
|
|
4
|
+
activateReleaseChannelPointer,
|
|
5
|
+
assertReleaseChannelPointer,
|
|
6
|
+
assertSduiReleaseV3,
|
|
7
|
+
canonicalizeJcs,
|
|
8
|
+
verifySignedExperienceArtifact,
|
|
9
|
+
verifySignedExperienceRelease
|
|
10
|
+
} from "./chunk-BTWHVBZ3.js";
|
|
11
|
+
|
|
1
12
|
// types.ts
|
|
2
13
|
var SDUI_DOCUMENT_FORMAT_VERSION = 1;
|
|
3
14
|
var SDUI_TOKEN_SET_FORMAT_VERSION = 1;
|
|
@@ -24,8 +35,9 @@ function requireArray(value, path) {
|
|
|
24
35
|
if (!Array.isArray(value)) fail(path, "must be an array");
|
|
25
36
|
return value;
|
|
26
37
|
}
|
|
38
|
+
var CAPABILITY_REF = /^capability:\/\/([a-z][a-z0-9-]*)\/([a-z][a-z0-9._-]*(?:\/[a-z0-9._-]+)*)$/;
|
|
27
39
|
function parseCapabilityRef(ref) {
|
|
28
|
-
const match =
|
|
40
|
+
const match = CAPABILITY_REF.exec(ref);
|
|
29
41
|
if (!match) return void 0;
|
|
30
42
|
return { namespace: match[1], name: match[2] };
|
|
31
43
|
}
|
|
@@ -160,8 +172,20 @@ function assertSduiDocument(value, path = "document") {
|
|
|
160
172
|
});
|
|
161
173
|
}
|
|
162
174
|
const tokenSet = requireRecord(document.tokenSet, `${path}.tokenSet`);
|
|
163
|
-
|
|
164
|
-
|
|
175
|
+
assertTokenSetReference(tokenSet, `${path}.tokenSet`);
|
|
176
|
+
if (document.supportedTokenSets !== void 0) {
|
|
177
|
+
const seenTokenSets = /* @__PURE__ */ new Set();
|
|
178
|
+
requireArray(document.supportedTokenSets, `${path}.supportedTokenSets`).forEach((entry, index) => {
|
|
179
|
+
const supportedPath = `${path}.supportedTokenSets[${index}]`;
|
|
180
|
+
const supported = requireRecord(entry, supportedPath);
|
|
181
|
+
assertTokenSetReference(supported, supportedPath);
|
|
182
|
+
const identity = `${String(supported.name)}\0${String(supported.version)}`;
|
|
183
|
+
if (seenTokenSets.has(identity)) {
|
|
184
|
+
fail(supportedPath, `declares token set "${String(supported.name)}@${String(supported.version)}" a second time`);
|
|
185
|
+
}
|
|
186
|
+
seenTokenSets.add(identity);
|
|
187
|
+
});
|
|
188
|
+
}
|
|
165
189
|
requireArray(document.componentPacks, `${path}.componentPacks`).forEach((entry, index) => {
|
|
166
190
|
const pack = requireRecord(entry, `${path}.componentPacks[${index}]`);
|
|
167
191
|
requireString(pack.pack, `${path}.componentPacks[${index}].pack`);
|
|
@@ -241,6 +265,14 @@ function assertSduiRelease(value, path = "release") {
|
|
|
241
265
|
}
|
|
242
266
|
});
|
|
243
267
|
}
|
|
268
|
+
function assertTokenSetReference(reference, path) {
|
|
269
|
+
if (!/^[a-z][a-z0-9-]*$/.test(requireString(reference.name, `${path}.name`))) {
|
|
270
|
+
fail(`${path}.name`, "must be lowercase letters, numbers and hyphens");
|
|
271
|
+
}
|
|
272
|
+
if (!/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?$/.test(requireString(reference.version, `${path}.version`))) {
|
|
273
|
+
fail(`${path}.version`, "must be a semver version");
|
|
274
|
+
}
|
|
275
|
+
}
|
|
244
276
|
|
|
245
277
|
// validate.ts
|
|
246
278
|
import {
|
|
@@ -293,6 +325,16 @@ function releaseDigest(document, tokenSet, componentPacks, grants, assemblyDiges
|
|
|
293
325
|
variants: [...document.variants].sort((left, right) => left.id < right.id ? -1 : left.id > right.id ? 1 : 0)
|
|
294
326
|
} : {},
|
|
295
327
|
tokenSet: document.tokenSet,
|
|
328
|
+
// Which brands a document may be promoted against is part of what was
|
|
329
|
+
// authored. Sorted, and omitted when absent so a document declaring
|
|
330
|
+
// none digests exactly as it did before the field existed.
|
|
331
|
+
...document.supportedTokenSets?.length ? {
|
|
332
|
+
supportedTokenSets: [...document.supportedTokenSets].sort((left, right) => {
|
|
333
|
+
if (left.name !== right.name) return left.name < right.name ? -1 : 1;
|
|
334
|
+
if (left.version !== right.version) return left.version < right.version ? -1 : 1;
|
|
335
|
+
return 0;
|
|
336
|
+
})
|
|
337
|
+
} : {},
|
|
296
338
|
componentPacks: [...document.componentPacks].sort((a, b) => {
|
|
297
339
|
const key = (e) => `${e.namespace}/${e.pack}`;
|
|
298
340
|
return key(a) < key(b) ? -1 : key(a) > key(b) ? 1 : 0;
|
|
@@ -342,19 +384,54 @@ function buildKnownViews(contracts) {
|
|
|
342
384
|
const views = /* @__PURE__ */ new Set();
|
|
343
385
|
for (const contract of contracts) {
|
|
344
386
|
for (const view of contract.views) {
|
|
345
|
-
|
|
346
|
-
views.add(`${contract.namespace}/${name}`);
|
|
387
|
+
views.add(`${contract.namespace}/${shortViewName(view.name, contract.namespace)}`);
|
|
347
388
|
}
|
|
348
389
|
}
|
|
349
390
|
return views;
|
|
350
391
|
}
|
|
351
|
-
function
|
|
392
|
+
function shortViewName(name, namespace) {
|
|
393
|
+
return name.startsWith(`${namespace}/`) ? name.slice(namespace.length + 1) : name;
|
|
394
|
+
}
|
|
395
|
+
function reportAmbiguousGrantedViews(contracts, grantedViews, add) {
|
|
396
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
397
|
+
for (const contract of contracts) {
|
|
398
|
+
for (const view of contract.views) {
|
|
399
|
+
const reference = `capability://${contract.namespace}/${shortViewName(view.name, contract.namespace)}`;
|
|
400
|
+
if (!grantedViews.has(reference)) continue;
|
|
401
|
+
occurrences.set(reference, [...occurrences.get(reference) ?? [], `${view.name}@${view.version}`]);
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
for (const [reference, declarations] of occurrences) {
|
|
405
|
+
if (declarations.length > 1) {
|
|
406
|
+
add(
|
|
407
|
+
"ambiguous_capability_reference",
|
|
408
|
+
`grants.views.${reference}`,
|
|
409
|
+
`"${reference}" is granted but addresses ${declarations.length} views (${declarations.join(", ")}); a versionless reference cannot pick one`
|
|
410
|
+
);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
function buildKnownActionIntents(contracts, add, grantedIntents) {
|
|
352
415
|
const intents = /* @__PURE__ */ new Set();
|
|
416
|
+
const occurrences = /* @__PURE__ */ new Map();
|
|
353
417
|
for (const contract of contracts) {
|
|
354
418
|
const experience = contract.extensions?.["fabric.experience/v1"];
|
|
355
419
|
for (const intent of experience?.actionIntents ?? []) {
|
|
356
|
-
const
|
|
357
|
-
|
|
420
|
+
const shortName2 = intent.name.startsWith(`${contract.namespace}.`) ? intent.name.slice(contract.namespace.length + 1) : intent.name;
|
|
421
|
+
const reference = `${contract.namespace}/${shortName2}`;
|
|
422
|
+
if (grantedIntents === void 0 || grantedIntents.has(`capability://${reference}`)) {
|
|
423
|
+
occurrences.set(reference, [...occurrences.get(reference) ?? [], intent.name]);
|
|
424
|
+
}
|
|
425
|
+
intents.add(reference);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
for (const [reference, declarations] of occurrences) {
|
|
429
|
+
if (declarations.length > 1) {
|
|
430
|
+
add?.(
|
|
431
|
+
"ambiguous_capability_reference",
|
|
432
|
+
`contracts.actionIntents.${reference}`,
|
|
433
|
+
`"capability://${reference}" is declared ${declarations.length} times (${declarations.join(", ")}), so a route for it cannot be resolved`
|
|
434
|
+
);
|
|
358
435
|
}
|
|
359
436
|
}
|
|
360
437
|
return intents;
|
|
@@ -548,18 +625,13 @@ function validateSduiRelease(input) {
|
|
|
548
625
|
const add = (code, path, message) => {
|
|
549
626
|
findings.push({ code, path, message });
|
|
550
627
|
};
|
|
551
|
-
|
|
628
|
+
const promotableTokenSets = [input.document.tokenSet, ...input.document.supportedTokenSets ?? []];
|
|
629
|
+
const promotedAgainst = promotableTokenSets.some((candidate) => candidate.name === input.tokenSet.name && candidate.version === input.tokenSet.version);
|
|
630
|
+
if (!promotedAgainst) {
|
|
552
631
|
add(
|
|
553
632
|
"missing_token_set",
|
|
554
|
-
"document.tokenSet
|
|
555
|
-
`document
|
|
556
|
-
);
|
|
557
|
-
}
|
|
558
|
-
if (input.document.tokenSet.version !== input.tokenSet.version) {
|
|
559
|
-
add(
|
|
560
|
-
"missing_token_set",
|
|
561
|
-
"document.tokenSet.version",
|
|
562
|
-
`document references token set version "${input.document.tokenSet.version}" but the provided set is version "${input.tokenSet.version}"`
|
|
633
|
+
"document.tokenSet",
|
|
634
|
+
`document supports token sets [${promotableTokenSets.map((set) => `${set.name}@${set.version}`).join(", ")}] but the provided set is "${input.tokenSet.name}@${input.tokenSet.version}"`
|
|
563
635
|
);
|
|
564
636
|
}
|
|
565
637
|
const packsByKey = /* @__PURE__ */ new Map();
|
|
@@ -675,9 +747,21 @@ function validateSduiRelease(input) {
|
|
|
675
747
|
}
|
|
676
748
|
const coreComponents = new Set(input.coreComponents ?? []);
|
|
677
749
|
const knownViews = buildKnownViews(contracts);
|
|
678
|
-
const knownIntents = buildKnownActionIntents(contracts);
|
|
679
750
|
const grantedViews = buildGrantedReferences(input.grants.views, "grants.views");
|
|
680
751
|
const grantedIntents = buildGrantedReferences(input.grants.intents, "grants.intents");
|
|
752
|
+
const knownIntents = buildKnownActionIntents(contracts, add, grantedIntents);
|
|
753
|
+
reportAmbiguousGrantedViews(contracts, grantedViews, add);
|
|
754
|
+
const contractNamespaces = /* @__PURE__ */ new Set();
|
|
755
|
+
for (const contract of contracts) {
|
|
756
|
+
if (contractNamespaces.has(contract.namespace)) {
|
|
757
|
+
add(
|
|
758
|
+
"ambiguous_capability_reference",
|
|
759
|
+
`contracts.${contract.namespace}`,
|
|
760
|
+
`contract for "${contract.namespace}" is supplied more than once`
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
contractNamespaces.add(contract.namespace);
|
|
764
|
+
}
|
|
681
765
|
validateIntentActionDeclarations(contracts, add);
|
|
682
766
|
const fragmentGrants = /* @__PURE__ */ new Map();
|
|
683
767
|
for (const [fragmentId, declared] of Object.entries(input.grants.fragments ?? {})) {
|
|
@@ -802,6 +886,221 @@ function collectCoreComponents(fragment) {
|
|
|
802
886
|
}
|
|
803
887
|
return used;
|
|
804
888
|
}
|
|
889
|
+
async function refusalReason(run, expect) {
|
|
890
|
+
try {
|
|
891
|
+
await run();
|
|
892
|
+
return null;
|
|
893
|
+
} catch (error) {
|
|
894
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
895
|
+
return expect.test(message) ? message : null;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
function sduiChannelChecks() {
|
|
899
|
+
return [
|
|
900
|
+
{
|
|
901
|
+
id: "fabric.sdui-channel.verified-release.v1",
|
|
902
|
+
async run(subject) {
|
|
903
|
+
assertSduiReleaseIntegrity(subject.validRelease);
|
|
904
|
+
const result = subject.channel.consume(subject.validRelease);
|
|
905
|
+
if (result.release.releaseDigest !== subject.validRelease.releaseDigest) {
|
|
906
|
+
throw new Error("channel returned a release other than the one it was given");
|
|
907
|
+
}
|
|
908
|
+
return [subject.validRelease.releaseDigest];
|
|
909
|
+
}
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
id: "fabric.sdui-channel.tampered-release-denial.v1",
|
|
913
|
+
async run(subject) {
|
|
914
|
+
const tampered = {
|
|
915
|
+
...subject.validRelease,
|
|
916
|
+
document: { ...subject.validRelease.document, name: `${subject.validRelease.document.name}-tampered` }
|
|
917
|
+
};
|
|
918
|
+
const reason = await refusalReason(() => subject.channel.consume(tampered), /digest|integrity/i);
|
|
919
|
+
if (reason === null) {
|
|
920
|
+
throw new Error("channel did not refuse a release whose content no longer matches its digest, citing the digest");
|
|
921
|
+
}
|
|
922
|
+
for (const [label, mutate] of [
|
|
923
|
+
["version", (release) => ({ ...release, document: { ...release.document, version: "9.9.9" } })],
|
|
924
|
+
["grants", (release) => ({ ...release, grants: { ...release.grants, views: [...release.grants.views, "capability://smuggled/view"] } })],
|
|
925
|
+
["assembly", (release) => ({ ...release, assemblyDigest: "f".repeat(64) })]
|
|
926
|
+
]) {
|
|
927
|
+
const edited = mutate(subject.validRelease);
|
|
928
|
+
if (await refusalReason(() => subject.channel.consume(edited), /digest|integrity/i) === null) {
|
|
929
|
+
throw new Error(`channel accepted a release whose ${label} was edited after promotion`);
|
|
930
|
+
}
|
|
931
|
+
}
|
|
932
|
+
return ["tampered document refused", "edited version, grants and assembly refused"];
|
|
933
|
+
}
|
|
934
|
+
},
|
|
935
|
+
{
|
|
936
|
+
id: "fabric.sdui-channel.unpromoted-document-denial.v1",
|
|
937
|
+
async run(subject) {
|
|
938
|
+
const expected = /digest|integrity|release|formatVersion|must be/i;
|
|
939
|
+
const unpromoted = { document: subject.validRelease.document };
|
|
940
|
+
if (await refusalReason(() => subject.channel.consume(unpromoted), expected) === null) {
|
|
941
|
+
throw new Error("channel did not refuse a bare document that was never promoted, citing what was wrong with it");
|
|
942
|
+
}
|
|
943
|
+
const digestless = { ...subject.validRelease, releaseDigest: void 0 };
|
|
944
|
+
if (await refusalReason(() => subject.channel.consume(digestless), expected) === null) {
|
|
945
|
+
throw new Error("channel did not refuse a release carrying no digest, citing what was wrong with it");
|
|
946
|
+
}
|
|
947
|
+
return ["bare document refused", "digestless release refused"];
|
|
948
|
+
}
|
|
949
|
+
},
|
|
950
|
+
{
|
|
951
|
+
id: "fabric.sdui-channel.reports-missing-vocabulary.v1",
|
|
952
|
+
async run(subject) {
|
|
953
|
+
const used = collectCoreComponents(subject.validRelease.document.root);
|
|
954
|
+
for (const variant of subject.validRelease.document.variants ?? []) {
|
|
955
|
+
for (const component of collectCoreComponents(variant.root)) used.add(component);
|
|
956
|
+
}
|
|
957
|
+
if (!used.has(subject.unimplementedCoreComponent)) {
|
|
958
|
+
throw new Error(
|
|
959
|
+
`conformance subject is vacuous: validRelease never uses "${subject.unimplementedCoreComponent}", so the channel is never asked to report it`
|
|
960
|
+
);
|
|
961
|
+
}
|
|
962
|
+
if (subject.channel.coreComponents.includes(subject.unimplementedCoreComponent)) {
|
|
963
|
+
throw new Error(
|
|
964
|
+
`conformance subject is contradictory: the channel implements "${subject.unimplementedCoreComponent}"`
|
|
965
|
+
);
|
|
966
|
+
}
|
|
967
|
+
const result = subject.channel.consume(subject.validRelease);
|
|
968
|
+
if (result.compatible) {
|
|
969
|
+
throw new Error("channel reported compatible for a release using a component it does not implement");
|
|
970
|
+
}
|
|
971
|
+
if (!result.missingCoreComponents.includes(subject.unimplementedCoreComponent)) {
|
|
972
|
+
throw new Error("channel reported incompatible without naming the component it cannot render");
|
|
973
|
+
}
|
|
974
|
+
return [`missing: ${result.missingCoreComponents.join(", ")}`];
|
|
975
|
+
}
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
id: "fabric.sdui-channel.consumption-is-pure.v1",
|
|
979
|
+
async run(subject) {
|
|
980
|
+
const before = JSON.stringify(subject.validRelease);
|
|
981
|
+
subject.channel.consume(subject.validRelease);
|
|
982
|
+
subject.channel.consume(subject.validRelease);
|
|
983
|
+
if (JSON.stringify(subject.validRelease) !== before) {
|
|
984
|
+
throw new Error("channel mutated the release it consumed");
|
|
985
|
+
}
|
|
986
|
+
return ["release unchanged after two consumptions"];
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
];
|
|
990
|
+
}
|
|
991
|
+
async function runSduiChannelChecks(subject) {
|
|
992
|
+
const checks = [];
|
|
993
|
+
for (const check of sduiChannelChecks()) {
|
|
994
|
+
try {
|
|
995
|
+
checks.push({ id: check.id, status: "passed", evidence: await check.run(subject) });
|
|
996
|
+
} catch (error) {
|
|
997
|
+
checks.push({
|
|
998
|
+
id: check.id,
|
|
999
|
+
status: "failed",
|
|
1000
|
+
evidence: [],
|
|
1001
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
return { passed: checks.every((check) => check.status === "passed"), checks };
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
// references.ts
|
|
1009
|
+
function collectDocumentReferences(document) {
|
|
1010
|
+
const views = /* @__PURE__ */ new Set();
|
|
1011
|
+
const intents = /* @__PURE__ */ new Set();
|
|
1012
|
+
const trees = [document.root, ...(document.variants ?? []).map((variant) => variant.root)];
|
|
1013
|
+
for (const tree of trees) walk(tree, views, intents);
|
|
1014
|
+
return { views, intents };
|
|
1015
|
+
}
|
|
1016
|
+
function walk(fragment, views, intents) {
|
|
1017
|
+
if (fragment.dataRef) views.add(fragment.dataRef);
|
|
1018
|
+
for (const action of Object.values(fragment.actions ?? {})) {
|
|
1019
|
+
intents.add(action.intent);
|
|
1020
|
+
if (action.params) collectValues(action.params, views);
|
|
1021
|
+
}
|
|
1022
|
+
if (fragment.props) collectValues(fragment.props, views);
|
|
1023
|
+
for (const child of fragment.children ?? []) walk(child, views, intents);
|
|
1024
|
+
}
|
|
1025
|
+
function collectValues(props, views) {
|
|
1026
|
+
for (const value of Object.values(props)) collectValue(value, views);
|
|
1027
|
+
}
|
|
1028
|
+
function collectValue(value, views) {
|
|
1029
|
+
if (value !== null && typeof value === "object" && !Array.isArray(value) && "$ref" in value) {
|
|
1030
|
+
views.add(value.$ref);
|
|
1031
|
+
for (const [key, sibling] of Object.entries(value)) {
|
|
1032
|
+
if (key !== "$ref") collectValue(sibling, views);
|
|
1033
|
+
}
|
|
1034
|
+
return;
|
|
1035
|
+
}
|
|
1036
|
+
if (Array.isArray(value)) {
|
|
1037
|
+
for (const item of value) collectValue(item, views);
|
|
1038
|
+
return;
|
|
1039
|
+
}
|
|
1040
|
+
if (value !== null && typeof value === "object") collectValues(value, views);
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
// entitlements.ts
|
|
1044
|
+
function deriveAuthorizationGrants(contracts, held, options = {}) {
|
|
1045
|
+
const undeclared = options.undeclaredRequirements ?? "withhold";
|
|
1046
|
+
const bound = options.document ? collectDocumentReferences(options.document) : void 0;
|
|
1047
|
+
const permissions = new Set(held.permissions ?? []);
|
|
1048
|
+
const entitlements = new Set(held.entitlements ?? []);
|
|
1049
|
+
const featureFlags = new Set(held.featureFlags ?? []);
|
|
1050
|
+
const views = [];
|
|
1051
|
+
const intents = [];
|
|
1052
|
+
const withheld = [];
|
|
1053
|
+
for (const contract of contracts) {
|
|
1054
|
+
if (undeclared === "withhold" && !declaresAnyRequirement(contract)) {
|
|
1055
|
+
withheld.push({ namespace: contract.namespace, missing: { kind: "permission", name: "(none declared)" } });
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
const missing = firstMissingRequirement(contract, { permissions, entitlements, featureFlags });
|
|
1059
|
+
if (missing) {
|
|
1060
|
+
withheld.push({ namespace: contract.namespace, missing });
|
|
1061
|
+
continue;
|
|
1062
|
+
}
|
|
1063
|
+
for (const view of contract.views) {
|
|
1064
|
+
const reference = `capability://${contract.namespace}/${shortName(view.name, contract.namespace, "/")}`;
|
|
1065
|
+
if (!bound || bound.views.has(reference)) views.push(reference);
|
|
1066
|
+
}
|
|
1067
|
+
for (const intent of actionIntentsOf(contract)) {
|
|
1068
|
+
const reference = `capability://${contract.namespace}/${shortName(intent.name, contract.namespace, ".")}`;
|
|
1069
|
+
if (!bound || bound.intents.has(reference)) intents.push(reference);
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
return {
|
|
1073
|
+
grants: { views: [...new Set(views)].sort(), intents: [...new Set(intents)].sort() },
|
|
1074
|
+
withheld: withheld.sort((left, right) => left.namespace < right.namespace ? -1 : left.namespace > right.namespace ? 1 : 0)
|
|
1075
|
+
};
|
|
1076
|
+
}
|
|
1077
|
+
function declaresAnyRequirement(contract) {
|
|
1078
|
+
const requirements = contract.requirements;
|
|
1079
|
+
return Boolean(
|
|
1080
|
+
requirements && ((requirements.permissions?.length ?? 0) > 0 || (requirements.entitlements?.length ?? 0) > 0 || (requirements.featureFlags?.length ?? 0) > 0)
|
|
1081
|
+
);
|
|
1082
|
+
}
|
|
1083
|
+
function firstMissingRequirement(contract, held) {
|
|
1084
|
+
const requirements = contract.requirements;
|
|
1085
|
+
for (const name of requirements?.permissions ?? []) {
|
|
1086
|
+
if (!held.permissions.has(name)) return { kind: "permission", name };
|
|
1087
|
+
}
|
|
1088
|
+
for (const name of requirements?.entitlements ?? []) {
|
|
1089
|
+
if (!held.entitlements.has(name)) return { kind: "entitlement", name };
|
|
1090
|
+
}
|
|
1091
|
+
for (const name of requirements?.featureFlags ?? []) {
|
|
1092
|
+
if (!held.featureFlags.has(name)) return { kind: "featureFlag", name };
|
|
1093
|
+
}
|
|
1094
|
+
return void 0;
|
|
1095
|
+
}
|
|
1096
|
+
function actionIntentsOf(contract) {
|
|
1097
|
+
const experience = contract.extensions?.["fabric.experience/v1"];
|
|
1098
|
+
return experience?.actionIntents ?? [];
|
|
1099
|
+
}
|
|
1100
|
+
function shortName(name, namespace, separator) {
|
|
1101
|
+
const prefix = `${namespace}${separator}`;
|
|
1102
|
+
return name.startsWith(prefix) ? name.slice(prefix.length) : name;
|
|
1103
|
+
}
|
|
805
1104
|
|
|
806
1105
|
// schemas.ts
|
|
807
1106
|
var SDUI_DOCUMENT_JSON_SCHEMA = {
|
|
@@ -834,22 +1133,34 @@ var SDUI_RELEASE_JSON_SCHEMA = {
|
|
|
834
1133
|
}
|
|
835
1134
|
};
|
|
836
1135
|
export {
|
|
1136
|
+
RELEASE_CHANNEL_POINTER_FORMAT_VERSION,
|
|
837
1137
|
SDUI_COMPONENT_PACK_FORMAT_VERSION,
|
|
838
1138
|
SDUI_DOCUMENT_FORMAT_VERSION,
|
|
839
1139
|
SDUI_DOCUMENT_JSON_SCHEMA,
|
|
840
1140
|
SDUI_RELEASE_FORMAT_VERSION,
|
|
841
1141
|
SDUI_RELEASE_JSON_SCHEMA,
|
|
1142
|
+
SDUI_RELEASE_V3_FORMAT_VERSION,
|
|
842
1143
|
SDUI_TOKEN_SET_FORMAT_VERSION,
|
|
1144
|
+
activateReleaseChannelPointer,
|
|
1145
|
+
assertReleaseChannelPointer,
|
|
843
1146
|
assertSduiAuthorizationGrants,
|
|
844
1147
|
assertSduiComponentPack,
|
|
845
1148
|
assertSduiDocument,
|
|
846
1149
|
assertSduiFragment,
|
|
847
1150
|
assertSduiRelease,
|
|
848
1151
|
assertSduiReleaseIntegrity,
|
|
1152
|
+
assertSduiReleaseV3,
|
|
849
1153
|
assertSduiReleaseValid,
|
|
850
1154
|
assertSduiTokenSet,
|
|
1155
|
+
canonicalizeJcs,
|
|
851
1156
|
computeReleaseDigest,
|
|
852
1157
|
createSduiChannel,
|
|
853
|
-
|
|
1158
|
+
deriveAuthorizationGrants,
|
|
1159
|
+
parseCapabilityRef,
|
|
1160
|
+
runSduiChannelChecks,
|
|
1161
|
+
sduiChannelChecks,
|
|
1162
|
+
validateSduiRelease,
|
|
1163
|
+
verifySignedExperienceArtifact,
|
|
1164
|
+
verifySignedExperienceRelease
|
|
854
1165
|
};
|
|
855
1166
|
//# sourceMappingURL=index.js.map
|