arcane-os 0.1.0-dev.5

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 (248) hide show
  1. package/CHANGELOG.md +154 -0
  2. package/COMMERCIAL-LICENSE.md +13 -0
  3. package/LICENSE +661 -0
  4. package/NOTICE +70 -0
  5. package/README.md +448 -0
  6. package/bin/arcane-test.mjs +741 -0
  7. package/bin/arcane.mjs +5 -0
  8. package/docs/architecture.md +234 -0
  9. package/docs/compatibility.md +36 -0
  10. package/docs/event-manager.md +166 -0
  11. package/docs/platform-targets.md +108 -0
  12. package/docs/publishing.md +203 -0
  13. package/docs/reference/README.md +117 -0
  14. package/docs/reference/arcane-ollama.md +288 -0
  15. package/docs/reference/availability-and-normalization.md +123 -0
  16. package/docs/reference/behavioral-testing.md +86 -0
  17. package/docs/reference/cli.md +569 -0
  18. package/docs/reference/core/README.md +62 -0
  19. package/docs/reference/core/arcane-ai-contracts.md +873 -0
  20. package/docs/reference/core/arcane-api.md +601 -0
  21. package/docs/reference/core/arcane-entities.md +65 -0
  22. package/docs/reference/core/arcane-events.md +134 -0
  23. package/docs/reference/core/ollama-module.md +181 -0
  24. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
  25. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
  26. package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
  27. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
  28. package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
  29. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
  30. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
  31. package/docs/reference/event-manager.md +957 -0
  32. package/docs/reference/inventory/package-api.json +2632 -0
  33. package/docs/reference/inventory/runtime-components.json +934 -0
  34. package/docs/reference/inventory/runtime-entities.json +26 -0
  35. package/docs/reference/inventory/runtime-modules.json +1249 -0
  36. package/docs/reference/protocols.md +242 -0
  37. package/docs/reference/runtime-components.md +1098 -0
  38. package/docs/reference/runtime-entities.md +303 -0
  39. package/docs/reference/runtime-modules.md +2010 -0
  40. package/docs/reference/sdk-api.md +4901 -0
  41. package/docs/roadmap.md +79 -0
  42. package/docs/work-amplification.md +124 -0
  43. package/node_modules/event-pubsub/CHANGELOG.md +55 -0
  44. package/node_modules/event-pubsub/MIGRATION.md +70 -0
  45. package/node_modules/event-pubsub/README.md +363 -0
  46. package/node_modules/event-pubsub/SECURITY.md +37 -0
  47. package/node_modules/event-pubsub/index.js +141 -0
  48. package/node_modules/event-pubsub/licence +21 -0
  49. package/node_modules/event-pubsub/package.json +59 -0
  50. package/node_modules/strong-type/README.md +408 -0
  51. package/node_modules/strong-type/assets/strong-type-header.png +0 -0
  52. package/node_modules/strong-type/index.js +1151 -0
  53. package/node_modules/strong-type/licence +21 -0
  54. package/node_modules/strong-type/node.js +125 -0
  55. package/node_modules/strong-type/package.json +61 -0
  56. package/package.json +95 -0
  57. package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
  58. package/runtime/arcane/components/app-bar.html +468 -0
  59. package/runtime/arcane/components/assistant-panel.html +715 -0
  60. package/runtime/arcane/components/calculator.html +8 -0
  61. package/runtime/arcane/components/chart.html +655 -0
  62. package/runtime/arcane/components/chat.html +1225 -0
  63. package/runtime/arcane/components/conversation-view.html +13 -0
  64. package/runtime/arcane/components/dashboard-config.html +341 -0
  65. package/runtime/arcane/components/data-maintenance.html +112 -0
  66. package/runtime/arcane/components/data-view.html +92 -0
  67. package/runtime/arcane/components/directory-picker.html +197 -0
  68. package/runtime/arcane/components/document-inspector.html +252 -0
  69. package/runtime/arcane/components/file-drop.html +264 -0
  70. package/runtime/arcane/components/file-inspector.html +293 -0
  71. package/runtime/arcane/components/file-manager.html +1715 -0
  72. package/runtime/arcane/components/header.html +142 -0
  73. package/runtime/arcane/components/integration-settings.html +14 -0
  74. package/runtime/arcane/components/local-ai-status.html +360 -0
  75. package/runtime/arcane/components/markdown-document.html +1048 -0
  76. package/runtime/arcane/components/markdown-editor.html +360 -0
  77. package/runtime/arcane/components/media-embed.html +8 -0
  78. package/runtime/arcane/components/modal.html +402 -0
  79. package/runtime/arcane/components/output-panel.html +259 -0
  80. package/runtime/arcane/components/preferences-form.html +135 -0
  81. package/runtime/arcane/components/record-timeline.html +105 -0
  82. package/runtime/arcane/components/relationship-board.html +116 -0
  83. package/runtime/arcane/components/screen-capture.html +8 -0
  84. package/runtime/arcane/components/source-code-viewer.html +441 -0
  85. package/runtime/arcane/components/source-explanation.html +124 -0
  86. package/runtime/arcane/components/speech.html +365 -0
  87. package/runtime/arcane/components/summary-strip.html +177 -0
  88. package/runtime/arcane/components/table.html +77 -0
  89. package/runtime/arcane/components/task-progress.html +282 -0
  90. package/runtime/arcane/components/terminal-workspace.html +65 -0
  91. package/runtime/arcane/components/theme-editor.html +41 -0
  92. package/runtime/arcane/components/theme-switcher.html +46 -0
  93. package/runtime/arcane/components/unified-inbox.html +20 -0
  94. package/runtime/arcane/components/voice-transcription.html +476 -0
  95. package/runtime/arcane/components/weather-widget.html +8 -0
  96. package/runtime/arcane/components/web-navigator.html +239 -0
  97. package/runtime/arcane/css/communications.css +1 -0
  98. package/runtime/arcane/css/dashboard-config.css +45 -0
  99. package/runtime/arcane/css/document-site.css +981 -0
  100. package/runtime/arcane/css/layout.css +438 -0
  101. package/runtime/arcane/css/primitives.css +321 -0
  102. package/runtime/arcane/css/theme.css +112 -0
  103. package/runtime/arcane/css/utility-workspace.css +1 -0
  104. package/runtime/arcane/entities/ApiModelRecord.js +20 -0
  105. package/runtime/arcane/entities/Calculation.js +13 -0
  106. package/runtime/arcane/entities/Chat.js +581 -0
  107. package/runtime/arcane/entities/CommunicationMessage.js +29 -0
  108. package/runtime/arcane/entities/CommunicationThread.js +21 -0
  109. package/runtime/arcane/entities/Document.js +10 -0
  110. package/runtime/arcane/entities/File.js +143 -0
  111. package/runtime/arcane/entities/Image.js +135 -0
  112. package/runtime/arcane/entities/IntentEnvelope.js +834 -0
  113. package/runtime/arcane/entities/Preference.js +83 -0
  114. package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
  115. package/runtime/arcane/entities/TerminalSession.js +46 -0
  116. package/runtime/arcane/entities/Theme.js +107 -0
  117. package/runtime/arcane/entities/User.js +1046 -0
  118. package/runtime/arcane/entities/Weather.js +23 -0
  119. package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
  120. package/runtime/arcane/img/arrow-left.png +0 -0
  121. package/runtime/arcane/img/arrow-right.png +0 -0
  122. package/runtime/arcane/img/doc.svg +5 -0
  123. package/runtime/arcane/img/folder.svg +4 -0
  124. package/runtime/arcane/img/image.svg +5 -0
  125. package/runtime/arcane/img/refresh.png +0 -0
  126. package/runtime/arcane/img/send.svg +9 -0
  127. package/runtime/arcane/img/trash.svg +5 -0
  128. package/runtime/arcane/img/upload.svg +5 -0
  129. package/runtime/arcane/modules/AI.js +2048 -0
  130. package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
  131. package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
  132. package/runtime/arcane/modules/AIResponseLength.js +42 -0
  133. package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
  134. package/runtime/arcane/modules/AnsiText.js +53 -0
  135. package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
  136. package/runtime/arcane/modules/AppDataScope.js +245 -0
  137. package/runtime/arcane/modules/AppearancePreferences.js +28 -0
  138. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
  139. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
  140. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
  141. package/runtime/arcane/modules/AsyncBoundary.js +161 -0
  142. package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
  143. package/runtime/arcane/modules/CalculatorEngine.js +20 -0
  144. package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
  145. package/runtime/arcane/modules/ChartLibrary.js +35 -0
  146. package/runtime/arcane/modules/ChatRecords.js +13 -0
  147. package/runtime/arcane/modules/CommunicationAppController.js +43 -0
  148. package/runtime/arcane/modules/CommunicationHub.js +16 -0
  149. package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
  150. package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
  151. package/runtime/arcane/modules/ComponentContracts.js +586 -0
  152. package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
  153. package/runtime/arcane/modules/ConversationActionItems.js +488 -0
  154. package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
  155. package/runtime/arcane/modules/ConversationTimebox.js +527 -0
  156. package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
  157. package/runtime/arcane/modules/DBLS.js +171 -0
  158. package/runtime/arcane/modules/DBOPFS.js +1154 -0
  159. package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
  160. package/runtime/arcane/modules/DataMaintenance.js +91 -0
  161. package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
  162. package/runtime/arcane/modules/DirectoryPicker.js +109 -0
  163. package/runtime/arcane/modules/DocumentNavigation.js +223 -0
  164. package/runtime/arcane/modules/Errors.js +1025 -0
  165. package/runtime/arcane/modules/GifEncoder.js +29 -0
  166. package/runtime/arcane/modules/HTMLImport.js +114 -0
  167. package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
  168. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
  169. package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
  170. package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
  171. package/runtime/arcane/modules/MD.js +111 -0
  172. package/runtime/arcane/modules/Mail.js +352 -0
  173. package/runtime/arcane/modules/MailTransport.mjs +180 -0
  174. package/runtime/arcane/modules/Marked.min.js +71 -0
  175. package/runtime/arcane/modules/MemoryRecords.js +44 -0
  176. package/runtime/arcane/modules/MessageAdvisory.js +38 -0
  177. package/runtime/arcane/modules/ModelDefinition.js +189 -0
  178. package/runtime/arcane/modules/Ollama.js +74 -0
  179. package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
  180. package/runtime/arcane/modules/OllamaSettings.js +24 -0
  181. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
  182. package/runtime/arcane/modules/PreferenceStore.js +109 -0
  183. package/runtime/arcane/modules/QRCode.min.js +1 -0
  184. package/runtime/arcane/modules/Questionnaire.js +61 -0
  185. package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
  186. package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
  187. package/runtime/arcane/modules/RecordReviewStore.js +94 -0
  188. package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
  189. package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
  190. package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
  191. package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
  192. package/runtime/arcane/modules/ScreenCapture.js +20 -0
  193. package/runtime/arcane/modules/SpeechPlayback.js +581 -0
  194. package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
  195. package/runtime/arcane/modules/SystemAppearance.js +20 -0
  196. package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
  197. package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
  198. package/runtime/arcane/modules/TerminalClient.js +52 -0
  199. package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
  200. package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
  201. package/runtime/arcane/modules/ThemeManager.js +131 -0
  202. package/runtime/arcane/modules/TimeGuard.js +149 -0
  203. package/runtime/arcane/modules/ToolCallRouter.js +83 -0
  204. package/runtime/arcane/modules/WaitForComponent.js +102 -0
  205. package/runtime/arcane/modules/YouTubeMedia.js +16 -0
  206. package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
  207. package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
  208. package/runtime/arcane/modules/uPlot.min.css +1 -0
  209. package/runtime/arcane/security/arcane-network-policy.json +6 -0
  210. package/runtime/strong-type/index.js +352 -0
  211. package/runtime/strong-type/licence +21 -0
  212. package/runtime/strong-type/package.json +45 -0
  213. package/schemas/arcane-app-bundle.schema.json +125 -0
  214. package/schemas/arcane-app.schema.json +329 -0
  215. package/schemas/arcane-lock.schema.json +86 -0
  216. package/schemas/arcane-package.schema.json +224 -0
  217. package/schemas/cli-event.schema.json +122 -0
  218. package/schemas/event-stack.schema.json +152 -0
  219. package/schemas/native-build-plan.schema.json +176 -0
  220. package/schemas/target-adapter.schema.json +117 -0
  221. package/src/app-descriptor.mjs +500 -0
  222. package/src/cli/main.mjs +561 -0
  223. package/src/constants.mjs +31 -0
  224. package/src/dev-server.mjs +718 -0
  225. package/src/doctor.mjs +315 -0
  226. package/src/dom-event-instrumentation.mjs +594 -0
  227. package/src/errors.mjs +75 -0
  228. package/src/event-manager.mjs +1342 -0
  229. package/src/event-queue.mjs +138 -0
  230. package/src/events.mjs +219 -0
  231. package/src/index.mjs +177 -0
  232. package/src/integrated-provider-loader.mjs +432 -0
  233. package/src/native-plan.mjs +698 -0
  234. package/src/native-provider-loader.mjs +1126 -0
  235. package/src/packager/core.mjs +2691 -0
  236. package/src/process.mjs +353 -0
  237. package/src/release-bundle.mjs +2523 -0
  238. package/src/repository.mjs +90 -0
  239. package/src/runtime.mjs +452 -0
  240. package/src/scaffold.mjs +380 -0
  241. package/src/targets/index.mjs +436 -0
  242. package/src/templates/assets/app-icon.png +0 -0
  243. package/src/templates/workspace-template.mjs +388 -0
  244. package/src/testing-loader.mjs +9 -0
  245. package/src/testing.mjs +427 -0
  246. package/src/toolchain.mjs +1335 -0
  247. package/src/update-check.mjs +307 -0
  248. package/src/workspace.mjs +449 -0
