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.
- package/CHANGELOG.md +154 -0
- package/COMMERCIAL-LICENSE.md +13 -0
- package/LICENSE +661 -0
- package/NOTICE +70 -0
- package/README.md +448 -0
- package/bin/arcane-test.mjs +741 -0
- package/bin/arcane.mjs +5 -0
- package/docs/architecture.md +234 -0
- package/docs/compatibility.md +36 -0
- package/docs/event-manager.md +166 -0
- package/docs/platform-targets.md +108 -0
- package/docs/publishing.md +203 -0
- package/docs/reference/README.md +117 -0
- package/docs/reference/arcane-ollama.md +288 -0
- package/docs/reference/availability-and-normalization.md +123 -0
- package/docs/reference/behavioral-testing.md +86 -0
- package/docs/reference/cli.md +569 -0
- package/docs/reference/core/README.md +62 -0
- package/docs/reference/core/arcane-ai-contracts.md +873 -0
- package/docs/reference/core/arcane-api.md +601 -0
- package/docs/reference/core/arcane-entities.md +65 -0
- package/docs/reference/core/arcane-events.md +134 -0
- package/docs/reference/core/ollama-module.md +181 -0
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +1909 -0
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +1057 -0
- package/docs/reference/core/reference/arcane-api/core-and-events.md +320 -0
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +610 -0
- package/docs/reference/core/reference/arcane-api/namespaces.md +1157 -0
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +1423 -0
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +315 -0
- package/docs/reference/event-manager.md +957 -0
- package/docs/reference/inventory/package-api.json +2632 -0
- package/docs/reference/inventory/runtime-components.json +934 -0
- package/docs/reference/inventory/runtime-entities.json +26 -0
- package/docs/reference/inventory/runtime-modules.json +1249 -0
- package/docs/reference/protocols.md +242 -0
- package/docs/reference/runtime-components.md +1098 -0
- package/docs/reference/runtime-entities.md +303 -0
- package/docs/reference/runtime-modules.md +2010 -0
- package/docs/reference/sdk-api.md +4901 -0
- package/docs/roadmap.md +79 -0
- package/docs/work-amplification.md +124 -0
- package/node_modules/event-pubsub/CHANGELOG.md +55 -0
- package/node_modules/event-pubsub/MIGRATION.md +70 -0
- package/node_modules/event-pubsub/README.md +363 -0
- package/node_modules/event-pubsub/SECURITY.md +37 -0
- package/node_modules/event-pubsub/index.js +141 -0
- package/node_modules/event-pubsub/licence +21 -0
- package/node_modules/event-pubsub/package.json +59 -0
- package/node_modules/strong-type/README.md +408 -0
- package/node_modules/strong-type/assets/strong-type-header.png +0 -0
- package/node_modules/strong-type/index.js +1151 -0
- package/node_modules/strong-type/licence +21 -0
- package/node_modules/strong-type/node.js +125 -0
- package/node_modules/strong-type/package.json +61 -0
- package/package.json +95 -0
- package/runtime/ARCANE_RUNTIME_RELEASE.json +791 -0
- package/runtime/arcane/components/app-bar.html +468 -0
- package/runtime/arcane/components/assistant-panel.html +715 -0
- package/runtime/arcane/components/calculator.html +8 -0
- package/runtime/arcane/components/chart.html +655 -0
- package/runtime/arcane/components/chat.html +1225 -0
- package/runtime/arcane/components/conversation-view.html +13 -0
- package/runtime/arcane/components/dashboard-config.html +341 -0
- package/runtime/arcane/components/data-maintenance.html +112 -0
- package/runtime/arcane/components/data-view.html +92 -0
- package/runtime/arcane/components/directory-picker.html +197 -0
- package/runtime/arcane/components/document-inspector.html +252 -0
- package/runtime/arcane/components/file-drop.html +264 -0
- package/runtime/arcane/components/file-inspector.html +293 -0
- package/runtime/arcane/components/file-manager.html +1715 -0
- package/runtime/arcane/components/header.html +142 -0
- package/runtime/arcane/components/integration-settings.html +14 -0
- package/runtime/arcane/components/local-ai-status.html +360 -0
- package/runtime/arcane/components/markdown-document.html +1048 -0
- package/runtime/arcane/components/markdown-editor.html +360 -0
- package/runtime/arcane/components/media-embed.html +8 -0
- package/runtime/arcane/components/modal.html +402 -0
- package/runtime/arcane/components/output-panel.html +259 -0
- package/runtime/arcane/components/preferences-form.html +135 -0
- package/runtime/arcane/components/record-timeline.html +105 -0
- package/runtime/arcane/components/relationship-board.html +116 -0
- package/runtime/arcane/components/screen-capture.html +8 -0
- package/runtime/arcane/components/source-code-viewer.html +441 -0
- package/runtime/arcane/components/source-explanation.html +124 -0
- package/runtime/arcane/components/speech.html +365 -0
- package/runtime/arcane/components/summary-strip.html +177 -0
- package/runtime/arcane/components/table.html +77 -0
- package/runtime/arcane/components/task-progress.html +282 -0
- package/runtime/arcane/components/terminal-workspace.html +65 -0
- package/runtime/arcane/components/theme-editor.html +41 -0
- package/runtime/arcane/components/theme-switcher.html +46 -0
- package/runtime/arcane/components/unified-inbox.html +20 -0
- package/runtime/arcane/components/voice-transcription.html +476 -0
- package/runtime/arcane/components/weather-widget.html +8 -0
- package/runtime/arcane/components/web-navigator.html +239 -0
- package/runtime/arcane/css/communications.css +1 -0
- package/runtime/arcane/css/dashboard-config.css +45 -0
- package/runtime/arcane/css/document-site.css +981 -0
- package/runtime/arcane/css/layout.css +438 -0
- package/runtime/arcane/css/primitives.css +321 -0
- package/runtime/arcane/css/theme.css +112 -0
- package/runtime/arcane/css/utility-workspace.css +1 -0
- package/runtime/arcane/entities/ApiModelRecord.js +20 -0
- package/runtime/arcane/entities/Calculation.js +13 -0
- package/runtime/arcane/entities/Chat.js +581 -0
- package/runtime/arcane/entities/CommunicationMessage.js +29 -0
- package/runtime/arcane/entities/CommunicationThread.js +21 -0
- package/runtime/arcane/entities/Document.js +10 -0
- package/runtime/arcane/entities/File.js +143 -0
- package/runtime/arcane/entities/Image.js +135 -0
- package/runtime/arcane/entities/IntentEnvelope.js +834 -0
- package/runtime/arcane/entities/Preference.js +83 -0
- package/runtime/arcane/entities/TWiNPolicyDecision.js +1092 -0
- package/runtime/arcane/entities/TerminalSession.js +46 -0
- package/runtime/arcane/entities/Theme.js +107 -0
- package/runtime/arcane/entities/User.js +1046 -0
- package/runtime/arcane/entities/Weather.js +23 -0
- package/runtime/arcane/img/arcane-os-everywhere.png +0 -0
- package/runtime/arcane/img/arrow-left.png +0 -0
- package/runtime/arcane/img/arrow-right.png +0 -0
- package/runtime/arcane/img/doc.svg +5 -0
- package/runtime/arcane/img/folder.svg +4 -0
- package/runtime/arcane/img/image.svg +5 -0
- package/runtime/arcane/img/refresh.png +0 -0
- package/runtime/arcane/img/send.svg +9 -0
- package/runtime/arcane/img/trash.svg +5 -0
- package/runtime/arcane/img/upload.svg +5 -0
- package/runtime/arcane/modules/AI.js +2048 -0
- package/runtime/arcane/modules/AIPreferenceRuntime.js +32 -0
- package/runtime/arcane/modules/AIPreferenceTuple.js +92 -0
- package/runtime/arcane/modules/AIResponseLength.js +42 -0
- package/runtime/arcane/modules/AIResponseURLPolicy.js +626 -0
- package/runtime/arcane/modules/AnsiText.js +53 -0
- package/runtime/arcane/modules/ApiModelDatabase.js +25 -0
- package/runtime/arcane/modules/AppDataScope.js +245 -0
- package/runtime/arcane/modules/AppearancePreferences.js +28 -0
- package/runtime/arcane/modules/ArcaneCommunicationBridge.js +22 -0
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +135 -0
- package/runtime/arcane/modules/ArcaneNetworkPolicy.js +255 -0
- package/runtime/arcane/modules/AsyncBoundary.js +161 -0
- package/runtime/arcane/modules/BrowserTestSuite.js +326 -0
- package/runtime/arcane/modules/CalculatorEngine.js +20 -0
- package/runtime/arcane/modules/CaseEvidenceIndexer.js +134 -0
- package/runtime/arcane/modules/ChartLibrary.js +35 -0
- package/runtime/arcane/modules/ChatRecords.js +13 -0
- package/runtime/arcane/modules/CommunicationAppController.js +43 -0
- package/runtime/arcane/modules/CommunicationHub.js +16 -0
- package/runtime/arcane/modules/CommunicationPreferences.js +13 -0
- package/runtime/arcane/modules/CommunicationProviderRegistry.js +16 -0
- package/runtime/arcane/modules/ComponentContracts.js +586 -0
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +288 -0
- package/runtime/arcane/modules/ConversationActionItems.js +488 -0
- package/runtime/arcane/modules/ConversationClosingReport.js +274 -0
- package/runtime/arcane/modules/ConversationTimebox.js +527 -0
- package/runtime/arcane/modules/CoreLocalModelCatalog.js +255 -0
- package/runtime/arcane/modules/DBLS.js +171 -0
- package/runtime/arcane/modules/DBOPFS.js +1154 -0
- package/runtime/arcane/modules/DBOPFSWorker.js +116 -0
- package/runtime/arcane/modules/DataMaintenance.js +91 -0
- package/runtime/arcane/modules/DevelopmentWorkspace.js +74 -0
- package/runtime/arcane/modules/DirectoryPicker.js +109 -0
- package/runtime/arcane/modules/DocumentNavigation.js +223 -0
- package/runtime/arcane/modules/Errors.js +1025 -0
- package/runtime/arcane/modules/GifEncoder.js +29 -0
- package/runtime/arcane/modules/HTMLImport.js +114 -0
- package/runtime/arcane/modules/InMemoryCommunicationProvider.js +14 -0
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +275 -0
- package/runtime/arcane/modules/LocalAIReadiness.js +870 -0
- package/runtime/arcane/modules/LocalAIReadinessController.js +156 -0
- package/runtime/arcane/modules/MD.js +111 -0
- package/runtime/arcane/modules/Mail.js +352 -0
- package/runtime/arcane/modules/MailTransport.mjs +180 -0
- package/runtime/arcane/modules/Marked.min.js +71 -0
- package/runtime/arcane/modules/MemoryRecords.js +44 -0
- package/runtime/arcane/modules/MessageAdvisory.js +38 -0
- package/runtime/arcane/modules/ModelDefinition.js +189 -0
- package/runtime/arcane/modules/Ollama.js +74 -0
- package/runtime/arcane/modules/OllamaModelIdentifier.js +21 -0
- package/runtime/arcane/modules/OllamaSettings.js +24 -0
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +14 -0
- package/runtime/arcane/modules/PreferenceStore.js +109 -0
- package/runtime/arcane/modules/QRCode.min.js +1 -0
- package/runtime/arcane/modules/Questionnaire.js +61 -0
- package/runtime/arcane/modules/RecordLinkIndex.js +24 -0
- package/runtime/arcane/modules/RecordPassageIndex.js +222 -0
- package/runtime/arcane/modules/RecordReviewStore.js +94 -0
- package/runtime/arcane/modules/RevocableProjectionLedger.js +1623 -0
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +37 -0
- package/runtime/arcane/modules/ScamRiskPolicy.js +62 -0
- package/runtime/arcane/modules/ScopedOPFSCache.js +183 -0
- package/runtime/arcane/modules/ScreenCapture.js +20 -0
- package/runtime/arcane/modules/SpeechPlayback.js +581 -0
- package/runtime/arcane/modules/StaticDocumentCatalog.js +1248 -0
- package/runtime/arcane/modules/SystemAppearance.js +20 -0
- package/runtime/arcane/modules/SystemPlatformPresentation.js +51 -0
- package/runtime/arcane/modules/SystemToolRegistry.js +28 -0
- package/runtime/arcane/modules/TerminalClient.js +52 -0
- package/runtime/arcane/modules/TerminalCommandRegistry.js +50 -0
- package/runtime/arcane/modules/ThemeBootstrap.js +26 -0
- package/runtime/arcane/modules/ThemeManager.js +131 -0
- package/runtime/arcane/modules/TimeGuard.js +149 -0
- package/runtime/arcane/modules/ToolCallRouter.js +83 -0
- package/runtime/arcane/modules/WaitForComponent.js +102 -0
- package/runtime/arcane/modules/YouTubeMedia.js +16 -0
- package/runtime/arcane/modules/uPlot.LICENSE.txt +21 -0
- package/runtime/arcane/modules/uPlot.iife.min.js +2 -0
- package/runtime/arcane/modules/uPlot.min.css +1 -0
- package/runtime/arcane/security/arcane-network-policy.json +6 -0
- package/runtime/strong-type/index.js +352 -0
- package/runtime/strong-type/licence +21 -0
- package/runtime/strong-type/package.json +45 -0
- package/schemas/arcane-app-bundle.schema.json +125 -0
- package/schemas/arcane-app.schema.json +329 -0
- package/schemas/arcane-lock.schema.json +86 -0
- package/schemas/arcane-package.schema.json +224 -0
- package/schemas/cli-event.schema.json +122 -0
- package/schemas/event-stack.schema.json +152 -0
- package/schemas/native-build-plan.schema.json +176 -0
- package/schemas/target-adapter.schema.json +117 -0
- package/src/app-descriptor.mjs +500 -0
- package/src/cli/main.mjs +561 -0
- package/src/constants.mjs +31 -0
- package/src/dev-server.mjs +718 -0
- package/src/doctor.mjs +315 -0
- package/src/dom-event-instrumentation.mjs +594 -0
- package/src/errors.mjs +75 -0
- package/src/event-manager.mjs +1342 -0
- package/src/event-queue.mjs +138 -0
- package/src/events.mjs +219 -0
- package/src/index.mjs +177 -0
- package/src/integrated-provider-loader.mjs +432 -0
- package/src/native-plan.mjs +698 -0
- package/src/native-provider-loader.mjs +1126 -0
- package/src/packager/core.mjs +2691 -0
- package/src/process.mjs +353 -0
- package/src/release-bundle.mjs +2523 -0
- package/src/repository.mjs +90 -0
- package/src/runtime.mjs +452 -0
- package/src/scaffold.mjs +380 -0
- package/src/targets/index.mjs +436 -0
- package/src/templates/assets/app-icon.png +0 -0
- package/src/templates/workspace-template.mjs +388 -0
- package/src/testing-loader.mjs +9 -0
- package/src/testing.mjs +427 -0
- package/src/toolchain.mjs +1335 -0
- package/src/update-check.mjs +307 -0
- package/src/workspace.mjs +449 -0
|
@@ -0,0 +1,834 @@
|
|
|
1
|
+
const AGGREGATE_MAXIMUM_BYTES = 64 * 1024;
|
|
2
|
+
const AMBIGUITY_MAXIMUM = 32;
|
|
3
|
+
const CONSTRAINT_MAXIMUM = 64;
|
|
4
|
+
const DESCRIPTION_MAXIMUM_BYTES = 2048;
|
|
5
|
+
const EXPRESSION_MAXIMUM_BYTES = 32 * 1024;
|
|
6
|
+
const GOAL_MAXIMUM_BYTES = 8 * 1024;
|
|
7
|
+
const IDENTIFIER_MAXIMUM_CHARACTERS = 128;
|
|
8
|
+
const LABEL_MAXIMUM_BYTES = 256;
|
|
9
|
+
const OPTION_MAXIMUM = 16;
|
|
10
|
+
const OPTION_MAXIMUM_BYTES = 512;
|
|
11
|
+
const RESOURCE_LOCATOR_MAXIMUM_BYTES = 4096;
|
|
12
|
+
const RESOURCE_MAXIMUM = 64;
|
|
13
|
+
const SENSITIVITY_LABEL_MAXIMUM = 32;
|
|
14
|
+
|
|
15
|
+
const SCHEMA = 'arcane.intent-envelope';
|
|
16
|
+
const VERSION = 1;
|
|
17
|
+
const IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;
|
|
18
|
+
const RESERVED_KEYS_VALUES = ['__proto__', 'constructor', 'prototype'];
|
|
19
|
+
const CONSTRAINT_KIND_VALUES = ['exclusion', 'limit', 'other', 'preference', 'requirement'];
|
|
20
|
+
const RESOURCE_TYPE_VALUES = ['application', 'device', 'directory', 'file', 'other', 'record', 'uri'];
|
|
21
|
+
const AMBIGUITY_KIND_VALUES = ['meaning', 'other', 'reference', 'scope', 'target'];
|
|
22
|
+
const SENSITIVITY_LEVEL_VALUES = ['confidential', 'internal', 'public', 'restricted', 'unknown'];
|
|
23
|
+
const PROVENANCE_SOURCE_VALUES = ['application', 'import', 'system', 'user'];
|
|
24
|
+
const PROVENANCE_CHANNEL_VALUES = ['automation', 'selection', 'speech', 'text'];
|
|
25
|
+
const RESERVED_KEYS = new Set(RESERVED_KEYS_VALUES);
|
|
26
|
+
const CONSTRAINT_KINDS = new Set(CONSTRAINT_KIND_VALUES);
|
|
27
|
+
const RESOURCE_TYPES = new Set(RESOURCE_TYPE_VALUES);
|
|
28
|
+
const AMBIGUITY_KINDS = new Set(AMBIGUITY_KIND_VALUES);
|
|
29
|
+
const SENSITIVITY_LEVELS = new Set(SENSITIVITY_LEVEL_VALUES);
|
|
30
|
+
const PROVENANCE_SOURCES = new Set(PROVENANCE_SOURCE_VALUES);
|
|
31
|
+
const PROVENANCE_CHANNELS = new Set(PROVENANCE_CHANNEL_VALUES);
|
|
32
|
+
const ROOT_KEY_VALUES = [
|
|
33
|
+
'schema', 'version', 'id', 'createdAt', 'originalExpression', 'normalizedGoal',
|
|
34
|
+
'constraints', 'resources', 'ambiguities', 'sensitivity', 'provenance'
|
|
35
|
+
];
|
|
36
|
+
const PAYLOAD_KEY_VALUES = [
|
|
37
|
+
'originalExpression', 'normalizedGoal', 'constraints', 'resources', 'ambiguities', 'sensitivity'
|
|
38
|
+
];
|
|
39
|
+
const TRUSTED_CONTEXT_KEY_VALUES = [
|
|
40
|
+
'id', 'createdAt', 'source', 'channel', 'actorId', 'applicationId', 'sessionId'
|
|
41
|
+
];
|
|
42
|
+
const GOAL_KEY_VALUES = ['text', 'producer', 'version', 'confidence'];
|
|
43
|
+
const CONSTRAINT_KEY_VALUES = ['id', 'kind', 'value', 'description'];
|
|
44
|
+
const RESOURCE_KEY_VALUES = ['id', 'type', 'locator', 'description'];
|
|
45
|
+
const AMBIGUITY_KEY_VALUES = ['id', 'kind', 'description', 'options'];
|
|
46
|
+
const SENSITIVITY_KEY_VALUES = ['level', 'labels'];
|
|
47
|
+
const PROVENANCE_KEY_VALUES = ['source', 'channel', 'actorId', 'applicationId', 'sessionId'];
|
|
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 GOAL_KEYS = Object.freeze(GOAL_KEY_VALUES);
|
|
52
|
+
const CONSTRAINT_KEYS = Object.freeze(CONSTRAINT_KEY_VALUES);
|
|
53
|
+
const RESOURCE_KEYS = Object.freeze(RESOURCE_KEY_VALUES);
|
|
54
|
+
const AMBIGUITY_KEYS = Object.freeze(AMBIGUITY_KEY_VALUES);
|
|
55
|
+
const SENSITIVITY_KEYS = Object.freeze(SENSITIVITY_KEY_VALUES);
|
|
56
|
+
const PROVENANCE_KEYS = Object.freeze(PROVENANCE_KEY_VALUES);
|
|
57
|
+
|
|
58
|
+
const encoder = new TextEncoder();
|
|
59
|
+
const constructionToken = Symbol('IntentEnvelope construction');
|
|
60
|
+
|
|
61
|
+
export class IntentEnvelopeValidationError extends TypeError {
|
|
62
|
+
constructor(code, path, message, options) {
|
|
63
|
+
super(message, options);
|
|
64
|
+
this.name = 'IntentEnvelopeValidationError';
|
|
65
|
+
this.code = code;
|
|
66
|
+
this.path = path;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function fail(code, path, message, options) {
|
|
71
|
+
throw new IntentEnvelopeValidationError(code, path, message, options);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function isReservedKey(key) {
|
|
75
|
+
return typeof key === 'string' && RESERVED_KEYS.has(key);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function validateUnicode(value, path) {
|
|
79
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
80
|
+
const unit = value.charCodeAt(index);
|
|
81
|
+
if (unit >= 0xD800 && unit <= 0xDBFF) {
|
|
82
|
+
const following = value.charCodeAt(index + 1);
|
|
83
|
+
if (!(following >= 0xDC00 && following <= 0xDFFF)) {
|
|
84
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope text contains invalid Unicode.');
|
|
85
|
+
}
|
|
86
|
+
index += 1;
|
|
87
|
+
} else if (unit >= 0xDC00 && unit <= 0xDFFF) {
|
|
88
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope text contains invalid Unicode.');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function byteLength(value) {
|
|
95
|
+
return encoder.encode(value).byteLength;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function boundedString(value, path, maximum, options = {}) {
|
|
99
|
+
const nullable = options.nullable === true;
|
|
100
|
+
const nonempty = options.nonempty !== false;
|
|
101
|
+
|
|
102
|
+
if (nullable && value === null) {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
if (typeof value !== 'string') {
|
|
106
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope field has an invalid type.');
|
|
107
|
+
}
|
|
108
|
+
validateUnicode(value, path);
|
|
109
|
+
if (nonempty && !value.trim()) {
|
|
110
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope field must contain text.');
|
|
111
|
+
}
|
|
112
|
+
if (byteLength(value) > maximum) {
|
|
113
|
+
fail('INTENT_ENVELOPE_LIMIT_EXCEEDED', path, 'Intent envelope field exceeds its size limit.');
|
|
114
|
+
}
|
|
115
|
+
return value;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function identifier(value, path, options = {}) {
|
|
119
|
+
const nullable = options.nullable === true;
|
|
120
|
+
|
|
121
|
+
if (nullable && value === null) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
if (typeof value !== 'string') {
|
|
125
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope identifier has an invalid type.');
|
|
126
|
+
}
|
|
127
|
+
validateUnicode(value, path);
|
|
128
|
+
if (value.length > IDENTIFIER_MAXIMUM_CHARACTERS || !IDENTIFIER_PATTERN.test(value)) {
|
|
129
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope identifier is invalid.');
|
|
130
|
+
}
|
|
131
|
+
return value;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function enumeration(value, path, values) {
|
|
135
|
+
if (typeof value !== 'string' || !values.has(value)) {
|
|
136
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope enum value is invalid.');
|
|
137
|
+
}
|
|
138
|
+
return value;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function plainRecord(value, path, allowedKeys, active) {
|
|
142
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) {
|
|
143
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope field must be a plain record.');
|
|
144
|
+
}
|
|
145
|
+
const prototype = Object.getPrototypeOf(value);
|
|
146
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
147
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope record prototype is invalid.');
|
|
148
|
+
}
|
|
149
|
+
if (active.has(value)) {
|
|
150
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope data must not contain cycles.');
|
|
151
|
+
}
|
|
152
|
+
active.add(value);
|
|
153
|
+
const allowed = new Set(allowedKeys);
|
|
154
|
+
const ownKeys = Reflect.ownKeys(value);
|
|
155
|
+
|
|
156
|
+
for (const key of ownKeys) {
|
|
157
|
+
if (typeof key !== 'string' || isReservedKey(key) || !allowed.has(key)) {
|
|
158
|
+
active.delete(value);
|
|
159
|
+
fail('INTENT_ENVELOPE_UNKNOWN_FIELD', path, 'Intent envelope contains an unsupported field.');
|
|
160
|
+
}
|
|
161
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
162
|
+
if (!descriptor || !Object.hasOwn(descriptor, 'value')) {
|
|
163
|
+
active.delete(value);
|
|
164
|
+
fail('INTENT_ENVELOPE_INVALID', `${path}.${key}`, 'Intent envelope accessors are not allowed.');
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
return value;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function finishRecord(value, active) {
|
|
171
|
+
active.delete(value);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function ownValue(record, key, path, options = {}) {
|
|
175
|
+
const required = options.required !== false;
|
|
176
|
+
const defaultValue = options.defaultValue;
|
|
177
|
+
const descriptor = Object.getOwnPropertyDescriptor(record, key);
|
|
178
|
+
|
|
179
|
+
if (!descriptor) {
|
|
180
|
+
if (required) {
|
|
181
|
+
fail('INTENT_ENVELOPE_INVALID', `${path}.${key}`, 'Intent envelope required field is missing.');
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return defaultValue;
|
|
185
|
+
}
|
|
186
|
+
return descriptor.value;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function denseArray(value, path, maximum, active) {
|
|
190
|
+
if (!Array.isArray(value) || Object.getPrototypeOf(value) !== Array.prototype) {
|
|
191
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope field must be an array.');
|
|
192
|
+
}
|
|
193
|
+
if (active.has(value)) {
|
|
194
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope data must not contain cycles.');
|
|
195
|
+
}
|
|
196
|
+
if (value.length > maximum) {
|
|
197
|
+
fail('INTENT_ENVELOPE_LIMIT_EXCEEDED', path, 'Intent envelope array exceeds its item limit.');
|
|
198
|
+
}
|
|
199
|
+
active.add(value);
|
|
200
|
+
const keys = Reflect.ownKeys(value);
|
|
201
|
+
for (const key of keys) {
|
|
202
|
+
if (key === 'length') {
|
|
203
|
+
continue;
|
|
204
|
+
}
|
|
205
|
+
if (typeof key !== 'string' || isReservedKey(key) || !/^(0|[1-9]\d*)$/.test(key) || Number(key) >= value.length) {
|
|
206
|
+
active.delete(value);
|
|
207
|
+
fail('INTENT_ENVELOPE_UNKNOWN_FIELD', path, 'Intent envelope array contains an unsupported field.');
|
|
208
|
+
}
|
|
209
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
210
|
+
if (!descriptor || !Object.hasOwn(descriptor, 'value')) {
|
|
211
|
+
active.delete(value);
|
|
212
|
+
fail('INTENT_ENVELOPE_INVALID', `${path}[${key}]`, 'Intent envelope accessors are not allowed.');
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
216
|
+
if (!Object.hasOwn(value, index)) {
|
|
217
|
+
active.delete(value);
|
|
218
|
+
fail('INTENT_ENVELOPE_INVALID', `${path}[${index}]`, 'Intent envelope sparse arrays are not allowed.');
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return value;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function finishArray(value, active) {
|
|
225
|
+
active.delete(value);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function normalizeTimestamp(value, path) {
|
|
229
|
+
if (typeof value !== 'string') {
|
|
230
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope timestamp is invalid.');
|
|
231
|
+
}
|
|
232
|
+
const instant = new Date(value);
|
|
233
|
+
if (Number.isNaN(instant.valueOf()) || instant.toISOString() !== value) {
|
|
234
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope timestamp is invalid.');
|
|
235
|
+
}
|
|
236
|
+
return value;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function normalizeTrustedTimestamp(value, path) {
|
|
240
|
+
if (!(typeof value === 'string' || value instanceof Date)) {
|
|
241
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope timestamp is invalid.');
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const instant = new Date(value);
|
|
245
|
+
|
|
246
|
+
if (Number.isNaN(instant.valueOf())) {
|
|
247
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope timestamp is invalid.');
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return instant.toISOString();
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function normalizeGoal(value, path, active) {
|
|
254
|
+
if (value === null) {
|
|
255
|
+
return null;
|
|
256
|
+
}
|
|
257
|
+
const record = plainRecord(value, path, GOAL_KEYS, active);
|
|
258
|
+
const textValue = ownValue(record, 'text', path);
|
|
259
|
+
const producerValue = ownValue(record, 'producer', path);
|
|
260
|
+
const versionValue = ownValue(record, 'version', path);
|
|
261
|
+
const text = boundedString(textValue, `${path}.text`, GOAL_MAXIMUM_BYTES);
|
|
262
|
+
const producer = identifier(producerValue, `${path}.producer`);
|
|
263
|
+
const version = identifier(versionValue, `${path}.version`);
|
|
264
|
+
const confidenceValue = ownValue(record, 'confidence', path);
|
|
265
|
+
if (confidenceValue !== null && (
|
|
266
|
+
typeof confidenceValue !== 'number' || !Number.isFinite(confidenceValue) || confidenceValue < 0 || confidenceValue > 1
|
|
267
|
+
)) {
|
|
268
|
+
finishRecord(record, active);
|
|
269
|
+
fail('INTENT_ENVELOPE_INVALID', `${path}.confidence`, 'Intent envelope confidence is invalid.');
|
|
270
|
+
}
|
|
271
|
+
finishRecord(record, active);
|
|
272
|
+
const goal = { text, producer, version, confidence: confidenceValue };
|
|
273
|
+
return Object.freeze(goal);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
function normalizeScalar(value, path) {
|
|
277
|
+
if (value === null || typeof value === 'string' || typeof value === 'boolean') {
|
|
278
|
+
if (typeof value === 'string') {
|
|
279
|
+
validateUnicode(value, path);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return value;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (typeof value === 'number' && Number.isFinite(value)) {
|
|
286
|
+
return value;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
fail('INTENT_ENVELOPE_INVALID', path, 'Intent envelope scalar value is invalid.');
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function normalizeConstraint(value, index, active) {
|
|
293
|
+
const path = `constraints[${index}]`;
|
|
294
|
+
const record = plainRecord(value, path, CONSTRAINT_KEYS, active);
|
|
295
|
+
const idValue = ownValue(record, 'id', path);
|
|
296
|
+
const kindValue = ownValue(record, 'kind', path);
|
|
297
|
+
const scalarValue = ownValue(record, 'value', path);
|
|
298
|
+
const id = identifier(idValue, `${path}.id`);
|
|
299
|
+
const kind = enumeration(kindValue, `${path}.kind`, CONSTRAINT_KINDS);
|
|
300
|
+
const scalar = normalizeScalar(scalarValue, `${path}.value`);
|
|
301
|
+
const description = boundedString(
|
|
302
|
+
ownValue(
|
|
303
|
+
record,
|
|
304
|
+
'description',
|
|
305
|
+
path,
|
|
306
|
+
{
|
|
307
|
+
required: false,
|
|
308
|
+
defaultValue: null
|
|
309
|
+
}
|
|
310
|
+
),
|
|
311
|
+
`${path}.description`,
|
|
312
|
+
DESCRIPTION_MAXIMUM_BYTES,
|
|
313
|
+
{
|
|
314
|
+
nullable: true
|
|
315
|
+
}
|
|
316
|
+
);
|
|
317
|
+
finishRecord(record, active);
|
|
318
|
+
const constraint = { id, kind, value: scalar, description };
|
|
319
|
+
return Object.freeze(constraint);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
function normalizeResource(value, index, active) {
|
|
323
|
+
const path = `resources[${index}]`;
|
|
324
|
+
const record = plainRecord(value, path, RESOURCE_KEYS, active);
|
|
325
|
+
const idValue = ownValue(record, 'id', path);
|
|
326
|
+
const typeValue = ownValue(record, 'type', path);
|
|
327
|
+
const locatorValue = ownValue(record, 'locator', path);
|
|
328
|
+
const id = identifier(idValue, `${path}.id`);
|
|
329
|
+
const type = enumeration(typeValue, `${path}.type`, RESOURCE_TYPES);
|
|
330
|
+
const locator = boundedString(locatorValue, `${path}.locator`, RESOURCE_LOCATOR_MAXIMUM_BYTES);
|
|
331
|
+
const description = boundedString(
|
|
332
|
+
ownValue(
|
|
333
|
+
record,
|
|
334
|
+
'description',
|
|
335
|
+
path,
|
|
336
|
+
{
|
|
337
|
+
required: false,
|
|
338
|
+
defaultValue: null
|
|
339
|
+
}
|
|
340
|
+
),
|
|
341
|
+
`${path}.description`,
|
|
342
|
+
DESCRIPTION_MAXIMUM_BYTES,
|
|
343
|
+
{
|
|
344
|
+
nullable: true
|
|
345
|
+
}
|
|
346
|
+
);
|
|
347
|
+
finishRecord(record, active);
|
|
348
|
+
const resource = { id, type, locator, description };
|
|
349
|
+
return Object.freeze(resource);
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function normalizeOptions(value, path, active) {
|
|
353
|
+
const options = denseArray(value, path, OPTION_MAXIMUM, active);
|
|
354
|
+
const normalized = [];
|
|
355
|
+
for (let index = 0; index < options.length; index += 1) {
|
|
356
|
+
const descriptor = Object.getOwnPropertyDescriptor(options, String(index));
|
|
357
|
+
const normalizedOption = boundedString(descriptor.value, `${path}[${index}]`, OPTION_MAXIMUM_BYTES);
|
|
358
|
+
normalized.push(normalizedOption);
|
|
359
|
+
}
|
|
360
|
+
finishArray(options, active);
|
|
361
|
+
return Object.freeze(normalized);
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function normalizeAmbiguity(value, index, active) {
|
|
365
|
+
const path = `ambiguities[${index}]`;
|
|
366
|
+
const record = plainRecord(value, path, AMBIGUITY_KEYS, active);
|
|
367
|
+
const idValue = ownValue(record, 'id', path);
|
|
368
|
+
const kindValue = ownValue(record, 'kind', path);
|
|
369
|
+
const id = identifier(idValue, `${path}.id`);
|
|
370
|
+
const kind = enumeration(kindValue, `${path}.kind`, AMBIGUITY_KINDS);
|
|
371
|
+
const description = boundedString(
|
|
372
|
+
ownValue(record, 'description', path),
|
|
373
|
+
`${path}.description`,
|
|
374
|
+
DESCRIPTION_MAXIMUM_BYTES,
|
|
375
|
+
);
|
|
376
|
+
const options = normalizeOptions(
|
|
377
|
+
ownValue(
|
|
378
|
+
record,
|
|
379
|
+
'options',
|
|
380
|
+
path,
|
|
381
|
+
{
|
|
382
|
+
required: false,
|
|
383
|
+
defaultValue: []
|
|
384
|
+
}
|
|
385
|
+
),
|
|
386
|
+
`${path}.options`,
|
|
387
|
+
active,
|
|
388
|
+
);
|
|
389
|
+
finishRecord(record, active);
|
|
390
|
+
const ambiguity = { id, kind, description, options };
|
|
391
|
+
return Object.freeze(ambiguity);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function normalizeList(value, path, maximum, normalizer, active) {
|
|
395
|
+
const list = denseArray(value, path, maximum, active);
|
|
396
|
+
const normalized = [];
|
|
397
|
+
const ids = new Set();
|
|
398
|
+
for (let index = 0; index < list.length; index += 1) {
|
|
399
|
+
const descriptor = Object.getOwnPropertyDescriptor(list, String(index));
|
|
400
|
+
const item = normalizer(descriptor.value, index, active);
|
|
401
|
+
if (ids.has(item.id)) {
|
|
402
|
+
finishArray(list, active);
|
|
403
|
+
fail('INTENT_ENVELOPE_INVALID', `${path}[${index}].id`, 'Intent envelope item identifiers must be unique.');
|
|
404
|
+
}
|
|
405
|
+
ids.add(item.id);
|
|
406
|
+
normalized.push(item);
|
|
407
|
+
}
|
|
408
|
+
finishArray(list, active);
|
|
409
|
+
return Object.freeze(normalized);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
function normalizeSensitivity(value, path, active) {
|
|
413
|
+
const record = plainRecord(value, path, SENSITIVITY_KEYS, active);
|
|
414
|
+
const levelValue = ownValue(record, 'level', path);
|
|
415
|
+
const level = enumeration(levelValue, `${path}.level`, SENSITIVITY_LEVELS);
|
|
416
|
+
const labelsValue = ownValue(
|
|
417
|
+
record,
|
|
418
|
+
'labels',
|
|
419
|
+
path,
|
|
420
|
+
{
|
|
421
|
+
required: false,
|
|
422
|
+
defaultValue: []
|
|
423
|
+
}
|
|
424
|
+
);
|
|
425
|
+
const labels = denseArray(labelsValue, `${path}.labels`, SENSITIVITY_LABEL_MAXIMUM, active);
|
|
426
|
+
const normalized = [];
|
|
427
|
+
const seen = new Set();
|
|
428
|
+
for (let index = 0; index < labels.length; index += 1) {
|
|
429
|
+
const descriptor = Object.getOwnPropertyDescriptor(labels, String(index));
|
|
430
|
+
const label = boundedString(
|
|
431
|
+
descriptor.value,
|
|
432
|
+
`${path}.labels[${index}]`,
|
|
433
|
+
LABEL_MAXIMUM_BYTES,
|
|
434
|
+
).trim();
|
|
435
|
+
if (seen.has(label)) {
|
|
436
|
+
finishArray(labels, active);
|
|
437
|
+
finishRecord(record, active);
|
|
438
|
+
fail('INTENT_ENVELOPE_INVALID', `${path}.labels[${index}]`, 'Intent envelope sensitivity labels must be unique.');
|
|
439
|
+
}
|
|
440
|
+
seen.add(label);
|
|
441
|
+
normalized.push(label);
|
|
442
|
+
}
|
|
443
|
+
normalized.sort();
|
|
444
|
+
finishArray(labels, active);
|
|
445
|
+
finishRecord(record, active);
|
|
446
|
+
const sensitivity = {
|
|
447
|
+
level,
|
|
448
|
+
labels: Object.freeze(normalized)
|
|
449
|
+
};
|
|
450
|
+
return Object.freeze(sensitivity);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
function normalizeProvenance(value, path, active) {
|
|
454
|
+
const record = plainRecord(value, path, PROVENANCE_KEYS, active);
|
|
455
|
+
const sourceValue = ownValue(record, 'source', path);
|
|
456
|
+
const channelValue = ownValue(record, 'channel', path);
|
|
457
|
+
const actorIdValue = ownValue(record, 'actorId', path);
|
|
458
|
+
const applicationIdValue = ownValue(record, 'applicationId', path);
|
|
459
|
+
const sessionIdValue = ownValue(record, 'sessionId', path);
|
|
460
|
+
const source = enumeration(sourceValue, `${path}.source`, PROVENANCE_SOURCES);
|
|
461
|
+
const channel = enumeration(channelValue, `${path}.channel`, PROVENANCE_CHANNELS);
|
|
462
|
+
const nullableIdentifierOptions = {
|
|
463
|
+
nullable: true
|
|
464
|
+
};
|
|
465
|
+
const actorId = identifier(actorIdValue, `${path}.actorId`, nullableIdentifierOptions);
|
|
466
|
+
const applicationId = identifier(applicationIdValue, `${path}.applicationId`, nullableIdentifierOptions);
|
|
467
|
+
const sessionId = identifier(sessionIdValue, `${path}.sessionId`, nullableIdentifierOptions);
|
|
468
|
+
finishRecord(record, active);
|
|
469
|
+
const provenance = { source, channel, actorId, applicationId, sessionId };
|
|
470
|
+
return Object.freeze(provenance);
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function cloneGoal(value) {
|
|
474
|
+
if (value === null) {
|
|
475
|
+
return null;
|
|
476
|
+
}
|
|
477
|
+
return {
|
|
478
|
+
text: value.text,
|
|
479
|
+
producer: value.producer,
|
|
480
|
+
version: value.version,
|
|
481
|
+
confidence: value.confidence
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function cloneConstraint(value) {
|
|
486
|
+
return {
|
|
487
|
+
id: value.id,
|
|
488
|
+
kind: value.kind,
|
|
489
|
+
value: value.value,
|
|
490
|
+
description: value.description
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
function cloneResource(value) {
|
|
495
|
+
return {
|
|
496
|
+
id: value.id,
|
|
497
|
+
type: value.type,
|
|
498
|
+
locator: value.locator,
|
|
499
|
+
description: value.description
|
|
500
|
+
};
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
function cloneAmbiguity(value) {
|
|
504
|
+
return {
|
|
505
|
+
id: value.id,
|
|
506
|
+
kind: value.kind,
|
|
507
|
+
description: value.description,
|
|
508
|
+
options: [...value.options]
|
|
509
|
+
};
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
function cloneSensitivity(value) {
|
|
513
|
+
return {
|
|
514
|
+
level: value.level,
|
|
515
|
+
labels: [...value.labels]
|
|
516
|
+
};
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function cloneProvenance(value) {
|
|
520
|
+
return {
|
|
521
|
+
source: value.source,
|
|
522
|
+
channel: value.channel,
|
|
523
|
+
actorId: value.actorId,
|
|
524
|
+
applicationId: value.applicationId,
|
|
525
|
+
sessionId: value.sessionId,
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
function canonicalRecord(values) {
|
|
530
|
+
return {
|
|
531
|
+
schema: SCHEMA,
|
|
532
|
+
version: VERSION,
|
|
533
|
+
id: values.id,
|
|
534
|
+
createdAt: values.createdAt,
|
|
535
|
+
originalExpression: values.originalExpression,
|
|
536
|
+
normalizedGoal: cloneGoal(values.normalizedGoal),
|
|
537
|
+
constraints: values.constraints.map(cloneConstraint),
|
|
538
|
+
resources: values.resources.map(cloneResource),
|
|
539
|
+
ambiguities: values.ambiguities.map(cloneAmbiguity),
|
|
540
|
+
sensitivity: cloneSensitivity(values.sensitivity),
|
|
541
|
+
provenance: cloneProvenance(values.provenance),
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
function validateAggregate(values) {
|
|
546
|
+
const canonical = canonicalRecord(values);
|
|
547
|
+
const serialized = JSON.stringify(canonical);
|
|
548
|
+
if (byteLength(serialized) > AGGREGATE_MAXIMUM_BYTES) {
|
|
549
|
+
fail('INTENT_ENVELOPE_LIMIT_EXCEEDED', '$', 'Intent envelope exceeds its aggregate size limit.');
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
function normalizeValues(input) {
|
|
554
|
+
const active = new WeakSet();
|
|
555
|
+
const record = plainRecord(input, '$', ROOT_KEYS, active);
|
|
556
|
+
const schema = ownValue(record, 'schema', '$');
|
|
557
|
+
if (schema !== SCHEMA) {
|
|
558
|
+
finishRecord(record, active);
|
|
559
|
+
fail('INTENT_ENVELOPE_UNSUPPORTED_VERSION', 'schema', 'Intent envelope schema is unsupported.');
|
|
560
|
+
}
|
|
561
|
+
const version = ownValue(record, 'version', '$');
|
|
562
|
+
if (version !== VERSION) {
|
|
563
|
+
finishRecord(record, active);
|
|
564
|
+
fail('INTENT_ENVELOPE_UNSUPPORTED_VERSION', 'version', 'Intent envelope version is unsupported.');
|
|
565
|
+
}
|
|
566
|
+
const idValue = ownValue(record, 'id', '$');
|
|
567
|
+
const createdAtValue = ownValue(record, 'createdAt', '$');
|
|
568
|
+
const normalizedGoalValue = ownValue(record, 'normalizedGoal', '$');
|
|
569
|
+
const sensitivityValue = ownValue(record, 'sensitivity', '$');
|
|
570
|
+
const provenanceValue = ownValue(record, 'provenance', '$');
|
|
571
|
+
const values = {
|
|
572
|
+
id: identifier(idValue, 'id'),
|
|
573
|
+
createdAt: normalizeTimestamp(createdAtValue, 'createdAt'),
|
|
574
|
+
originalExpression: boundedString(
|
|
575
|
+
ownValue(record, 'originalExpression', '$'),
|
|
576
|
+
'originalExpression',
|
|
577
|
+
EXPRESSION_MAXIMUM_BYTES,
|
|
578
|
+
),
|
|
579
|
+
normalizedGoal: normalizeGoal(normalizedGoalValue, 'normalizedGoal', active),
|
|
580
|
+
constraints: normalizeList(
|
|
581
|
+
ownValue(record, 'constraints', '$'),
|
|
582
|
+
'constraints',
|
|
583
|
+
CONSTRAINT_MAXIMUM,
|
|
584
|
+
normalizeConstraint,
|
|
585
|
+
active,
|
|
586
|
+
),
|
|
587
|
+
resources: normalizeList(
|
|
588
|
+
ownValue(record, 'resources', '$'),
|
|
589
|
+
'resources',
|
|
590
|
+
RESOURCE_MAXIMUM,
|
|
591
|
+
normalizeResource,
|
|
592
|
+
active,
|
|
593
|
+
),
|
|
594
|
+
ambiguities: normalizeList(
|
|
595
|
+
ownValue(record, 'ambiguities', '$'),
|
|
596
|
+
'ambiguities',
|
|
597
|
+
AMBIGUITY_MAXIMUM,
|
|
598
|
+
normalizeAmbiguity,
|
|
599
|
+
active,
|
|
600
|
+
),
|
|
601
|
+
sensitivity: normalizeSensitivity(sensitivityValue, 'sensitivity', active),
|
|
602
|
+
provenance: normalizeProvenance(provenanceValue, 'provenance', active)
|
|
603
|
+
};
|
|
604
|
+
finishRecord(record, active);
|
|
605
|
+
validateAggregate(values);
|
|
606
|
+
return values;
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
function normalizeCreatePayload(payload, trustedContext) {
|
|
610
|
+
const active = new WeakSet();
|
|
611
|
+
const source = plainRecord(payload, 'payload', PAYLOAD_KEYS, active);
|
|
612
|
+
const context = plainRecord(trustedContext, 'trustedContext', TRUSTED_CONTEXT_KEYS, active);
|
|
613
|
+
const canonical = {
|
|
614
|
+
schema: SCHEMA,
|
|
615
|
+
version: VERSION,
|
|
616
|
+
id: ownValue(context, 'id', 'trustedContext'),
|
|
617
|
+
createdAt: normalizeTrustedTimestamp(
|
|
618
|
+
ownValue(context, 'createdAt', 'trustedContext'),
|
|
619
|
+
'createdAt'
|
|
620
|
+
),
|
|
621
|
+
originalExpression: ownValue(source, 'originalExpression', 'payload'),
|
|
622
|
+
normalizedGoal: ownValue(
|
|
623
|
+
source,
|
|
624
|
+
'normalizedGoal',
|
|
625
|
+
'payload',
|
|
626
|
+
{
|
|
627
|
+
required: false,
|
|
628
|
+
defaultValue: null
|
|
629
|
+
}
|
|
630
|
+
),
|
|
631
|
+
constraints: ownValue(
|
|
632
|
+
source,
|
|
633
|
+
'constraints',
|
|
634
|
+
'payload',
|
|
635
|
+
{
|
|
636
|
+
required: false,
|
|
637
|
+
defaultValue: []
|
|
638
|
+
}
|
|
639
|
+
),
|
|
640
|
+
resources: ownValue(
|
|
641
|
+
source,
|
|
642
|
+
'resources',
|
|
643
|
+
'payload',
|
|
644
|
+
{
|
|
645
|
+
required: false,
|
|
646
|
+
defaultValue: []
|
|
647
|
+
}
|
|
648
|
+
),
|
|
649
|
+
ambiguities: ownValue(
|
|
650
|
+
source,
|
|
651
|
+
'ambiguities',
|
|
652
|
+
'payload',
|
|
653
|
+
{
|
|
654
|
+
required: false,
|
|
655
|
+
defaultValue: []
|
|
656
|
+
}
|
|
657
|
+
),
|
|
658
|
+
sensitivity: ownValue(
|
|
659
|
+
source,
|
|
660
|
+
'sensitivity',
|
|
661
|
+
'payload',
|
|
662
|
+
{
|
|
663
|
+
required: false,
|
|
664
|
+
defaultValue: {
|
|
665
|
+
level: 'unknown',
|
|
666
|
+
labels: []
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
),
|
|
670
|
+
provenance: {
|
|
671
|
+
source: ownValue(context, 'source', 'trustedContext'),
|
|
672
|
+
channel: ownValue(context, 'channel', 'trustedContext'),
|
|
673
|
+
actorId: ownValue(context, 'actorId', 'trustedContext'),
|
|
674
|
+
applicationId: ownValue(context, 'applicationId', 'trustedContext'),
|
|
675
|
+
sessionId: ownValue(context, 'sessionId', 'trustedContext'),
|
|
676
|
+
},
|
|
677
|
+
};
|
|
678
|
+
finishRecord(context, active);
|
|
679
|
+
finishRecord(source, active);
|
|
680
|
+
return canonical;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function auditConstraint(value) {
|
|
684
|
+
const valueType = value.value === null ? 'null' : typeof value.value;
|
|
685
|
+
return {
|
|
686
|
+
id: value.id,
|
|
687
|
+
kind: value.kind,
|
|
688
|
+
valueType
|
|
689
|
+
};
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
function auditResource(value) {
|
|
693
|
+
return {
|
|
694
|
+
id: value.id,
|
|
695
|
+
type: value.type
|
|
696
|
+
};
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
function auditAmbiguity(value) {
|
|
700
|
+
return {
|
|
701
|
+
id: value.id,
|
|
702
|
+
kind: value.kind,
|
|
703
|
+
optionCount: value.options.length
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
class IntentEnvelope {
|
|
708
|
+
constructor(token, values) {
|
|
709
|
+
if (token !== constructionToken) {
|
|
710
|
+
fail('INTENT_ENVELOPE_INVALID', '$', 'Intent envelopes must be created through the public factory.');
|
|
711
|
+
}
|
|
712
|
+
this.schema = SCHEMA;
|
|
713
|
+
this.version = VERSION;
|
|
714
|
+
this.id = values.id;
|
|
715
|
+
this.createdAt = values.createdAt;
|
|
716
|
+
this.originalExpression = values.originalExpression;
|
|
717
|
+
this.normalizedGoal = values.normalizedGoal;
|
|
718
|
+
this.constraints = values.constraints;
|
|
719
|
+
this.resources = values.resources;
|
|
720
|
+
this.ambiguities = values.ambiguities;
|
|
721
|
+
this.sensitivity = values.sensitivity;
|
|
722
|
+
this.provenance = values.provenance;
|
|
723
|
+
Object.freeze(this);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
toJSON() {
|
|
727
|
+
return canonicalRecord(this);
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
function buildEnvelope(canonical) {
|
|
732
|
+
const values = normalizeValues(canonical);
|
|
733
|
+
return new IntentEnvelope(constructionToken, values);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
export function createIntentEnvelope(payload, trustedContext) {
|
|
737
|
+
const canonical = normalizeCreatePayload(payload, trustedContext);
|
|
738
|
+
return buildEnvelope(canonical);
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
export function rehydrateIntentEnvelope(canonical) {
|
|
742
|
+
if (canonical instanceof IntentEnvelope) {
|
|
743
|
+
return canonical;
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
if (typeof canonical === 'string') {
|
|
747
|
+
try {
|
|
748
|
+
const parsed = JSON.parse(canonical);
|
|
749
|
+
return buildEnvelope(parsed);
|
|
750
|
+
} catch (error) {
|
|
751
|
+
if (error instanceof IntentEnvelopeValidationError) {
|
|
752
|
+
throw error;
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
fail(
|
|
756
|
+
'INTENT_ENVELOPE_INVALID',
|
|
757
|
+
'$',
|
|
758
|
+
'Intent envelope JSON is invalid.'
|
|
759
|
+
);
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
return buildEnvelope(canonical);
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
export function serializeIntentEnvelope(envelope) {
|
|
766
|
+
const hydrated = rehydrateIntentEnvelope(envelope);
|
|
767
|
+
return JSON.stringify(hydrated);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
export function intentEnvelopeAuditProjection(envelope) {
|
|
771
|
+
const value = rehydrateIntentEnvelope(envelope);
|
|
772
|
+
const projection = {
|
|
773
|
+
schema: value.schema,
|
|
774
|
+
version: value.version,
|
|
775
|
+
id: value.id,
|
|
776
|
+
createdAt: value.createdAt,
|
|
777
|
+
constraints: value.constraints.map(auditConstraint),
|
|
778
|
+
resources: value.resources.map(auditResource),
|
|
779
|
+
ambiguities: value.ambiguities.map(auditAmbiguity),
|
|
780
|
+
sensitivity: cloneSensitivity(value.sensitivity),
|
|
781
|
+
provenance: cloneProvenance(value.provenance),
|
|
782
|
+
};
|
|
783
|
+
const frozenConstraints = projection.constraints.map(Object.freeze);
|
|
784
|
+
const frozenResources = projection.resources.map(Object.freeze);
|
|
785
|
+
const frozenAmbiguities = projection.ambiguities.map(Object.freeze);
|
|
786
|
+
const frozenSensitivity = {
|
|
787
|
+
...projection.sensitivity,
|
|
788
|
+
labels: Object.freeze(projection.sensitivity.labels)
|
|
789
|
+
};
|
|
790
|
+
const frozenProjection = {
|
|
791
|
+
...projection,
|
|
792
|
+
constraints: Object.freeze(frozenConstraints),
|
|
793
|
+
resources: Object.freeze(frozenResources),
|
|
794
|
+
ambiguities: Object.freeze(frozenAmbiguities),
|
|
795
|
+
sensitivity: Object.freeze(frozenSensitivity),
|
|
796
|
+
provenance: Object.freeze(projection.provenance)
|
|
797
|
+
};
|
|
798
|
+
return Object.freeze(frozenProjection);
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
const intentEnvelopeLimits = {
|
|
802
|
+
aggregateBytes: AGGREGATE_MAXIMUM_BYTES,
|
|
803
|
+
ambiguityCount: AMBIGUITY_MAXIMUM,
|
|
804
|
+
constraintCount: CONSTRAINT_MAXIMUM,
|
|
805
|
+
descriptionBytes: DESCRIPTION_MAXIMUM_BYTES,
|
|
806
|
+
expressionBytes: EXPRESSION_MAXIMUM_BYTES,
|
|
807
|
+
goalBytes: GOAL_MAXIMUM_BYTES,
|
|
808
|
+
identifierCharacters: IDENTIFIER_MAXIMUM_CHARACTERS,
|
|
809
|
+
labelBytes: LABEL_MAXIMUM_BYTES,
|
|
810
|
+
optionBytes: OPTION_MAXIMUM_BYTES,
|
|
811
|
+
optionCount: OPTION_MAXIMUM,
|
|
812
|
+
resourceCount: RESOURCE_MAXIMUM,
|
|
813
|
+
resourceLocatorBytes: RESOURCE_LOCATOR_MAXIMUM_BYTES,
|
|
814
|
+
sensitivityLabelCount: SENSITIVITY_LABEL_MAXIMUM
|
|
815
|
+
};
|
|
816
|
+
const ambiguityKinds = [...AMBIGUITY_KINDS];
|
|
817
|
+
const constraintKinds = [...CONSTRAINT_KINDS];
|
|
818
|
+
const provenanceChannels = [...PROVENANCE_CHANNELS];
|
|
819
|
+
const provenanceSources = [...PROVENANCE_SOURCES];
|
|
820
|
+
const resourceTypes = [...RESOURCE_TYPES];
|
|
821
|
+
const sensitivityLevels = [...SENSITIVITY_LEVELS];
|
|
822
|
+
const intentEnvelopeContractValue = {
|
|
823
|
+
schema: SCHEMA,
|
|
824
|
+
version: VERSION,
|
|
825
|
+
limits: Object.freeze(intentEnvelopeLimits),
|
|
826
|
+
ambiguityKinds: Object.freeze(ambiguityKinds),
|
|
827
|
+
constraintKinds: Object.freeze(constraintKinds),
|
|
828
|
+
provenanceChannels: Object.freeze(provenanceChannels),
|
|
829
|
+
provenanceSources: Object.freeze(provenanceSources),
|
|
830
|
+
resourceTypes: Object.freeze(resourceTypes),
|
|
831
|
+
sensitivityLevels: Object.freeze(sensitivityLevels)
|
|
832
|
+
};
|
|
833
|
+
|
|
834
|
+
export const intentEnvelopeContract = Object.freeze(intentEnvelopeContractValue);
|