eyeling 1.21.3 → 1.21.4

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/HANDBOOK.md CHANGED
@@ -1573,6 +1573,10 @@ A tiny `sprintf` subset:
1573
1573
  - Supports only `%s` and `%%`.
1574
1574
  - Any other specifier (`%d`, `%f`, …) causes the builtin to fail.
1575
1575
  - Missing arguments are treated as empty strings.
1576
+ - The format string `fmt` itself must be string-castable.
1577
+ - Each `%s` argument may be any bound non-variable term:
1578
+ - string-castable terms (IRIs and literals) use their direct string value;
1579
+ - other bound terms (blank nodes, lists, quoted formulas, …) are rendered as N3.
1576
1580
 
1577
1581
  ### Length and character utilities (Eyeling extensions)
1578
1582
 
@@ -1012,6 +1012,13 @@ function simpleStringFormat(fmt, args) {
1012
1012
  return out;
1013
1013
  }
1014
1014
 
1015
+ function termToFormatArgString(t) {
1016
+ const s = termToJsString(t);
1017
+ if (s !== null) return s;
1018
+ if (t instanceof Var) return null;
1019
+ return termToN3(t, PrefixEnv.newDefault());
1020
+ }
1021
+
1015
1022
  // -----------------------------------------------------------------------------
1016
1023
  // SWAP/N3 regex compatibility helper
1017
1024
  // -----------------------------------------------------------------------------
@@ -4182,6 +4189,10 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4182
4189
 
4183
4190
  // string:format
4184
4191
  // (limited: only %s and %% are supported, anything else ⇒ builtin fails)