@@ -0,0 +1,1092 @@
1
+ const AGGREGATE_MAXIMUM_BYTES = 32 * 1024;
2
+ const IDENTIFIER_MAXIMUM_CHARACTERS = 128;
3
+ const REASON_CODE_MAXIMUM_BYTES = 128;
4
+ const REASON_CODE_MAXIMUM = 16;
5
+ const REQUIREMENT_MAXIMUM = 32;
6
+ const REQUIREMENT_TARGET_MAXIMUM_BYTES = 256;
7
+ const SCALAR_STRING_MAXIMUM_BYTES = 2048;
8
+
9
+ const SCHEMA = 'arcane.twin-policy-decision';
10
+ const VERSION = 1;
11
+ const IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
12
+ const REASON_CODE_PATTERN = /^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*){2,7}$/;
13
+ const REQUIREMENT_TARGET_PATTERN = /^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*){1,7}$/;
14
+ const EXPLICIT_TIMESTAMP_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{1,3})?(?:Z|[+-]\d{2}:\d{2})$/;
15
+ const RESERVED_KEYS_VALUES = ['__proto__', 'constructor', 'prototype'];
16
+ const LAYER_VALUES = ['advisory', 'organization', 'platform', 'role'];
17
+ const OUTCOME_VALUES = ['confirm', 'constrain', 'deny', 'escalate', 'log', 'permit', 'redact'];
18
+ const REQUIREMENT_OUTCOME_VALUES = ['confirm', 'constrain', 'escalate', 'log', 'redact'];
19
+ const CORE_REASON_CODE_VALUES = {
20
+ confirm: 'arcane.policy.confirm',
21
+ constrain: 'arcane.policy.constrain',
22
+ deny: 'arcane.policy.deny',
23
+ escalate: 'arcane.policy.escalate',
24
+ log: 'arcane.policy.log',
25
+ permit: 'arcane.policy.permit',
26
+ redact: 'arcane.policy.redact'
27
+ };
28
+ const ROOT_KEY_VALUES = [
29
+ 'schema',
30
+ 'version',
31
+ 'id',
32
+ 'evaluatedAt',
33
+ 'layer',
34
+ 'policy',
35
+ 'outcome',
36
+ 'reasonCodes',
37
+ 'requirements'
38
+ ];
39
+ const PAYLOAD_KEY_VALUES = ['outcome', 'reasonCodes', 'requirements'];
40
+ const TRUSTED_CONTEXT_KEY_VALUES = ['id', 'evaluatedAt', 'layer', 'policyId', 'policyVersion'];
41
+ const POLICY_KEY_VALUES = ['id', 'version'];
42
+ const REQUIREMENT_KEY_VALUES = ['id', 'reasonCode', 'target', 'value'];
43
+
44
+ const RESERVED_KEYS = new Set(RESERVED_KEYS_VALUES);
45
+ const LAYERS = new Set(LAYER_VALUES);
46
+ const OUTCOMES = new Set(OUTCOME_VALUES);
47
+ const REQUIREMENT_OUTCOMES = new Set(REQUIREMENT_OUTCOME_VALUES);
48
+ const ROOT_KEYS = Object.freeze(ROOT_KEY_VALUES);
49
+ const PAYLOAD_KEYS = Object.freeze(PAYLOAD_KEY_VALUES);
50
+ const TRUSTED_CONTEXT_KEYS = Object.freeze(TRUSTED_CONTEXT_KEY_VALUES);
51
+ const POLICY_KEYS = Object.freeze(POLICY_KEY_VALUES);
52
+ const REQUIREMENT_KEYS = Object.freeze(REQUIREMENT_KEY_VALUES);
53
+ const CORE_REASON_CODES = Object.freeze(CORE_REASON_CODE_VALUES);
54
+ const CORE_REASON_CODE_SET = new Set(Object.values(CORE_REASON_CODES));
55
+
56
+ function safeJSONIdentity() {
57
+ return this;
58
+ }
59
+ Object.freeze(safeJSONIdentity);
60
+
61
+ const SAFE_RECORD_PROTOTYPE = Object.create(Object.prototype);
62
+ Object.defineProperty(
63
+ SAFE_RECORD_PROTOTYPE,
64
+ 'toJSON',
65
+ {
66
+ value: safeJSONIdentity
67
+ }
68
+ );
69
+ Object.freeze(SAFE_RECORD_PROTOTYPE);
70
+
71
+ const SAFE_ARRAY_PROTOTYPE = Object.create(Array.prototype);
72
+ Object.defineProperty(
73
+ SAFE_ARRAY_PROTOTYPE,
74
+ 'toJSON',
75
+ {
76
+ value: safeJSONIdentity
77
+ }
78
+ );
79
+ Object.freeze(SAFE_ARRAY_PROTOTYPE);
80
+
81
+ const encoder = new TextEncoder();
82
+ const constructionToken = Symbol('TWiNPolicyDecision construction');
83
+ const decisionInstances = new WeakSet();
84
+ const validationFailureTokens = new WeakMap();
85
+ let activeValidationToken = null;
86
+
87
+ export class TWiNPolicyDecisionValidationError extends TypeError {
88
+ constructor(code, path, message) {
89
+ super(message);
90
+ defineDataProperty(this, 'name', 'TWiNPolicyDecisionValidationError');
91
+ defineDataProperty(this, 'code', code);
92
+ defineDataProperty(this, 'path', path);
93
+ }
94
+ }
95
+
96
+ function fail(code, path, message) {
97
+ const error = new TWiNPolicyDecisionValidationError(code, path, message);
98
+
99
+ validationFailureTokens.set(error, activeValidationToken);
100
+ Object.freeze(error);
101
+ throw error;
102
+ }
103
+
104
+ function validationBoundary(operation) {
105
+ const previousToken = activeValidationToken;
106
+ const operationToken = Symbol('TWiNPolicyDecision validation');
107
+
108
+ activeValidationToken = operationToken;
109
+ try {
110
+ return operation();
111
+ } catch (error) {
112
+ if (validationFailureTokens.get(error) === operationToken) {
113
+ throw error;
114
+ }
115
+ fail(
116
+ 'TWIN_POLICY_DECISION_INVALID',
117
+ '$',
118
+ 'TWiN policy decision input could not be inspected safely.'
119
+ );
120
+ } finally {
121
+ activeValidationToken = previousToken;
122
+ }
123
+ }
124
+
125
+ function safeRecord() {
126
+ return Object.create(SAFE_RECORD_PROTOTYPE);
127
+ }
128
+
129
+ function defineDataProperty(target, key, value) {
130
+ Object.defineProperty(
131
+ target,
132
+ key,
133
+ {
134
+ configurable: true,
135
+ enumerable: true,
136
+ value,
137
+ writable: true
138
+ }
139
+ );
140
+
141
+ return target;
142
+ }
143
+
144
+ function appendOwn(array, value) {
145
+ defineDataProperty(array, array.length, value);
146
+
147
+ return array;
148
+ }
149
+
150
+ function safeArray(values) {
151
+ const array = [];
152
+
153
+ for (let index = 0; index < values.length; index += 1) {
154
+ appendOwn(array, values[index]);
155
+ }
156
+ Object.setPrototypeOf(array, SAFE_ARRAY_PROTOTYPE);
157
+
158
+ return array;
159
+ }
160
+
161
+ function sortedCopy(values, compare) {
162
+ const sorted = [];
163
+
164
+ for (let index = 0; index < values.length; index += 1) {
165
+ appendOwn(sorted, values[index]);
166
+ }
167
+ for (let index = 1; index < sorted.length; index += 1) {
168
+ const current = sorted[index];
169
+ let destination = index;
170
+
171
+ while (destination > 0 && compare(sorted[destination - 1], current) > 0) {
172
+ defineDataProperty(sorted, destination, sorted[destination - 1]);
173
+ destination -= 1;
174
+ }
175
+ defineDataProperty(sorted, destination, current);
176
+ }
177
+
178
+ return sorted;
179
+ }
180
+
181
+ function byteLength(value) {
182
+ return encoder.encode(value).byteLength;
183
+ }
184
+
185
+ function validateUnicode(value, path) {
186
+ for (let index = 0; index < value.length; index += 1) {
187
+ const unit = value.charCodeAt(index);
188
+
189
+ if (unit >= 0xD800 && unit <= 0xDBFF) {
190
+ const following = value.charCodeAt(index + 1);
191
+
192
+ if (!(following >= 0xDC00 && following <= 0xDFFF)) {
193
+ fail(
194
+ 'TWIN_POLICY_DECISION_INVALID',
195
+ path,
196
+ 'TWiN policy decision text contains invalid Unicode.'
197
+ );
198
+ }
199
+ index += 1;
200
+ } else if (unit >= 0xDC00 && unit <= 0xDFFF) {
201
+ fail(
202
+ 'TWIN_POLICY_DECISION_INVALID',
203
+ path,
204
+ 'TWiN policy decision text contains invalid Unicode.'
205
+ );
206
+ }
207
+ }
208
+
209
+ return value;
210
+ }
211
+
212
+ function boundedString(value, path, maximum, options = {}) {
213
+ const nullable = options.nullable === true;
214
+ const nonempty = options.nonempty !== false;
215
+
216
+ if (nullable && value === null) {
217
+ return null;
218
+ }
219
+ if (typeof value !== 'string') {
220
+ fail(
221
+ 'TWIN_POLICY_DECISION_INVALID',
222
+ path,
223
+ 'TWiN policy decision field has an invalid type.'
224
+ );
225
+ }
226
+ if (value.length > maximum) {
227
+ fail(
228
+ 'TWIN_POLICY_DECISION_LIMIT_EXCEEDED',
229
+ path,
230
+ 'TWiN policy decision field exceeds its size limit.'
231
+ );
232
+ }
233
+ validateUnicode(value, path);
234
+ if (nonempty && !value.trim()) {
235
+ fail(
236
+ 'TWIN_POLICY_DECISION_INVALID',
237
+ path,
238
+ 'TWiN policy decision field must contain text.'
239
+ );
240
+ }
241
+ if (byteLength(value) > maximum) {
242
+ fail(
243
+ 'TWIN_POLICY_DECISION_LIMIT_EXCEEDED',
244
+ path,
245
+ 'TWiN policy decision field exceeds its size limit.'
246
+ );
247
+ }
248
+
249
+ return value;
250
+ }
251
+
252
+ function identifier(value, path) {
253
+ if (typeof value !== 'string') {
254
+ fail(
255
+ 'TWIN_POLICY_DECISION_INVALID',
256
+ path,
257
+ 'TWiN policy decision identifier has an invalid type.'
258
+ );
259
+ }
260
+ validateUnicode(value, path);
261
+ if (value.length > IDENTIFIER_MAXIMUM_CHARACTERS || !IDENTIFIER_PATTERN.test(value)) {
262
+ fail(
263
+ 'TWIN_POLICY_DECISION_INVALID',
264
+ path,
265
+ 'TWiN policy decision identifier is invalid.'
266
+ );
267
+ }
268
+
269
+ return value;
270
+ }
271
+
272
+ function enumeration(value, path, allowed) {
273
+ if (typeof value !== 'string' || !allowed.has(value)) {
274
+ fail(
275
+ 'TWIN_POLICY_DECISION_INVALID',
276
+ path,
277
+ 'TWiN policy decision enum value is invalid.'
278
+ );
279
+ }
280
+
281
+ return value;
282
+ }
283
+
284
+ function isReservedKey(key) {
285
+ return typeof key === 'string' && RESERVED_KEYS.has(key);
286
+ }
287
+
288
+ function plainRecord(value, path, allowedKeys, active) {
289
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
290
+ fail(
291
+ 'TWIN_POLICY_DECISION_INVALID',
292
+ path,
293
+ 'TWiN policy decision field must be a plain record.'
294
+ );
295
+ }
296
+ const prototype = Object.getPrototypeOf(value);
297
+
298
+ if (
299
+ prototype !== Object.prototype
300
+ && prototype !== null
301
+ && prototype !== SAFE_RECORD_PROTOTYPE
302
+ ) {
303
+ fail(
304
+ 'TWIN_POLICY_DECISION_INVALID',
305
+ path,
306
+ 'TWiN policy decision record prototype is invalid.'
307
+ );
308
+ }
309
+ if (active.has(value)) {
310
+ fail(
311
+ 'TWIN_POLICY_DECISION_INVALID',
312
+ path,
313
+ 'TWiN policy decision data must not contain cycles.'
314
+ );
315
+ }
316
+ active.add(value);
317
+ const allowed = new Set(allowedKeys);
318
+
319
+ for (const key of Reflect.ownKeys(value)) {
320
+ if (typeof key !== 'string' || isReservedKey(key) || !allowed.has(key)) {
321
+ active.delete(value);
322
+ fail(
323
+ 'TWIN_POLICY_DECISION_UNKNOWN_FIELD',
324
+ path,
325
+ 'TWiN policy decision contains an unsupported field.'
326
+ );
327
+ }
328
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
329
+
330
+ if (!descriptor || !Object.hasOwn(descriptor, 'value')) {
331
+ active.delete(value);
332
+ fail(
333
+ 'TWIN_POLICY_DECISION_INVALID',
334
+ `${path}.${key}`,
335
+ 'TWiN policy decision accessors are not allowed.'
336
+ );
337
+ }
338
+ }
339
+
340
+ return value;
341
+ }
342
+
343
+ function finishRecord(value, active) {
344
+ active.delete(value);
345
+ }
346
+
347
+ function ownValue(record, key, path, options = {}) {
348
+ const required = options.required !== false;
349
+ const descriptor = Object.getOwnPropertyDescriptor(record, key);
350
+
351
+ if (!descriptor) {
352
+ if (required) {
353
+ fail(
354
+ 'TWIN_POLICY_DECISION_INVALID',
355
+ `${path}.${key}`,
356
+ 'TWiN policy decision required field is missing.'
357
+ );
358
+ }
359
+
360
+ return options.defaultValue;
361
+ }
362
+
363
+ return descriptor.value;
364
+ }
365
+
366
+ function denseArray(value, path, maximum, active) {
367
+ if (!Array.isArray(value)) {
368
+ fail(
369
+ 'TWIN_POLICY_DECISION_INVALID',
370
+ path,
371
+ 'TWiN policy decision field must be an array.'
372
+ );
373
+ }
374
+ const prototype = Object.getPrototypeOf(value);
375
+
376
+ if (prototype !== Array.prototype && prototype !== SAFE_ARRAY_PROTOTYPE) {
377
+ fail(
378
+ 'TWIN_POLICY_DECISION_INVALID',
379
+ path,
380
+ 'TWiN policy decision array prototype is invalid.'
381
+ );
382
+ }
383
+ if (active.has(value)) {
384
+ fail(
385
+ 'TWIN_POLICY_DECISION_INVALID',
386
+ path,
387
+ 'TWiN policy decision data must not contain cycles.'
388
+ );
389
+ }
390
+ if (value.length > maximum) {
391
+ fail(
392
+ 'TWIN_POLICY_DECISION_LIMIT_EXCEEDED',
393
+ path,
394
+ 'TWiN policy decision array exceeds its item limit.'
395
+ );
396
+ }
397
+ active.add(value);
398
+
399
+ for (const key of Reflect.ownKeys(value)) {
400
+ if (key === 'length') {
401
+ continue;
402
+ }
403
+ if (
404
+ typeof key !== 'string'
405
+ || isReservedKey(key)
406
+ || !/^(0|[1-9]\d*)$/.test(key)
407
+ || Number(key) >= value.length
408
+ ) {
409
+ active.delete(value);
410
+ fail(
411
+ 'TWIN_POLICY_DECISION_UNKNOWN_FIELD',
412
+ path,
413
+ 'TWiN policy decision array contains an unsupported field.'
414
+ );
415
+ }
416
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
417
+
418
+ if (!descriptor || !Object.hasOwn(descriptor, 'value')) {
419
+ active.delete(value);
420
+ fail(
421
+ 'TWIN_POLICY_DECISION_INVALID',
422
+ `${path}[${key}]`,
423
+ 'TWiN policy decision accessors are not allowed.'
424
+ );
425
+ }
426
+ }
427
+ for (let index = 0; index < value.length; index += 1) {
428
+ if (!Object.hasOwn(value, index)) {
429
+ active.delete(value);
430
+ fail(
431
+ 'TWIN_POLICY_DECISION_INVALID',
432
+ `${path}[${index}]`,
433
+ 'TWiN policy decision sparse arrays are not allowed.'
434
+ );
435
+ }
436
+ }
437
+
438
+ return value;
439
+ }
440
+
441
+ function finishArray(value, active) {
442
+ active.delete(value);
443
+ }
444
+
445
+ function normalizeTimestamp(value, path) {
446
+ if (typeof value !== 'string') {
447
+ fail(
448
+ 'TWIN_POLICY_DECISION_INVALID',
449
+ path,
450
+ 'TWiN policy decision timestamp is invalid.'
451
+ );
452
+ }
453
+ const instant = new Date(value);
454
+
455
+ if (Number.isNaN(instant.valueOf()) || instant.toISOString() !== value) {
456
+ fail(
457
+ 'TWIN_POLICY_DECISION_INVALID',
458
+ path,
459
+ 'TWiN policy decision timestamp is invalid.'
460
+ );
461
+ }
462
+
463
+ return value;
464
+ }
465
+
466
+ function isLeapYear(year) {
467
+ return year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0);
468
+ }
469
+
470
+ function isValidExplicitTimestamp(value) {
471
+ if (!EXPLICIT_TIMESTAMP_PATTERN.test(value)) {
472
+ return false;
473
+ }
474
+ const year = Number(value.slice(0, 4));
475
+ const month = Number(value.slice(5, 7));
476
+ const day = Number(value.slice(8, 10));
477
+ const hour = Number(value.slice(11, 13));
478
+ const minute = Number(value.slice(14, 16));
479
+ const second = Number(value.slice(17, 19));
480
+ const daysByMonth = [
481
+ 31,
482
+ isLeapYear(year) ? 29 : 28,
483
+ 31,
484
+ 30,
485
+ 31,
486
+ 30,
487
+ 31,
488
+ 31,
489
+ 30,
490
+ 31,
491
+ 30,
492
+ 31
493
+ ];
494
+
495
+ if (
496
+ month < 1
497
+ || month > 12
498
+ || day < 1
499
+ || day > daysByMonth[month - 1]
500
+ || hour > 23
501
+ || minute > 59
502
+ || second > 59
503
+ ) {
504
+ return false;
505
+ }
506
+ if (!value.endsWith('Z')) {
507
+ const offsetHour = Number(value.slice(-5, -3));
508
+ const offsetMinute = Number(value.slice(-2));
509
+
510
+ if (offsetHour > 23 || offsetMinute > 59) {
511
+ return false;
512
+ }
513
+ }
514
+
515
+ return true;
516
+ }
517
+
518
+ function normalizeTrustedTimestamp(value, path) {
519
+ if (!(typeof value === 'string' || (value && typeof value === 'object'))) {
520
+ fail(
521
+ 'TWIN_POLICY_DECISION_INVALID',
522
+ path,
523
+ 'TWiN policy decision timestamp is invalid.'
524
+ );
525
+ }
526
+ let instant;
527
+
528
+ if (typeof value === 'string' && !isValidExplicitTimestamp(value)) {
529
+ fail(
530
+ 'TWIN_POLICY_DECISION_INVALID',
531
+ path,
532
+ 'TWiN policy decision timestamp is invalid.'
533
+ );
534
+ }
535
+ try {
536
+ if (typeof value === 'string') {
537
+ instant = new Date(value);
538
+ } else {
539
+ instant = new Date(Date.prototype.getTime.call(value));
540
+ }
541
+ } catch {
542
+ fail(
543
+ 'TWIN_POLICY_DECISION_INVALID',
544
+ path,
545
+ 'TWiN policy decision timestamp is invalid.'
546
+ );
547
+ }
548
+
549
+ if (Number.isNaN(instant.valueOf())) {
550
+ fail(
551
+ 'TWIN_POLICY_DECISION_INVALID',
552
+ path,
553
+ 'TWiN policy decision timestamp is invalid.'
554
+ );
555
+ }
556
+
557
+ return instant.toISOString();
558
+ }
559
+
560
+ function normalizeReasonCode(value, path) {
561
+ const code = boundedString(value, path, REASON_CODE_MAXIMUM_BYTES);
562
+
563
+ if (!REASON_CODE_PATTERN.test(code)) {
564
+ fail(
565
+ 'TWIN_POLICY_DECISION_INVALID',
566
+ path,
567
+ 'TWiN policy decision reason code is invalid.'
568
+ );
569
+ }
570
+
571
+ return code;
572
+ }
573
+
574
+ function compareStrings(left, right) {
575
+ if (left === right) {
576
+ return 0;
577
+ }
578
+
579
+ return left < right ? -1 : 1;
580
+ }
581
+
582
+ function normalizeReasonCodes(value, outcome, path, active) {
583
+ const list = denseArray(value, path, REASON_CODE_MAXIMUM, active);
584
+
585
+ if (list.length === 0) {
586
+ finishArray(list, active);
587
+ fail(
588
+ 'TWIN_POLICY_DECISION_INVALID',
589
+ path,
590
+ 'TWiN policy decision requires a reason code.'
591
+ );
592
+ }
593
+ const normalized = [];
594
+ const seen = new Set();
595
+ const requiredCode = CORE_REASON_CODES[outcome];
596
+
597
+ for (let index = 0; index < list.length; index += 1) {
598
+ const code = normalizeReasonCode(list[index], `${path}[${index}]`);
599
+
600
+ if (seen.has(code)) {
601
+ finishArray(list, active);
602
+ fail(
603
+ 'TWIN_POLICY_DECISION_INVALID',
604
+ `${path}[${index}]`,
605
+ 'TWiN policy decision reason codes must be unique.'
606
+ );
607
+ }
608
+ if (index > 0 && CORE_REASON_CODE_SET.has(code)) {
609
+ finishArray(list, active);
610
+ fail(
611
+ 'TWIN_POLICY_DECISION_INCONSISTENT',
612
+ `${path}[${index}]`,
613
+ 'TWiN policy decision contains a contradictory core reason code.'
614
+ );
615
+ }
616
+ seen.add(code);
617
+ appendOwn(normalized, code);
618
+ }
619
+ finishArray(list, active);
620
+
621
+ if (normalized[0] !== requiredCode) {
622
+ fail(
623
+ 'TWIN_POLICY_DECISION_INCONSISTENT',
624
+ `${path}[0]`,
625
+ 'TWiN policy decision outcome and core reason code do not agree.'
626
+ );
627
+ }
628
+ const additional = [];
629
+
630
+ for (let index = 1; index < normalized.length; index += 1) {
631
+ appendOwn(additional, normalized[index]);
632
+ }
633
+ const sortedAdditional = sortedCopy(additional, compareStrings);
634
+ const canonicalReasons = [];
635
+
636
+ appendOwn(canonicalReasons, requiredCode);
637
+ for (let index = 0; index < sortedAdditional.length; index += 1) {
638
+ appendOwn(canonicalReasons, sortedAdditional[index]);
639
+ }
640
+
641
+ return Object.freeze(safeArray(canonicalReasons));
642
+ }
643
+
644
+ function normalizeRequirementTarget(value, path) {
645
+ const target = boundedString(
646
+ value,
647
+ path,
648
+ REQUIREMENT_TARGET_MAXIMUM_BYTES,
649
+ {
650
+ nullable: true
651
+ }
652
+ );
653
+
654
+ if (target !== null && !REQUIREMENT_TARGET_PATTERN.test(target)) {
655
+ fail(
656
+ 'TWIN_POLICY_DECISION_INVALID',
657
+ path,
658
+ 'TWiN policy decision requirement target is invalid.'
659
+ );
660
+ }
661
+
662
+ return target;
663
+ }
664
+
665
+ function normalizeScalar(value, path) {
666
+ if (value === null || typeof value === 'boolean') {
667
+ return value;
668
+ }
669
+ if (typeof value === 'string') {
670
+ return boundedString(
671
+ value,
672
+ path,
673
+ SCALAR_STRING_MAXIMUM_BYTES,
674
+ {
675
+ nonempty: false
676
+ }
677
+ );
678
+ }
679
+ if (typeof value === 'number' && Number.isFinite(value)) {
680
+ return Object.is(value, -0) ? 0 : value;
681
+ }
682
+
683
+ fail(
684
+ 'TWIN_POLICY_DECISION_INVALID',
685
+ path,
686
+ 'TWiN policy decision requirement value must be a JSON scalar.'
687
+ );
688
+ }
689
+
690
+ function normalizeRequirement(value, path, reasonCodes, active) {
691
+ const record = plainRecord(value, path, REQUIREMENT_KEYS, active);
692
+ const requirement = {
693
+ id: identifier(ownValue(record, 'id', path), `${path}.id`),
694
+ reasonCode: normalizeReasonCode(
695
+ ownValue(record, 'reasonCode', path),
696
+ `${path}.reasonCode`
697
+ ),
698
+ target: normalizeRequirementTarget(
699
+ ownValue(record, 'target', path),
700
+ `${path}.target`
701
+ ),
702
+ value: normalizeScalar(
703
+ ownValue(record, 'value', path),
704
+ `${path}.value`
705
+ )
706
+ };
707
+ finishRecord(record, active);
708
+
709
+ if (!reasonCodes.has(requirement.reasonCode)) {
710
+ fail(
711
+ 'TWIN_POLICY_DECISION_INCONSISTENT',
712
+ `${path}.reasonCode`,
713
+ 'TWiN policy decision requirement references an undeclared reason code.'
714
+ );
715
+ }
716
+
717
+ return Object.freeze(requirement);
718
+ }
719
+
720
+ function compareRequirements(left, right) {
721
+ return compareStrings(left.id, right.id);
722
+ }
723
+
724
+ function normalizeRequirements(value, outcome, reasonCodes, path, active) {
725
+ const list = denseArray(value, path, REQUIREMENT_MAXIMUM, active);
726
+ const normalized = [];
727
+ const ids = new Set();
728
+
729
+ for (let index = 0; index < list.length; index += 1) {
730
+ const requirement = normalizeRequirement(
731
+ list[index],
732
+ `${path}[${index}]`,
733
+ reasonCodes,
734
+ active
735
+ );
736
+
737
+ if (ids.has(requirement.id)) {
738
+ finishArray(list, active);
739
+ fail(
740
+ 'TWIN_POLICY_DECISION_INVALID',
741
+ `${path}[${index}].id`,
742
+ 'TWiN policy decision requirement identifiers must be unique.'
743
+ );
744
+ }
745
+ ids.add(requirement.id);
746
+ appendOwn(normalized, requirement);
747
+ }
748
+ finishArray(list, active);
749
+
750
+ if (REQUIREMENT_OUTCOMES.has(outcome) && normalized.length === 0) {
751
+ fail(
752
+ 'TWIN_POLICY_DECISION_INCONSISTENT',
753
+ path,
754
+ 'TWiN policy decision outcome requires at least one requirement.'
755
+ );
756
+ }
757
+ if (!REQUIREMENT_OUTCOMES.has(outcome) && normalized.length !== 0) {
758
+ fail(
759
+ 'TWIN_POLICY_DECISION_INCONSISTENT',
760
+ path,
761
+ 'TWiN policy decision outcome does not accept requirements.'
762
+ );
763
+ }
764
+
765
+ return Object.freeze(safeArray(sortedCopy(normalized, compareRequirements)));
766
+ }
767
+
768
+ function normalizePolicy(value, path, active) {
769
+ const record = plainRecord(value, path, POLICY_KEYS, active);
770
+ const policy = {
771
+ id: identifier(ownValue(record, 'id', path), `${path}.id`),
772
+ version: identifier(ownValue(record, 'version', path), `${path}.version`)
773
+ };
774
+ finishRecord(record, active);
775
+
776
+ return Object.freeze(policy);
777
+ }
778
+
779
+ function clonePolicy(value) {
780
+ const policy = safeRecord();
781
+
782
+ defineDataProperty(policy, 'id', value.id);
783
+ defineDataProperty(policy, 'version', value.version);
784
+
785
+ return policy;
786
+ }
787
+
788
+ function cloneRequirement(value) {
789
+ const requirement = safeRecord();
790
+
791
+ defineDataProperty(requirement, 'id', value.id);
792
+ defineDataProperty(requirement, 'reasonCode', value.reasonCode);
793
+ defineDataProperty(requirement, 'target', value.target);
794
+ defineDataProperty(requirement, 'value', value.value);
795
+
796
+ return requirement;
797
+ }
798
+
799
+ function canonicalRecord(values) {
800
+ const requirements = [];
801
+
802
+ for (let index = 0; index < values.requirements.length; index += 1) {
803
+ appendOwn(requirements, cloneRequirement(values.requirements[index]));
804
+ }
805
+ const canonical = safeRecord();
806
+
807
+ defineDataProperty(canonical, 'schema', SCHEMA);
808
+ defineDataProperty(canonical, 'version', VERSION);
809
+ defineDataProperty(canonical, 'id', values.id);
810
+ defineDataProperty(canonical, 'evaluatedAt', values.evaluatedAt);
811
+ defineDataProperty(canonical, 'layer', values.layer);
812
+ defineDataProperty(canonical, 'policy', clonePolicy(values.policy));
813
+ defineDataProperty(canonical, 'outcome', values.outcome);
814
+ defineDataProperty(canonical, 'reasonCodes', safeArray(values.reasonCodes));
815
+ defineDataProperty(canonical, 'requirements', safeArray(requirements));
816
+
817
+ return canonical;
818
+ }
819
+
820
+ function validateAggregate(values) {
821
+ const serialized = JSON.stringify(canonicalRecord(values));
822
+
823
+ if (byteLength(serialized) > AGGREGATE_MAXIMUM_BYTES) {
824
+ fail(
825
+ 'TWIN_POLICY_DECISION_LIMIT_EXCEEDED',
826
+ '$',
827
+ 'TWiN policy decision exceeds its aggregate size limit.'
828
+ );
829
+ }
830
+ }
831
+
832
+ function normalizeValues(input) {
833
+ const active = new WeakSet();
834
+ const record = plainRecord(input, '$', ROOT_KEYS, active);
835
+ const schema = ownValue(record, 'schema', '$');
836
+
837
+ if (schema !== SCHEMA) {
838
+ finishRecord(record, active);
839
+ fail(
840
+ 'TWIN_POLICY_DECISION_UNSUPPORTED_VERSION',
841
+ 'schema',
842
+ 'TWiN policy decision schema is unsupported.'
843
+ );
844
+ }
845
+ const version = ownValue(record, 'version', '$');
846
+
847
+ if (version !== VERSION) {
848
+ finishRecord(record, active);
849
+ fail(
850
+ 'TWIN_POLICY_DECISION_UNSUPPORTED_VERSION',
851
+ 'version',
852
+ 'TWiN policy decision version is unsupported.'
853
+ );
854
+ }
855
+ const outcome = enumeration(
856
+ ownValue(record, 'outcome', '$'),
857
+ 'outcome',
858
+ OUTCOMES
859
+ );
860
+ const reasonCodes = normalizeReasonCodes(
861
+ ownValue(record, 'reasonCodes', '$'),
862
+ outcome,
863
+ 'reasonCodes',
864
+ active
865
+ );
866
+ const values = {
867
+ id: identifier(ownValue(record, 'id', '$'), 'id'),
868
+ evaluatedAt: normalizeTimestamp(
869
+ ownValue(record, 'evaluatedAt', '$'),
870
+ 'evaluatedAt'
871
+ ),
872
+ layer: enumeration(
873
+ ownValue(record, 'layer', '$'),
874
+ 'layer',
875
+ LAYERS
876
+ ),
877
+ policy: normalizePolicy(
878
+ ownValue(record, 'policy', '$'),
879
+ 'policy',
880
+ active
881
+ ),
882
+ outcome,
883
+ reasonCodes,
884
+ requirements: normalizeRequirements(
885
+ ownValue(record, 'requirements', '$'),
886
+ outcome,
887
+ new Set(reasonCodes),
888
+ 'requirements',
889
+ active
890
+ )
891
+ };
892
+ finishRecord(record, active);
893
+ validateAggregate(values);
894
+
895
+ return values;
896
+ }
897
+
898
+ function normalizeCreatePayload(payload, trustedContext) {
899
+ const active = new WeakSet();
900
+ const source = plainRecord(payload, 'payload', PAYLOAD_KEYS, active);
901
+ const context = plainRecord(
902
+ trustedContext,
903
+ 'trustedContext',
904
+ TRUSTED_CONTEXT_KEYS,
905
+ active
906
+ );
907
+ const canonical = {
908
+ schema: SCHEMA,
909
+ version: VERSION,
910
+ id: ownValue(context, 'id', 'trustedContext'),
911
+ evaluatedAt: normalizeTrustedTimestamp(
912
+ ownValue(context, 'evaluatedAt', 'trustedContext'),
913
+ 'evaluatedAt'
914
+ ),
915
+ layer: ownValue(context, 'layer', 'trustedContext'),
916
+ policy: {
917
+ id: ownValue(context, 'policyId', 'trustedContext'),
918
+ version: ownValue(context, 'policyVersion', 'trustedContext')
919
+ },
920
+ outcome: ownValue(source, 'outcome', 'payload'),
921
+ reasonCodes: ownValue(source, 'reasonCodes', 'payload'),
922
+ requirements: ownValue(
923
+ source,
924
+ 'requirements',
925
+ 'payload',
926
+ {
927
+ required: false,
928
+ defaultValue: []
929
+ }
930
+ )
931
+ };
932
+ finishRecord(context, active);
933
+ finishRecord(source, active);
934
+
935
+ return canonical;
936
+ }
937
+
938
+ function auditRequirement(value) {
939
+ const requirement = safeRecord();
940
+
941
+ defineDataProperty(requirement, 'id', value.id);
942
+ defineDataProperty(requirement, 'reasonCode', value.reasonCode);
943
+ defineDataProperty(requirement, 'targetPresent', value.target !== null);
944
+ defineDataProperty(
945
+ requirement,
946
+ 'valueType',
947
+ value.value === null ? 'null' : typeof value.value
948
+ );
949
+
950
+ return requirement;
951
+ }
952
+
953
+ class TWiNPolicyDecision {
954
+ constructor(token, values) {
955
+ if (token !== constructionToken) {
956
+ fail(
957
+ 'TWIN_POLICY_DECISION_INVALID',
958
+ '$',
959
+ 'TWiN policy decisions must be created through the public factory.'
960
+ );
961
+ }
962
+ defineDataProperty(this, 'schema', SCHEMA);
963
+ defineDataProperty(this, 'version', VERSION);
964
+ defineDataProperty(this, 'id', values.id);
965
+ defineDataProperty(this, 'evaluatedAt', values.evaluatedAt);
966
+ defineDataProperty(this, 'layer', values.layer);
967
+ defineDataProperty(this, 'policy', values.policy);
968
+ defineDataProperty(this, 'outcome', values.outcome);
969
+ defineDataProperty(this, 'reasonCodes', values.reasonCodes);
970
+ defineDataProperty(this, 'requirements', values.requirements);
971
+ decisionInstances.add(this);
972
+ Object.freeze(this);
973
+ }
974
+
975
+ toJSON() {
976
+ return canonicalRecord(this);
977
+ }
978
+ }
979
+ Object.freeze(TWiNPolicyDecision.prototype);
980
+
981
+ function buildDecision(canonical) {
982
+ const values = normalizeValues(canonical);
983
+
984
+ return new TWiNPolicyDecision(constructionToken, values);
985
+ }
986
+
987
+ export function createTWiNPolicyDecision(payload, trustedContext) {
988
+ return validationBoundary(
989
+ function validateCreateInput() {
990
+ return buildDecision(normalizeCreatePayload(payload, trustedContext));
991
+ }
992
+ );
993
+ }
994
+
995
+ export function rehydrateTWiNPolicyDecision(canonical) {
996
+ if (decisionInstances.has(canonical)) {
997
+ return canonical;
998
+ }
999
+ return validationBoundary(
1000
+ function validateCanonicalInput() {
1001
+ let source = canonical;
1002
+ let canonicalText = null;
1003
+
1004
+ if (typeof source === 'string') {
1005
+ if (
1006
+ source.length > AGGREGATE_MAXIMUM_BYTES
1007
+ || byteLength(source) > AGGREGATE_MAXIMUM_BYTES
1008
+ ) {
1009
+ fail(
1010
+ 'TWIN_POLICY_DECISION_LIMIT_EXCEEDED',
1011
+ '$',
1012
+ 'TWiN policy decision JSON exceeds its input size limit.'
1013
+ );
1014
+ }
1015
+ canonicalText = source;
1016
+ source = JSON.parse(source);
1017
+ }
1018
+ const decision = buildDecision(source);
1019
+
1020
+ if (
1021
+ canonicalText !== null
1022
+ && canonicalText !== JSON.stringify(canonicalRecord(decision))
1023
+ ) {
1024
+ fail(
1025
+ 'TWIN_POLICY_DECISION_INVALID',
1026
+ '$',
1027
+ 'TWiN policy decision JSON is not canonical.'
1028
+ );
1029
+ }
1030
+
1031
+ return decision;
1032
+ }
1033
+ );
1034
+ }
1035
+
1036
+ export function serializeTWiNPolicyDecision(decision) {
1037
+ return JSON.stringify(canonicalRecord(rehydrateTWiNPolicyDecision(decision)));
1038
+ }
1039
+
1040
+ export function twinPolicyDecisionAuditProjection(decision) {
1041
+ const value = rehydrateTWiNPolicyDecision(decision);
1042
+ const requirements = [];
1043
+
1044
+ for (let index = 0; index < value.requirements.length; index += 1) {
1045
+ appendOwn(
1046
+ requirements,
1047
+ Object.freeze(auditRequirement(value.requirements[index]))
1048
+ );
1049
+ }
1050
+ const projection = safeRecord();
1051
+
1052
+ defineDataProperty(projection, 'schema', value.schema);
1053
+ defineDataProperty(projection, 'version', value.version);
1054
+ defineDataProperty(projection, 'id', value.id);
1055
+ defineDataProperty(projection, 'evaluatedAt', value.evaluatedAt);
1056
+ defineDataProperty(projection, 'layer', value.layer);
1057
+ defineDataProperty(projection, 'policy', Object.freeze(clonePolicy(value.policy)));
1058
+ defineDataProperty(projection, 'outcome', value.outcome);
1059
+ defineDataProperty(
1060
+ projection,
1061
+ 'reasonCodes',
1062
+ Object.freeze(safeArray(value.reasonCodes))
1063
+ );
1064
+ defineDataProperty(
1065
+ projection,
1066
+ 'requirements',
1067
+ Object.freeze(safeArray(requirements))
1068
+ );
1069
+
1070
+ return Object.freeze(projection);
1071
+ }
1072
+
1073
+ const limits = Object.freeze({
1074
+ aggregateBytes: AGGREGATE_MAXIMUM_BYTES,
1075
+ identifierCharacters: IDENTIFIER_MAXIMUM_CHARACTERS,
1076
+ reasonCodeBytes: REASON_CODE_MAXIMUM_BYTES,
1077
+ reasonCodeCount: REASON_CODE_MAXIMUM,
1078
+ requirementCount: REQUIREMENT_MAXIMUM,
1079
+ requirementTargetBytes: REQUIREMENT_TARGET_MAXIMUM_BYTES,
1080
+ scalarStringBytes: SCALAR_STRING_MAXIMUM_BYTES
1081
+ });
1082
+ const contractValue = {
1083
+ schema: SCHEMA,
1084
+ version: VERSION,
1085
+ layers: Object.freeze([...LAYERS]),
1086
+ outcomes: Object.freeze([...OUTCOMES]),
1087
+ requirementOutcomes: Object.freeze([...REQUIREMENT_OUTCOMES]),
1088
+ coreReasonCodes: CORE_REASON_CODES,
1089
+ limits
1090
+ };
1091
+
1092
+ export const twinPolicyDecisionContract = Object.freeze(contractValue);