4192
+ // The format string itself must be string-castable, but placeholder arguments
4193
+ // are allowed to be any bound non-variable term. Plain strings/IRIs keep their
4194
+ // direct string value; other terms fall back to N3 rendering so formatting a
4195
+ // bound blank node, list, or quoted formula does not make the whole builtin fail.
4185
4196
  if (pv === STRING_NS + 'format') {
4186
4197
  if (!(g.s instanceof ListTerm) || g.s.elems.length < 1) return [];
4187
4198
  const fmtStr = termToJsString(g.s.elems[0]);
@@ -4189,7 +4200,7 @@ function evalBuiltin(goal, subst, facts, backRules, depth, varGen, maxResults) {
4189
4200
 
4190
4201
  const args = [];
4191
4202
  for (let i = 1; i < g.s.elems.length; i++) {
4192
- const aStr = termToJsString(g.s.elems[i]);
4203
+ const aStr = termToFormatArgString(g.s.elems[i]);
4193
4204
  if (aStr === null) return [];
4194
4205
  args.push(aStr);
4195
4206
  }
@@ -0,0 +1,208 @@
1
+ # ARC Bridge
2
+
3
+ This directory holds **ARC Bridge** cases.
4
+
5
+ An ARC Bridge case sits between two existing forms in Eyeling:
6
+
7
+ - the **declarative N3** case in `examples/`, and
8
+ - the **specialized executable** case in `examples/extra/`.
9
+
10
+ Its purpose is to keep the ARC promise visible while making the case easy to read, easy to rerun, and easy to port.
11
+
12
+ In one line:
13
+
14
+ > `examples/arc-bridge/` presents controlled-English ARC specifications with reference ECMAScript realizations and JSON test vectors.
15
+
16
+ ## Why this directory exists
17
+
18
+ Eyeling already has two valuable ways to present a case.
19
+
20
+ The files in `examples/` are ideal for seeing the logic in open declarative form. The files in `examples/extra/` are ideal when a case has already been shaped and we want a compact executable artifact that runs quickly.
21
+
22
+ ARC Bridge exists for the middle layer. It gives each case:
23
+
24
+ - a **normative statement** in controlled mathematical English,
25
+ - a **reference realization** in ECMAScript,
26
+ - a **concrete instance** in JSON, and
27
+ - an **expected result** for comparison and regression testing.
28
+
29
+ So ARC Bridge is not a replacement for either of the existing collections. It is a bridge between them.
30
+
31
+ ## The ARC part
32
+
33
+ ARC means:
34
+
35
+ - **Answer**
36
+ - **Reason Why**
37
+ - **Check**
38
+
39
+ A good ARC Bridge case should preserve that trust pattern even though it is not written directly in N3.
40
+
41
+ That means:
42
+
43
+ - the **answer** is clearly identifiable,
44
+ - the **reason why** is visible as named clauses or derived predicates,
45
+ - and the **check** is real, meaning it could fail for a meaningful reason.
46
+
47
+ ## What belongs here
48
+
49
+ A case belongs in `examples/arc-bridge/` when:
50
+
51
+ - there is a useful ARC-style case in `examples/`,
52
+ - there is value in giving the case a more direct operational form,
53
+ - but we still want the logic to stay explicit and auditable,
54
+ - with a specification that can be read independently of the code.
55
+
56
+ Typical uses:
57
+
58
+ - policy and governance examples,
59
+ - privacy-preserving decision examples,
60
+ - cases with a stable logical core and a small executable shell,
61
+ - cases that benefit from conformance-style testing.
62
+
63
+ ## Directory shape
64
+
65
+ Each case should live in its own subdirectory.
66
+
67
+ For example:
68
+
69
+ ```text
70
+ examples/arc-bridge/
71
+ delfour/
72
+ delfour.spec.md
73
+ delfour.data.json
74
+ delfour.model.mjs
75
+ delfour.expected.json
76
+ delfour.instance.schema.json
77
+ flandor/
78
+ flandor.spec.md
79
+ flandor.data.json
80
+ flandor.model.mjs
81
+ flandor.expected.json
82
+ flandor.instance.schema.json
83
+ ```
84
+
85
+ ## The five files
86
+
87
+ Each ARC Bridge case should contain these files.
88
+
89
+ ### 1. `name.spec.md`
90
+
91
+ The normative case description.
92
+
93
+ This file should use **controlled mathematical English**. It should define the vocabulary, the inputs, the derived predicates, the decision rule, the governance rule, the checks, and the output contract.
94
+
95
+ The spec should be written so that a careful reader can understand the case without reading the ECMAScript source first.
96
+
97
+ ### 2. `name.data.json`
98
+
99
+ The concrete instance data.
100
+
101
+ This file contains the facts for the case: entities, thresholds, observed values, policies, timestamps, candidate actions, and any other case inputs.
102
+
103
+ ### 3. `name.model.mjs`
104
+
105
+ The reference ECMAScript realization.
106
+
107
+ This file should implement the case directly and clearly. A good pattern is to map named clauses in the spec to named functions in the model.
108
+
109
+ For example:
110
+
111
+ - `clauseR1_exportWeakness`
112
+ - `clauseS2_recommendedPackage`
113
+ - `clauseG1_authorizedUse`
114
+ - `clauseM2_payloadHash`
115
+
116
+ The model is not the normative source. It is the **reference realization** of the normative source.
117
+
118
+ ### 4. `name.expected.json`
119
+
120
+ The expected derived result.
121
+
122
+ This file is the conformance vector for the case. It should capture the main derived predicates, the selected answer, the visible checks, and any stable integrity values needed for regression testing.
123
+
124
+ ### 5. `name.instance.schema.json`
125
+
126
+ The instance schema.
127
+
128
+ This file defines the required structure of the input JSON. It should be strict enough to catch malformed case instances before evaluation.
129
+
130
+ ## How to read an ARC Bridge case
131
+
132
+ A good reading order is:
133
+
134
+ 1. start with `name.spec.md`,
135
+ 2. inspect `name.data.json`,
136
+ 3. run `name.model.mjs`,
137
+ 4. compare the result with `name.expected.json`,
138
+ 5. then relate the case back to its N3 and specialized counterparts.
139
+
140
+ That order keeps the meaning visible before the operational details.
141
+
142
+ ## Relationship to the rest of `examples/`
143
+
144
+ A useful mental model is:
145
+
146
+ - `examples/` shows ARC cases in **declarative Eyeling form**,
147
+ - `examples/arc-bridge/` shows the same kind of cases in **specification-plus-reference form**,
148
+ - `examples/extra/` shows selected cases in **specialized executable form**.
149
+
150
+ So the three collections are complementary:
151
+
152
+ - **N3** is best for seeing the logic in the open,
153
+ - **ARC Bridge** is best for stating the case normatively and running a portable reference model,
154
+ - **extra** is best for fast specialized execution.
155
+
156
+ ## Design rules
157
+
158
+ When adding a case here, prefer the following.
159
+
160
+ ### 1. Keep the spec normative
161
+
162
+ The spec should say what the case means. It should not merely paraphrase the code.
163
+
164
+ ### 2. Keep the code direct
165
+
166
+ The ECMAScript model should say what it does and do what it says. Avoid unnecessary framework machinery.
167
+
168
+ ### 3. Keep the data separate
169
+
170
+ Case facts belong in JSON, not hard-coded into the prose.
171
+
172
+ ### 4. Keep checks substantive
173
+
174
+ A check should add confidence. It should not only restate the answer.
175
+
176
+ ### 5. Keep names aligned
177
+
178
+ If a case is called `delfour` in `examples/` and `examples/extra/`, the ARC Bridge case should use the same base name.
179
+
180
+ ## Suggested workflow for a new case
181
+
182
+ 1. Start from a strong ARC-style N3 example.
183
+ 2. Write a controlled-English specification of the case.
184
+ 3. Move the concrete instance into JSON.
185
+ 4. Implement a small ECMAScript reference model.
186
+ 5. Capture the expected result in JSON.
187
+ 6. Keep the visible output in Answer / Reason Why / Check shape.
188
+ 7. Link the bridge case to its N3 and specialized counterparts.
189
+
190
+ ## What ARC Bridge is not
191
+
192
+ ARC Bridge is not:
193
+
194
+ - a replacement for declarative Eyeling,
195
+ - a performance collection,
196
+ - a general application framework,
197
+ - or a place for prose that cannot be tested.
198
+
199
+ It exists to make a case simultaneously:
200
+
201
+ - readable,
202
+ - executable,
203
+ - checkable,
204
+ - and portable.
205
+
206
+ ## In one line
207
+
208
+ `examples/arc-bridge/` presents controlled-English ARC case specifications with reference ECMAScript realizations, JSON instances, and expected results, as a bridge between declarative Eyeling examples and specialized executable companions.
@@ -0,0 +1,68 @@
1
+ {
2
+ "$schema": "./delfour.instance.schema.json",
3
+ "caseName": "Delfour",
4
+ "retailer": "Delfour",
5
+ "question": "Is the Delfour self-scanner allowed to use a neutral shopping insight for shopping assistance, and if so what lower-sugar alternative should it suggest?",
6
+ "timestamps": {
7
+ "createdAt": "2025-10-05T20:33:48.907163+00:00",
8
+ "expiresAt": "2025-10-05T22:33:48.907185+00:00",
9
+ "authorizedAt": "2025-10-05T20:35:48.907163+00:00",
10
+ "dutyPerformedAt": "2025-10-05T20:37:48.907163+00:00"
11
+ },
12
+ "evaluationContext": {
13
+ "scopeDevice": "self-scanner",
14
+ "scopeEvent": "pick_up_scanner",
15
+ "purpose": "shopping_assist",
16
+ "prohibitedReusePurpose": "marketing",
17
+ "requestAction": "odrl:use"
18
+ },
19
+ "thresholds": {
20
+ "sugarPerServingGAtLeast": 10.0
21
+ },
22
+ "householdProfile": {
23
+ "condition": "Diabetes"
24
+ },
25
+ "catalog": [
26
+ {
27
+ "id": "prod:BIS_001",
28
+ "name": "Classic Tea Biscuits",
29
+ "sugarTenths": 120,
30
+ "sugarPerServing": 12.0
31
+ },
32
+ {
33
+ "id": "prod:BIS_101",
34
+ "name": "Low-Sugar Tea Biscuits",
35
+ "sugarTenths": 30,
36
+ "sugarPerServing": 3.0
37
+ },
38
+ {
39
+ "id": "prod:CHOC_050",
40
+ "name": "Milk Chocolate Bar",
41
+ "sugarTenths": 150,
42
+ "sugarPerServing": 15.0
43
+ },
44
+ {
45
+ "id": "prod:CHOC_150",
46
+ "name": "85% Dark Chocolate",
47
+ "sugarTenths": 60,
48
+ "sugarPerServing": 6.0
49
+ }
50
+ ],
51
+ "scan": {
52
+ "scannedProductId": "prod:BIS_001"
53
+ },
54
+ "insightPolicy": {
55
+ "id": "https://example.org/insight/delfour",
56
+ "metric": "sugar_g_per_serving",
57
+ "type": "ins:Insight",
58
+ "suggestionPolicy": "lower_metric_first_higher_price_ok",
59
+ "policyType": "odrl:Policy",
60
+ "policyProfile": "Delfour-Insight-Policy"
61
+ },
62
+ "integrity": {
63
+ "hashAlgorithm": "SHA-256",
64
+ "macAlgorithm": "HMAC-SHA-256",
65
+ "secret": "neutral-insight-demo-shared-secret",
66
+ "verificationMode": "trustedPrecomputedInput"
67
+ }
68
+ }
@@ -0,0 +1,88 @@
1
+ {
2
+ "caseName": "Delfour",
3
+ "derived": {
4
+ "needsLowSugar": true,
5
+ "highSugarScanned": true,
6
+ "lowerSugarCandidateIds": ["prod:BIS_101", "prod:CHOC_150"],
7
+ "recommendedAlternativeId": "prod:BIS_101",
8
+ "recommendedAlternativeName": "Low-Sugar Tea Biscuits",
9
+ "alternativeLowersSugar": true
10
+ },
11
+ "envelope": {
12
+ "insight": {
13
+ "createdAt": "2025-10-05T20:33:48.907163+00:00",
14
+ "expiresAt": "2025-10-05T22:33:48.907185+00:00",
15
+ "id": "https://example.org/insight/delfour",
16
+ "metric": "sugar_g_per_serving",
17
+ "retailer": "Delfour",
18
+ "scopeDevice": "self-scanner",
19
+ "scopeEvent": "pick_up_scanner",
20
+ "suggestionPolicy": "lower_metric_first_higher_price_ok",
21
+ "threshold": 10,
22
+ "type": "ins:Insight"
23
+ },
24
+ "policy": {
25
+ "duty": {
26
+ "action": "odrl:delete",
27
+ "constraint": {
28
+ "leftOperand": "odrl:dateTime",
29
+ "operator": "odrl:eq",
30
+ "rightOperand": "2025-10-05T22:33:48.907185+00:00"
31
+ }
32
+ },
33
+ "permission": {
34
+ "action": "odrl:use",
35
+ "constraint": {
36
+ "leftOperand": "odrl:purpose",
37
+ "operator": "odrl:eq",
38
+ "rightOperand": "shopping_assist"
39
+ },
40
+ "target": "https://example.org/insight/delfour"
41
+ },
42
+ "profile": "Delfour-Insight-Policy",
43
+ "prohibition": {
44
+ "action": "odrl:distribute",
45
+ "constraint": {
46
+ "leftOperand": "odrl:purpose",
47
+ "operator": "odrl:eq",
48
+ "rightOperand": "marketing"
49
+ },
50
+ "target": "https://example.org/insight/delfour"
51
+ },
52
+ "type": "odrl:Policy"
53
+ }
54
+ },
55
+ "integrity": {
56
+ "canonicalEnvelope": "{\"insight\":{\"createdAt\":\"2025-10-05T20:33:48.907163+00:00\",\"expiresAt\":\"2025-10-05T22:33:48.907185+00:00\",\"id\":\"https://example.org/insight/delfour\",\"metric\":\"sugar_g_per_serving\",\"retailer\":\"Delfour\",\"scopeDevice\":\"self-scanner\",\"scopeEvent\":\"pick_up_scanner\",\"suggestionPolicy\":\"lower_metric_first_higher_price_ok\",\"threshold\":10.0,\"type\":\"ins:Insight\"},\"policy\":{\"duty\":{\"action\":\"odrl:delete\",\"constraint\":{\"leftOperand\":\"odrl:dateTime\",\"operator\":\"odrl:eq\",\"rightOperand\":\"2025-10-05T22:33:48.907185+00:00\"}},\"permission\":{\"action\":\"odrl:use\",\"constraint\":{\"leftOperand\":\"odrl:purpose\",\"operator\":\"odrl:eq\",\"rightOperand\":\"shopping_assist\"},\"target\":\"https://example.org/insight/delfour\"},\"profile\":\"Delfour-Insight-Policy\",\"prohibition\":{\"action\":\"odrl:distribute\",\"constraint\":{\"leftOperand\":\"odrl:purpose\",\"operator\":\"odrl:eq\",\"rightOperand\":\"marketing\"},\"target\":\"https://example.org/insight/delfour\"},\"type\":\"odrl:Policy\"}}",
57
+ "payloadHashSHA256": "e1ad69852c98ca7697a164dbc6f0ca28f873508a6676865dba37b81faa66ebcb",
58
+ "envelopeHmacSHA256": "518a84185e2975928c6c935dae6e251a071766078c6e9e70d6f583a1147728db",
59
+ "verificationMode": "trustedPrecomputedInput"
60
+ },
61
+ "answer": {
62
+ "sentence": "The scanner is allowed to use a neutral shopping insight and recommends Low-Sugar Tea Biscuits instead of Classic Tea Biscuits.",
63
+ "scannedProduct": "Classic Tea Biscuits",
64
+ "suggestedAlternative": "Low-Sugar Tea Biscuits",
65
+ "payloadHashSHA256": "e1ad69852c98ca7697a164dbc6f0ca28f873508a6676865dba37b81faa66ebcb",
66
+ "envelopeHmacSHA256": "518a84185e2975928c6c935dae6e251a071766078c6e9e70d6f583a1147728db"
67
+ },
68
+ "reasonWhy": [
69
+ "The phone desensitizes a diabetes-related household condition into a scoped low-sugar need, wraps it in an expiring Insight+Policy envelope, and signs it.",
70
+ "scanned product : Classic Tea Biscuits",
71
+ "suggested alternative: Low-Sugar Tea Biscuits",
72
+ "payload SHA-256 : e1ad69852c98ca7697a164dbc6f0ca28f873508a6676865dba37b81faa66ebcb",
73
+ "HMAC-SHA256 : 518a84185e2975928c6c935dae6e251a071766078c6e9e70d6f583a1147728db"
74
+ ],
75
+ "checks": {
76
+ "signatureVerifies": true,
77
+ "payloadHashMatches": true,
78
+ "minimizationRespected": true,
79
+ "scopeComplete": true,
80
+ "authorizationAllowed": true,
81
+ "highSugarBanner": true,
82
+ "alternativeLowersSugar": true,
83
+ "dutyTimingConsistent": true,
84
+ "marketingProhibited": true
85
+ },
86
+ "allChecksPass": true,
87
+ "arcText": "=== Answer ===\nThe scanner is allowed to use a neutral shopping insight and recommends Low-Sugar Tea Biscuits instead of Classic Tea Biscuits.\n\n=== Reason Why ===\nThe phone desensitizes a diabetes-related household condition into a scoped low-sugar need, wraps it in an expiring Insight+Policy envelope, and signs it.\nscanned product : Classic Tea Biscuits\nsuggested alternative: Low-Sugar Tea Biscuits\npayload SHA-256 : e1ad69852c98ca7697a164dbc6f0ca28f873508a6676865dba37b81faa66ebcb\nHMAC-SHA256 : 518a84185e2975928c6c935dae6e251a071766078c6e9e70d6f583a1147728db\n\n=== Check ===\nsignature verifies : yes\npayload hash matches : yes\nminimization strips sensitive terms: yes\nscope complete : yes\nauthorization allowed : yes\nhigh-sugar banner : yes\nalternative lowers sugar : yes\nduty timing consistent : yes\nmarketing prohibited : yes"
88
+ }
@@ -0,0 +1,201 @@
1
+ {
2
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
3
+ "$id": "https://example.org/schema/delfour.instance.schema.json",
4
+ "title": "ARC Bridge Flandor instance",
5
+ "type": "object",
6
+ "additionalProperties": false,
7
+ "required": [
8
+ "$schema",
9
+ "caseName",
10
+ "retailer",
11
+ "question",
12
+ "timestamps",
13
+ "evaluationContext",
14
+ "thresholds",
15
+ "householdProfile",
16
+ "catalog",
17
+ "scan",
18
+ "insightPolicy",
19
+ "integrity"
20
+ ],
21
+ "properties": {
22
+ "$schema": {
23
+ "type": "string"
24
+ },
25
+ "caseName": {
26
+ "type": "string",
27
+ "minLength": 1
28
+ },
29
+ "retailer": {
30
+ "type": "string",
31
+ "minLength": 1
32
+ },
33
+ "question": {
34
+ "type": "string",
35
+ "minLength": 1
36
+ },
37
+ "timestamps": {
38
+ "type": "object",
39
+ "additionalProperties": false,
40
+ "required": ["createdAt", "expiresAt", "authorizedAt", "dutyPerformedAt"],
41
+ "properties": {
42
+ "createdAt": {
43
+ "type": "string",
44
+ "format": "date-time"
45
+ },
46
+ "expiresAt": {
47
+ "type": "string",
48
+ "format": "date-time"
49
+ },
50
+ "authorizedAt": {
51
+ "type": "string",
52
+ "format": "date-time"
53
+ },
54
+ "dutyPerformedAt": {
55
+ "type": "string",
56
+ "format": "date-time"
57
+ }
58
+ }
59
+ },
60
+ "evaluationContext": {
61
+ "type": "object",
62
+ "additionalProperties": false,
63
+ "required": ["scopeDevice", "scopeEvent", "purpose", "prohibitedReusePurpose", "requestAction"],
64
+ "properties": {
65
+ "scopeDevice": {
66
+ "type": "string",
67
+ "minLength": 1
68
+ },
69
+ "scopeEvent": {
70
+ "type": "string",
71
+ "minLength": 1
72
+ },
73
+ "purpose": {
74
+ "type": "string",
75
+ "minLength": 1
76
+ },
77
+ "prohibitedReusePurpose": {
78
+ "type": "string",
79
+ "minLength": 1
80
+ },
81
+ "requestAction": {
82
+ "type": "string",
83
+ "minLength": 1
84
+ }
85
+ }
86
+ },
87
+ "thresholds": {
88
+ "type": "object",
89
+ "additionalProperties": false,
90
+ "required": ["sugarPerServingGAtLeast"],
91
+ "properties": {
92
+ "sugarPerServingGAtLeast": {
93
+ "type": "number"
94
+ }
95
+ }
96
+ },
97
+ "householdProfile": {
98
+ "type": "object",
99
+ "additionalProperties": false,
100
+ "required": ["condition"],
101
+ "properties": {
102
+ "condition": {
103
+ "type": "string",
104
+ "minLength": 1
105
+ }
106
+ }
107
+ },
108
+ "catalog": {
109
+ "type": "array",
110
+ "minItems": 1,
111
+ "items": {
112
+ "type": "object",
113
+ "additionalProperties": false,
114
+ "required": ["id", "name", "sugarTenths", "sugarPerServing"],
115
+ "properties": {
116
+ "id": {
117
+ "type": "string",
118
+ "minLength": 1
119
+ },
120
+ "name": {
121
+ "type": "string",
122
+ "minLength": 1
123
+ },
124
+ "sugarTenths": {
125
+ "type": "integer",
126
+ "minimum": 0
127
+ },
128
+ "sugarPerServing": {
129
+ "type": "number",
130
+ "minimum": 0
131
+ }
132
+ }
133
+ }
134
+ },
135
+ "scan": {
136
+ "type": "object",
137
+ "additionalProperties": false,
138
+ "required": ["scannedProductId"],
139
+ "properties": {
140
+ "scannedProductId": {
141
+ "type": "string",
142
+ "minLength": 1
143
+ }
144
+ }
145
+ },
146
+ "insightPolicy": {
147
+ "type": "object",
148
+ "additionalProperties": false,
149
+ "required": ["id", "metric", "type", "suggestionPolicy", "policyType", "policyProfile"],
150
+ "properties": {
151
+ "id": {
152
+ "type": "string",
153
+ "minLength": 1
154
+ },
155
+ "metric": {
156
+ "type": "string",
157
+ "minLength": 1
158
+ },
159
+ "type": {
160
+ "type": "string",
161
+ "minLength": 1
162
+ },
163
+ "suggestionPolicy": {
164
+ "type": "string",
165
+ "minLength": 1
166
+ },
167
+ "policyType": {
168
+ "type": "string",
169
+ "minLength": 1
170
+ },
171
+ "policyProfile": {
172
+ "type": "string",
173
+ "minLength": 1
174
+ }
175
+ }
176
+ },
177
+ "integrity": {
178
+ "type": "object",
179
+ "additionalProperties": false,
180
+ "required": ["hashAlgorithm", "macAlgorithm", "secret", "verificationMode"],
181
+ "properties": {
182
+ "hashAlgorithm": {
183
+ "type": "string",
184
+ "const": "SHA-256"
185
+ },
186
+ "macAlgorithm": {
187
+ "type": "string",
188
+ "const": "HMAC-SHA-256"
189
+ },
190
+ "secret": {
191
+ "type": "string",
192
+ "minLength": 1
193
+ },
194
+ "verificationMode": {
195
+ "type": "string",
196
+ "minLength": 1
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }