arcane-os 0.5.16 → 0.5.18

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 (190) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +42 -19
  3. package/bin/arcane-test.mjs +16 -13
  4. package/browser-runtime/ai/browser-speech-artifacts.mjs +41 -38
  5. package/browser-runtime/ai/browser-speech-providers.mjs +53 -46
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +127 -124
  7. package/browser-runtime/ai/browser-wasm.mjs +6 -3
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +26 -23
  9. package/browser-runtime/ai/model-controller.mjs +94 -91
  10. package/browser-runtime/ai/speech-worker-client.mjs +12 -9
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +56 -53
  12. package/browser-runtime/dependencies/strong-type/index.js +207 -82
  13. package/browser-runtime/dependencies/strong-type/package.json +15 -6
  14. package/browser-runtime/dom-event-instrumentation.mjs +24 -20
  15. package/browser-runtime/event-manager.mjs +111 -108
  16. package/browser-runtime/speech-text.mjs +35 -0
  17. package/docs/architecture.md +1 -1
  18. package/docs/reference/README.md +8 -8
  19. package/docs/reference/ai/browser-speech.md +230 -16
  20. package/docs/reference/ai/twin-cloud.md +1 -1
  21. package/docs/reference/availability-and-normalization.md +42 -1
  22. package/docs/reference/cli.md +1 -1
  23. package/docs/reference/core/arcane-ai-contracts.md +1 -1
  24. package/docs/reference/inventory/package-api.json +60 -4
  25. package/docs/reference/inventory/runtime-components.json +2 -2
  26. package/docs/reference/inventory/runtime-modules.json +29 -31
  27. package/docs/reference/protocols.md +17 -13
  28. package/docs/reference/runtime-components.md +6 -5
  29. package/docs/reference/runtime-modules.md +212 -70
  30. package/docs/reference/sdk-api.md +197 -11
  31. package/examples/wasm-ai-demo/index.html +1 -0
  32. package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
  33. package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
  34. package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
  35. package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
  36. package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
  37. package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
  38. package/node_modules/strong-type/README.md +42 -24
  39. package/node_modules/strong-type/benchmark/core.js +324 -0
  40. package/node_modules/strong-type/index.js +207 -82
  41. package/node_modules/strong-type/package.json +14 -6
  42. package/package.json +4 -3
  43. package/runtime/arcane/components/app-bar.html +5 -2
  44. package/runtime/arcane/components/assistant-panel.html +5 -2
  45. package/runtime/arcane/components/chart.html +15 -12
  46. package/runtime/arcane/components/chat.html +118 -141
  47. package/runtime/arcane/components/dashboard-config.html +6 -3
  48. package/runtime/arcane/components/data-view.html +4 -1
  49. package/runtime/arcane/components/directory-picker.html +5 -2
  50. package/runtime/arcane/components/document-inspector.html +6 -3
  51. package/runtime/arcane/components/file-drop.html +5 -2
  52. package/runtime/arcane/components/file-inspector.html +9 -6
  53. package/runtime/arcane/components/file-manager.html +16 -13
  54. package/runtime/arcane/components/local-ai-status.html +5 -2
  55. package/runtime/arcane/components/markdown-document.html +14 -11
  56. package/runtime/arcane/components/markdown-editor.html +4 -1
  57. package/runtime/arcane/components/modal.html +4 -1
  58. package/runtime/arcane/components/output-panel.html +6 -3
  59. package/runtime/arcane/components/preferences-form.html +4 -1
  60. package/runtime/arcane/components/record-timeline.html +8 -5
  61. package/runtime/arcane/components/relationship-board.html +9 -6
  62. package/runtime/arcane/components/source-code-viewer.html +12 -9
  63. package/runtime/arcane/components/source-explanation.html +8 -5
  64. package/runtime/arcane/components/speech.html +24 -21
  65. package/runtime/arcane/components/summary-strip.html +4 -1
  66. package/runtime/arcane/components/task-progress.html +6 -3
  67. package/runtime/arcane/components/terminal-workspace.html +4 -1
  68. package/runtime/arcane/components/voice-transcription.html +11 -8
  69. package/runtime/arcane/components/web-navigator.html +5 -2
  70. package/runtime/arcane/entities/ApiModelRecord.js +5 -2
  71. package/runtime/arcane/entities/Calculation.js +5 -2
  72. package/runtime/arcane/entities/Chat.js +33 -33
  73. package/runtime/arcane/entities/CommunicationMessage.js +4 -1
  74. package/runtime/arcane/entities/CommunicationThread.js +4 -1
  75. package/runtime/arcane/entities/File.js +1 -1
  76. package/runtime/arcane/entities/Image.js +8 -5
  77. package/runtime/arcane/entities/IntentEnvelope.js +20 -17
  78. package/runtime/arcane/entities/Preference.js +9 -6
  79. package/runtime/arcane/entities/Theme.js +5 -2
  80. package/runtime/arcane/entities/User.js +11 -11
  81. package/runtime/arcane/entities/Weather.js +5 -2
  82. package/runtime/arcane/modules/AI.js +503 -187
  83. package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
  84. package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
  85. package/runtime/arcane/modules/AIProviderRuntime.js +117 -92
  86. package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
  87. package/runtime/arcane/modules/AIRuntimeState.js +29 -26
  88. package/runtime/arcane/modules/AnsiText.js +4 -1
  89. package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
  90. package/runtime/arcane/modules/AppDataScope.js +8 -5
  91. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
  92. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
  93. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
  94. package/runtime/arcane/modules/AsyncBoundary.js +13 -10
  95. package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
  96. package/runtime/arcane/modules/CalculatorEngine.js +5 -2
  97. package/runtime/arcane/modules/ChatRecords.js +18 -15
  98. package/runtime/arcane/modules/CommunicationAppController.js +9 -6
  99. package/runtime/arcane/modules/CommunicationHub.js +9 -6
  100. package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
  101. package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
  102. package/runtime/arcane/modules/ComponentContracts.js +56 -53
  103. package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
  104. package/runtime/arcane/modules/ConversationActionItems.js +15 -12
  105. package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
  106. package/runtime/arcane/modules/ConversationTimebox.js +28 -25
  107. package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
  108. package/runtime/arcane/modules/DBLS.js +7 -4
  109. package/runtime/arcane/modules/DBOPFS.js +7 -7
  110. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
  111. package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
  112. package/runtime/arcane/modules/DirectoryPicker.js +8 -5
  113. package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
  114. package/runtime/arcane/modules/DocumentNavigation.js +7 -4
  115. package/runtime/arcane/modules/Errors.js +28 -25
  116. package/runtime/arcane/modules/HTMLImport.js +7 -4
  117. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
  118. package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
  119. package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
  120. package/runtime/arcane/modules/MD.js +1 -1
  121. package/runtime/arcane/modules/Mail.js +46 -43
  122. package/runtime/arcane/modules/MailOutbox.mjs +48 -45
  123. package/runtime/arcane/modules/MailTransport.mjs +29 -26
  124. package/runtime/arcane/modules/MarkdownSpeech.js +1 -50
  125. package/runtime/arcane/modules/MemoryRecords.js +7 -4
  126. package/runtime/arcane/modules/MessageAdvisory.js +6 -3
  127. package/runtime/arcane/modules/ModelDefinition.js +7 -4
  128. package/runtime/arcane/modules/Ollama.js +6 -3
  129. package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
  130. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
  131. package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
  132. package/runtime/arcane/modules/PreferenceStore.js +20 -17
  133. package/runtime/arcane/modules/PreparedSpeech.js +485 -0
  134. package/runtime/arcane/modules/Questionnaire.js +6 -3
  135. package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
  136. package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
  137. package/runtime/arcane/modules/RecordReviewStore.js +12 -9
  138. package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
  139. package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
  140. package/runtime/arcane/modules/ScreenCapture.js +20 -17
  141. package/runtime/arcane/modules/SpeechPlayback.js +61 -28
  142. package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
  143. package/runtime/arcane/modules/SystemAppearance.js +5 -2
  144. package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
  145. package/runtime/arcane/modules/TerminalClient.js +14 -11
  146. package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
  147. package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
  148. package/runtime/arcane/modules/ThemeManager.js +5 -2
  149. package/runtime/arcane/modules/TimeGuard.js +1 -1
  150. package/runtime/arcane/modules/ToolCallRouter.js +11 -8
  151. package/runtime/arcane/modules/WaitForComponent.js +19 -16
  152. package/runtime/arcane/modules/YouTubeMedia.js +4 -1
  153. package/runtime/strong-type/index.js +1276 -352
  154. package/runtime/strong-type/package.json +69 -45
  155. package/src/app-descriptor.mjs +17 -14
  156. package/src/application-tests.mjs +4 -1
  157. package/src/cli/main.mjs +8 -5
  158. package/src/constants.mjs +4 -1
  159. package/src/dev-server.mjs +8 -5
  160. package/src/doctor.mjs +5 -2
  161. package/src/dom-event-instrumentation.mjs +24 -20
  162. package/src/errors.mjs +7 -3
  163. package/src/event-manager.mjs +111 -108
  164. package/src/event-queue.mjs +8 -5
  165. package/src/events.mjs +12 -9
  166. package/src/import-map.mjs +36 -27
  167. package/src/installed-sdk-runtime.mjs +4 -1
  168. package/src/integrated-provider-loader.mjs +9 -6
  169. package/src/mail-credentials.mjs +16 -13
  170. package/src/mail-server.mjs +53 -50
  171. package/src/mail.mjs +18 -15
  172. package/src/native-plan.mjs +12 -9
  173. package/src/native-provider-loader.mjs +7 -4
  174. package/src/packager/core.mjs +18 -15
  175. package/src/process.mjs +6 -3
  176. package/src/release-bundle.mjs +14 -11
  177. package/src/runtime.mjs +4 -1
  178. package/src/scaffold.mjs +9 -6
  179. package/src/sdk-browser-runtime.mjs +4 -1
  180. package/src/source-server.mjs +17 -14
  181. package/src/targets/index.mjs +5 -2
  182. package/src/templates/workspace-template.mjs +13 -6
  183. package/src/testing-loader.mjs +7 -4
  184. package/src/testing.mjs +10 -7
  185. package/src/toolchain.mjs +13 -10
  186. package/src/update-check.mjs +9 -6
  187. package/src/workspace-operation-lock.mjs +14 -11
  188. package/src/workspace-runtime.mjs +4 -1
  189. package/src/workspace.mjs +17 -14
  190. package/runtime/arcane/modules/AIResponseLength.js +0 -32
@@ -1,45 +1,69 @@
1
- {
2
- "name": "strong-type",
3
- "version": "1.1.0",
4
- "type": "module",
5
- "description": "allows easy type enforcement for all JS types objects and classes. it is also extensible and provides simple to use type checks for your own custom classes and types",
6
- "main": "index.js",
7
- "directories": {
8
- "example": "example"
9
- },
10
- "engines": {
11
- "node": ">=12.21.0"
12
- },
13
- "scripts": {
14
- "test": "npm i && c8 -r lcov -r html node test/CI.js && c8 report && node ./lcov.js",
15
- "start": "npm run emulate && node-http-server port=8000 verbose=true",
16
- "emulate": "npm i && copyfiles -V \"./!(node_modules)/*\" \"./**!(node_modules)\" \"./example/node_modules/strong-type/\" && copyfiles -V \"./node_modules/**/*\" \"./example/\" && copyfiles -V \"./!(node_modules)/*\" \"./**!(node_modules)\"",
17
- "nodeExample": "node ./example/node/typecheck.js",
18
- "coverage": "echo 'See your coverage report at http://localhost:8080' && node-http-server port=8080 root=./coverage/"
19
- },
20
- "repository": {
21
- "type": "git",
22
- "url": "git+https://github.com/RIAEvangelist/strong-type.git"
23
- },
24
- "keywords": [
25
- "strong",
26
- "type",
27
- "validation",
28
- "validate",
29
- "check",
30
- "checking"
31
- ],
32
- "author": "Brandon Nozaki Miller",
33
- "license": "MIT",
34
- "bugs": {
35
- "url": "https://github.com/RIAEvangelist/strong-type/issues"
36
- },
37
- "homepage": "https://github.com/RIAEvangelist/strong-type#readme",
38
- "devDependencies": {
39
- "c8": "^7.6.0",
40
- "copyfiles": "*",
41
- "lcov2badge": "^0.1.2",
42
- "node-http-server": "^8.1.3",
43
- "vanilla-test": "*"
44
- }
45
- }
1
+ {
2
+ "name": "strong-type",
3
+ "version": "2.0.1",
4
+ "type": "module",
5
+ "description": "Dependency-free native ESM type enforcement for JavaScript values, objects, classes, browsers, and Node.",
6
+ "main": "index.js",
7
+ "exports": {
8
+ ".": "./index.js",
9
+ "./index.js": "./index.js",
10
+ "./node": "./node.js",
11
+ "./node.js": "./node.js",
12
+ "./package.json": "./package.json"
13
+ },
14
+ "files": [
15
+ "index.js",
16
+ "node.js",
17
+ "benchmark/core.js",
18
+ "assets/strong-type-header.png",
19
+ "README.md",
20
+ "licence"
21
+ ],
22
+ "sideEffects": false,
23
+ "directories": {
24
+ "example": "example"
25
+ },
26
+ "engines": {
27
+ "node": ">=12.21.0"
28
+ },
29
+ "scripts": {
30
+ "benchmark": "node benchmark/core.js",
31
+ "benchmark:baseline": "node benchmark/core.js --baseline",
32
+ "benchmark:historical": "node benchmark/core.js --historical",
33
+ "test": "node scripts/test.js",
34
+ "test:unit": "node scripts/test.js unit",
35
+ "test:functional": "node scripts/test.js functional",
36
+ "test:integration": "node scripts/test.js integration",
37
+ "test:regression": "node scripts/test.js regression",
38
+ "test:legacy": "node scripts/test.js legacy",
39
+ "coverage": "vanilla-test coverage node",
40
+ "start": "node scripts/serve.js",
41
+ "nodeExample": "node ./example/node/typecheck.js",
42
+ "check": "npm test"
43
+ },
44
+ "repository": {
45
+ "type": "git",
46
+ "url": "git+https://github.com/RIAEvangelist/strong-type.git"
47
+ },
48
+ "keywords": [
49
+ "strong",
50
+ "type",
51
+ "validation",
52
+ "validate",
53
+ "check",
54
+ "checking",
55
+ "esm",
56
+ "isomorphic",
57
+ "browser",
58
+ "node"
59
+ ],
60
+ "author": "Roshi _ _",
61
+ "license": "MIT",
62
+ "bugs": {
63
+ "url": "https://github.com/RIAEvangelist/strong-type/issues"
64
+ },
65
+ "homepage": "https://riaevangelist.github.io/strong-type/",
66
+ "devDependencies": {
67
+ "vanilla-test": "2.1.0"
68
+ }
69
+ }
@@ -1,3 +1,4 @@
1
+ import Is from 'strong-type';
1
2
  import {isDeepStrictEqual} from 'node:util';
2
3
  import {readFile} from 'node:fs/promises';
3
4
  import path from 'node:path';
@@ -12,6 +13,8 @@ import {
12
13
  validateAppConfig as validateAppPackageConfig
13
14
  } from './packager/core.mjs';
14
15
 
16
+ const is = new Is(false);
17
+
15
18
  export const APP_DESCRIPTOR_NAME='arcane-app.json';
16
19
  export const APP_DESCRIPTOR_SCHEMA_VERSION=2;
17
20
 
@@ -35,7 +38,7 @@ function fail(message){
35
38
  }
36
39
 
37
40
  function isObject(value){
38
- return value!==null&&typeof value==='object'&&!Array.isArray(value);
41
+ return value!==null&&is.object(value)&&!is.array(value);
39
42
  }
40
43
 
41
44
  function assertOnlyKeys(value,allowed,label){
@@ -46,7 +49,7 @@ function assertOnlyKeys(value,allowed,label){
46
49
  }
47
50
 
48
51
  function safeText(value,label){
49
- if(typeof value!=='string'||value!==value.trim()||!value
52
+ if(!is.string(value)||value!==value.trim()||!value
50
53
  ||/[<>\u0000-\u001f\u007f]/u.test(value)){
51
54
  fail(`${label} must be nonempty, trimmed presentation text.`);
52
55
  }
@@ -54,11 +57,11 @@ function safeText(value,label){
54
57
  }
55
58
 
56
59
  function uniqueSortedStrings(value,label,{pattern,required=false}={}){
57
- if(!Array.isArray(value)||(required&&value.length===0)){
60
+ if(!is.array(value)||(required&&value.length===0)){
58
61
  fail(`${label} must be ${required?'a nonempty':'an'} array.`);
59
62
  }
60
63
  const normalized=value.map((entry,index)=>{
61
- if(typeof entry!=='string'||entry!==entry.trim()||!entry
64
+ if(!is.string(entry)||entry!==entry.trim()||!entry
62
65
  ||(pattern&&!pattern.test(entry))){
63
66
  fail(`${label}[${index}] is invalid.`);
64
67
  }
@@ -68,7 +71,7 @@ function uniqueSortedStrings(value,label,{pattern,required=false}={}){
68
71
  }
69
72
 
70
73
  function relativePaths(value,label,{required=false}={}){
71
- if(!Array.isArray(value)||(required&&value.length===0)){
74
+ if(!is.array(value)||(required&&value.length===0)){
72
75
  fail(`${label} must be ${required?'a nonempty':'an'} array.`);
73
76
  }
74
77
  const normalized=value.map((entry,index)=>normalizeRelativePath(entry,`${label}[${index}]`));
@@ -76,9 +79,9 @@ function relativePaths(value,label,{required=false}={}){
76
79
  }
77
80
 
78
81
  function validateOrigins(value,label){
79
- if(!Array.isArray(value))fail(`${label} must be an array of origins.`);
82
+ if(!is.array(value))fail(`${label} must be an array of origins.`);
80
83
  const origins=value.map((origin,index)=>{
81
- if(typeof origin!=='string'||!origin.trim())fail(`${label}[${index}] is invalid.`);
84
+ if(!is.string(origin)||!origin.trim())fail(`${label}[${index}] is invalid.`);
82
85
  return origin;
83
86
  });
84
87
  return completeValue(origins);
@@ -87,14 +90,14 @@ function validateOrigins(value,label){
87
90
  function validateLocalAIModelPolicy(value,label){
88
91
  if(value===undefined)return undefined;
89
92
  assertOnlyKeys(value,new Set(['verified_only','models']),label);
90
- if(typeof value.verified_only!=='boolean'||!Array.isArray(value.models)){
93
+ if(!is.boolean(value.verified_only)||!is.array(value.models)){
91
94
  fail(`${label} is invalid.`);
92
95
  }
93
96
  const models=value.models.map((model,index)=>{
94
97
  const itemLabel=`${label}.models[${index}]`;
95
98
  assertOnlyKeys(model,new Set(['name','definition']),itemLabel);
96
- if(typeof model.name!=='string'||!model.name
97
- ||typeof model.definition!=='string'||!model.definition.endsWith('Modelfile')){
99
+ if(!is.string(model.name)||!model.name
100
+ ||!is.string(model.definition)||!model.definition.endsWith('Modelfile')){
98
101
  fail(`${itemLabel} is invalid.`);
99
102
  }
100
103
  return completeValue({name:model.name,definition:normalizeRelativePath(model.definition,`${itemLabel}.definition`)});
@@ -144,7 +147,7 @@ function validatePackage(value,appId){
144
147
 
145
148
  function validatePublisher(value){
146
149
  assertOnlyKeys(value,new Set(['id','name']),'descriptor.publisher');
147
- if(typeof value.id!=='string'||!PUBLISHER_PATTERN.test(value.id)){
150
+ if(!is.string(value.id)||!PUBLISHER_PATTERN.test(value.id)){
148
151
  fail('descriptor.publisher.id must be a lowercase publisher identifier.');
149
152
  }
150
153
  return completeValue({id:value.id,name:safeText(value.name,'descriptor.publisher.name')});
@@ -191,7 +194,7 @@ function validateDocumentCatalog(value){
191
194
  assertOnlyKeys(value,new Set([
192
195
  'policy','release','destination','originals','manifest','expectedCount'
193
196
  ]),label);
194
- if(value.policy!=='public-only'||!Number.isInteger(value.expectedCount)
197
+ if(value.policy!=='public-only'||!is.integer(value.expectedCount)
195
198
  ||value.expectedCount<1){
196
199
  fail(`${label} has unsupported policy or expectedCount.`);
197
200
  }
@@ -219,7 +222,7 @@ function validateNative(value,appId){
219
222
  assertOnlyKeys(value,new Set(['type','icon','order','bundledApps','documentCatalog']),'descriptor.native');
220
223
  if(!NATIVE_TYPES.has(value.type))fail('descriptor.native.type is unsupported.');
221
224
  const icon=value.icon===null?null:normalizeRelativePath(value.icon,'descriptor.native.icon');
222
- if(!Number.isInteger(value.order)||value.order<0){
225
+ if(!is.integer(value.order)||value.order<0){
223
226
  fail('descriptor.native.order must be a nonnegative integer.');
224
227
  }
225
228
  const bundledApps=uniqueSortedStrings(value.bundledApps,'descriptor.native.bundledApps',{
@@ -392,7 +395,7 @@ function synthesizedDescriptor(packageManifest,nativeDescriptor){
392
395
  native:{
393
396
  type:native.type??'app',
394
397
  icon:native.icon??null,
395
- order:Number.isInteger(native.order)?native.order:100,
398
+ order:is.integer(native.order)?native.order:100,
396
399
  bundledApps:[...(native.bundledApps??[])],
397
400
  ...(native.documentCatalog?{documentCatalog:native.documentCatalog}:{})
398
401
  },
@@ -1,14 +1,17 @@
1
+ import Is from 'strong-type';
1
2
  import {lstat,readdir,realpath} from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import {fileURLToPath} from 'node:url';
4
5
  import {ArcaneError,ERROR_CODES,throwIfAborted} from './errors.mjs';
5
6
  import {runProcess} from './process.mjs';
6
7
 
8
+ const is = new Is(false);
9
+
7
10
  const TEST_RUNNER_PATH=fileURLToPath(new URL('../bin/arcane-test.mjs',import.meta.url));
8
11
  const MANAGED_IMPORT_MAP_ENV='ARCANE_TEST_IMPORT_MAP_CONTEXT';
9
12
 
10
13
  function normalizedTestOutput(value){
11
- return typeof value==='string'?value:'';
14
+ return is.string(value)?value:'';
12
15
  }
13
16
 
14
17
  function captureFailedTest(testFile,result,workspaceRoot){
package/src/cli/main.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import Is from 'strong-type';
1
2
  import path from 'node:path';
2
3
  import {createReporter} from '../events.mjs';
3
4
  import {executeOperation} from '../toolchain.mjs';
@@ -6,6 +7,8 @@ import {CLI_NAME,SDK_NAME,SDK_VERSION,OUTPUT_MODES} from '../constants.mjs';
6
7
  import {loadArcaneNativeProvider} from '../native-provider-loader.mjs';
7
8
  import {APP_BUNDLE_EXTENSION} from '../release-bundle.mjs';
8
9
 
10
+ const is = new Is(false);
11
+
9
12
  const VALUE_OPTIONS=new Set([
10
13
  'path',
11
14
  'display-name',
@@ -169,7 +172,7 @@ function readPort(value,defaultValue){
169
172
  usage(`Invalid port: ${value}.`);
170
173
  }
171
174
  const port=Number(value);
172
- if(!Number.isSafeInteger(port)||port<0||port>65535){
175
+ if(!is.safeInteger(port)||port<0||port>65535){
173
176
  usage(`Invalid port: ${value}.`);
174
177
  }
175
178
  return port;
@@ -181,7 +184,7 @@ function readRequestTimeout(value){
181
184
  usage(`Invalid request timeout: ${value}.`);
182
185
  }
183
186
  const timeout=Number(value);
184
- if(!Number.isSafeInteger(timeout)||timeout<1||timeout>MAX_NODE_TIMER_DELAY_MS){
187
+ if(!is.safeInteger(timeout)||timeout<1||timeout>MAX_NODE_TIMER_DELAY_MS){
185
188
  usage(
186
189
  `Mail request timeout must be an integer from 1 through ${MAX_NODE_TIMER_DELAY_MS} `
187
190
  +'milliseconds, the Node timer range.'
@@ -253,7 +256,7 @@ function readPipedMailSecret(input,signal){
253
256
  }
254
257
 
255
258
  function readMaskedMailSecret(input,output,signal,label,stdinOption){
256
- if(!input?.isTTY||typeof input.setRawMode!=='function'||typeof output?.write!=='function'){
259
+ if(!input?.isTTY||!is.function(input.setRawMode)||!is.function(output?.write)){
257
260
  usage(`Interactive mail credential entry requires a terminal; use ${stdinOption} for piped input.`);
258
261
  }
259
262
  return new Promise((resolve,reject)=>{
@@ -353,7 +356,7 @@ function readPipedMailReport(input,signal){
353
356
  }catch{
354
357
  usage('Mail report input must be one valid JSON object.');
355
358
  }
356
- if(!report||typeof report!=='object'||Array.isArray(report)){
359
+ if(!report||!is.object(report)||is.array(report)){
357
360
  usage('Mail report input must be one valid JSON object.');
358
361
  }
359
362
  finish(resolve,report);
@@ -839,7 +842,7 @@ function serverSummary(result){
839
842
 
840
843
  async function waitForServer(result,signal,reporter){
841
844
  reporter.emit('server.ready',serverSummary(result),`Development server ready at ${result.url}`);
842
- if(result.lifecycle&&typeof result.lifecycle.then==='function'){
845
+ if(result.lifecycle&&is.function(result.lifecycle.then)){
843
846
  const abort=()=>{
844
847
  void Promise.resolve().then(()=>result.close?.()).catch(()=>{});
845
848
  };
package/src/constants.mjs CHANGED
@@ -1,8 +1,11 @@
1
+ import Is from 'strong-type';
1
2
  import path from 'node:path';
2
3
  import {fileURLToPath} from 'node:url';
3
4
  import packageDocument from '../package.json' with {type:'json'};
4
5
 
5
- if(packageDocument.name!=='arcane-os'||typeof packageDocument.version!=='string'
6
+ const is = new Is(false);
7
+
8
+ if(packageDocument.name!=='arcane-os'||!is.string(packageDocument.version)
6
9
  ||!/^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$/u.test(packageDocument.version)){
7
10
  throw new Error('The Arcane SDK package identity is invalid.');
8
11
  }
@@ -1,3 +1,4 @@
1
+ import Is from 'strong-type';
1
2
  import {constants as FS_CONSTANTS} from 'node:fs';
2
3
  import {lstat,open,readFile,realpath} from 'node:fs/promises';
3
4
  import http from 'node:http';
@@ -9,6 +10,8 @@ import {
9
10
  readWorkspaceAssetVersion,rewriteAssetReferences
10
11
  } from './import-map.mjs';
11
12
 
13
+ const is = new Is(false);
14
+
12
15
  const MIME_TYPES=new Map([
13
16
  ['.css','text/css; charset=utf-8'],
14
17
  ['.gif','image/gif'],
@@ -165,12 +168,12 @@ function sdkBrowserSourcePathAllowed(relative){
165
168
  }
166
169
 
167
170
  async function emitRuntimeSourceEvent(onEvent,event){
168
- if(typeof onEvent==='function')await onEvent(event);
171
+ if(is.function(onEvent))await onEvent(event);
169
172
  }
170
173
 
171
174
  async function verifySdkRuntimeSourceRoot(sourceRoot,workspaceRoot,appId,{signal,onEvent}={}){
172
175
  throwIfAborted(signal);
173
- if(typeof sourceRoot!=='string'||!sourceRoot.trim()){
176
+ if(!is.string(sourceRoot)||!sourceRoot.trim()){
174
177
  fail('sdkRuntimeSourceRoot must name an Arcane SDK directory.',
175
178
  'ARCANE_DEV_RUNTIME_SOURCE_INVALID');
176
179
  }
@@ -449,7 +452,7 @@ async function sourceRoutes(workspaceRoot,appId,{
449
452
  }
450
453
 
451
454
  async function packagedRoutes(releaseRoot){
452
- if(typeof releaseRoot!=='string'||!releaseRoot.trim())fail('releaseRoot is required in packaged mode.','ARCANE_USAGE');
455
+ if(!is.string(releaseRoot)||!releaseRoot.trim())fail('releaseRoot is required in packaged mode.','ARCANE_USAGE');
453
456
  const requested=path.resolve(releaseRoot);
454
457
  const canonical=await canonicalRealDirectory(requested,'Packaged release root');
455
458
  return {
@@ -504,7 +507,7 @@ async function startOwnedDevServer({
504
507
  if(host!=='127.0.0.1'&&host!=='::1'){
505
508
  fail('Development server host must be a numeric loopback address (127.0.0.1 or ::1).','ARCANE_POLICY_DENIED');
506
509
  }
507
- if(!Number.isInteger(port)||port<0||port>65535)fail('port must be an integer from 0 through 65535.','ARCANE_USAGE');
510
+ if(!is.integer(port)||port<0||port>65535)fail('port must be an integer from 0 through 65535.','ARCANE_USAGE');
508
511
  const requestedRuntimeMode=mode==='source'&&sdkRuntimeSourceRoot!==undefined
509
512
  ?'sdk-source'
510
513
  :null;
@@ -624,7 +627,7 @@ async function startOwnedDevServer({
624
627
  });
625
628
  await listen(server,{host,port,signal});
626
629
  const address=server.address();
627
- if(!address||typeof address==='string'){
630
+ if(!address||is.string(address)){
628
631
  server.close();
629
632
  fail('Development server did not expose a TCP address.');
630
633
  }
package/src/doctor.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import Is from 'strong-type';
1
2
  import path from 'node:path';
2
3
  import {access,lstat,readdir,realpath} from 'node:fs/promises';
3
4
  import {loadRuntimeRelease} from './runtime.mjs';
@@ -7,6 +8,8 @@ import {runProcess} from './process.mjs';
7
8
  import {ARCANE_PROTOCOL,SDK_VERSION} from './constants.mjs';
8
9
  import {ERROR_CODES,ArcaneError,throwIfAborted} from './errors.mjs';
9
10
 
11
+ const is = new Is(false);
12
+
10
13
  const WINDOWS_SERVICE_NAME='ArcaneOllama';
11
14
  const WINDOWS_SERVICE_HOST='C:\\Program Files\\Ollama\\ArcaneOllamaService.exe';
12
15
 
@@ -47,8 +50,8 @@ function parseProbe(text){
47
50
  }catch{
48
51
  return null;
49
52
  }
50
- if(!value||typeof value!=='object'||Array.isArray(value)
51
- ||Object.getPrototypeOf(value)!==Object.prototype||typeof value.ready!=='boolean'){
53
+ if(!value||!is.object(value)||is.array(value)
54
+ ||Object.getPrototypeOf(value)!==Object.prototype||!is.boolean(value.ready)){
52
55
  return null;
53
56
  }
54
57
  return {...value};
@@ -1,3 +1,7 @@
1
+ import Is from 'strong-type';
2
+
3
+ const is = new Is(false);
4
+
1
5
  export const DOM_INTERACTION_EVENT='arcane.dom.interaction';
2
6
  export const DOM_MUTATION_EVENT='arcane.dom.mutation';
3
7
  export const DOM_OBSERVATION_STARTED_EVENT='arcane.dom.observation.started';
@@ -25,13 +29,13 @@ function capturedString(value){
25
29
  }
26
30
 
27
31
  function cssEscape(value){
28
- if(typeof globalThis.CSS?.escape==='function')return globalThis.CSS.escape(value);
32
+ if(is.function(globalThis.CSS?.escape))return globalThis.CSS.escape(value);
29
33
  return String(value).replaceAll(/[^a-zA-Z0-9_-]/gu,character=>`\\${character.codePointAt(0).toString(16)} `);
30
34
  }
31
35
 
32
36
  function readAttribute(target,name){
33
37
  try{
34
- return typeof target?.getAttribute==='function'?target.getAttribute(name):null;
38
+ return is.function(target?.getAttribute)?target.getAttribute(name):null;
35
39
  }catch{
36
40
  return null;
37
41
  }
@@ -67,7 +71,7 @@ function selectorSegment(target){
67
71
  }
68
72
 
69
73
  export function domSelector(target,root){
70
- if(!target||typeof target!=='object')return null;
74
+ if(!target||!is.object(target))return null;
71
75
  if(target.nodeType===9)return ':document';
72
76
  if(target.nodeType===11)return ':shadow-root';
73
77
  if(target.nodeType===3)return domSelector(target.parentElement??target.parentNode,root);
@@ -111,7 +115,7 @@ export function domSelector(target,root){
111
115
  }
112
116
 
113
117
  export function describeDOMTarget(target,root){
114
- if(!target||typeof target!=='object')return null;
118
+ if(!target||!is.object(target))return null;
115
119
  if(target.nodeType===9){
116
120
  return {kind:'document',selector:':document'};
117
121
  }
@@ -145,7 +149,7 @@ export function describeDOMTarget(target,root){
145
149
  }
146
150
 
147
151
  function targetValue(target){
148
- if(!target||typeof target!=='object')return undefined;
152
+ if(!target||!is.object(target))return undefined;
149
153
  const type=String(target.type??'').toLowerCase();
150
154
  if(type==='checkbox'||type==='radio')return Boolean(target.checked);
151
155
  if(type==='file'){
@@ -156,24 +160,24 @@ function targetValue(target){
156
160
  }
157
161
 
158
162
  function safeEventDetail(value){
159
- if(typeof value==='string')return capturedString(value);
160
- if(value===null||typeof value==='number'||typeof value==='boolean')return value;
163
+ if(is.string(value))return capturedString(value);
164
+ if(value===null||is.number(value)||is.boolean(value))return value;
161
165
  return value;
162
166
  }
163
167
 
164
168
  function interactionRecord(event,root){
165
- const path=typeof event?.composedPath==='function'?event.composedPath():[event?.target];
166
- const target=path.find(item=>item&&typeof item==='object')??event?.target??null;
169
+ const path=is.function(event?.composedPath)?event.composedPath():[event?.target];
170
+ const target=path.find(item=>item&&is.object(item))??event?.target??null;
167
171
  const eventType=String(event?.type??'');
168
172
  const details={};
169
173
  const fields=new Set([
170
174
  ...EVENT_FIELDS,
171
- ...Reflect.ownKeys(event??{}).filter(field=>typeof field==='string')
175
+ ...Reflect.ownKeys(event??{}).filter(field=>is.string(field))
172
176
  ]);
173
177
  for(const field of fields){
174
178
  let value;
175
179
  try{value=event?.[field];}catch{continue;}
176
- if(value!==undefined&&typeof value!=='function'){
180
+ if(value!==undefined&&!is.function(value)){
177
181
  details[field]=safeEventDetail(value);
178
182
  }
179
183
  }
@@ -254,7 +258,7 @@ function collectOpenShadowRoots(root){
254
258
  const roots=[];
255
259
  const candidates=[];
256
260
  try{
257
- if(typeof root?.querySelectorAll==='function')candidates.push(...root.querySelectorAll('*'));
261
+ if(is.function(root?.querySelectorAll))candidates.push(...root.querySelectorAll('*'));
258
262
  }catch{}
259
263
  for(const candidate of candidates){
260
264
  if(candidate?.shadowRoot)roots.push(candidate.shadowRoot,...collectOpenShadowRoots(candidate.shadowRoot));
@@ -270,18 +274,18 @@ export function createDOMInstrumentation({
270
274
  captureMutations=true,
271
275
  observeOpenShadowRoots=true
272
276
  }={}){
273
- if(!eventManager||typeof eventManager.emit!=='function'){
277
+ if(!eventManager||!is.function(eventManager.emit)){
274
278
  throw new TypeError('DOM instrumentation requires an event manager.');
275
279
  }
276
- if(!root||typeof root.addEventListener!=='function'||typeof root.removeEventListener!=='function'){
280
+ if(!root||!is.function(root.addEventListener)||!is.function(root.removeEventListener)){
277
281
  throw new TypeError('DOM instrumentation requires an EventTarget-compatible root.');
278
282
  }
279
- if(!Array.isArray(eventTypes)||eventTypes.some(type=>typeof type!=='string'||!type)){
283
+ if(!is.array(eventTypes)||eventTypes.some(type=>!is.string(type)||!type)){
280
284
  throw new TypeError('DOM event types must be non-empty strings.');
281
285
  }
282
286
  eventTypes=[...new Set(eventTypes)];
283
287
  for(const [name,value] of Object.entries({captureMutations,observeOpenShadowRoots})){
284
- if(typeof value!=='boolean')throw new TypeError(`${name} must be boolean.`);
288
+ if(!is.boolean(value))throw new TypeError(`${name} must be boolean.`);
285
289
  }
286
290
  const observedRoots=new Set();
287
291
  const listenerRegistrations=new Map();
@@ -351,7 +355,7 @@ export function createDOMInstrumentation({
351
355
 
352
356
  const publish=(type,payload,metadata)=>{
353
357
  const before=Number(eventManager.eventCount??0);
354
- if(typeof eventManager.instrument==='function'){
358
+ if(is.function(eventManager.instrument)){
355
359
  eventManager.instrument(type,payload,{source:'dom',...metadata});
356
360
  }else{
357
361
  eventManager.emit(type,payload);
@@ -365,7 +369,7 @@ export function createDOMInstrumentation({
365
369
  const interaction=(event,observedRoot)=>{
366
370
  let path=[];
367
371
  try{
368
- if(typeof event?.composedPath==='function')path=event.composedPath();
372
+ if(is.function(event?.composedPath))path=event.composedPath();
369
373
  }catch{}
370
374
  const outermostObservedRoot=path.reduce(
371
375
  (selected,item)=>observedRoots.has(item)?item:selected,
@@ -446,7 +450,7 @@ export function createDOMInstrumentation({
446
450
  if(active)return api;
447
451
  try{
448
452
  if(captureMutations){
449
- if(typeof MutationObserverImpl!=='function'){
453
+ if(!is.function(MutationObserverImpl)){
450
454
  throw new TypeError('MutationObserver is required when DOM mutation capture is enabled.');
451
455
  }
452
456
  observer=new MutationObserverImpl(mutations);
@@ -473,7 +477,7 @@ export function createDOMInstrumentation({
473
477
  }
474
478
 
475
479
  function stop({emitLifecycle=true}={}){
476
- if(typeof emitLifecycle!=='boolean')throw new TypeError('emitLifecycle must be boolean.');
480
+ if(!is.boolean(emitLifecycle))throw new TypeError('emitLifecycle must be boolean.');
477
481
  if(!active)return api;
478
482
  const shouldEmitLifecycle=emitLifecycle&&lifecycleStarted;
479
483
  if(!emitLifecycle)lifecycleStarted=false;
package/src/errors.mjs CHANGED
@@ -1,3 +1,7 @@
1
+ import Is from 'strong-type';
2
+
3
+ const is = new Is(false);
4
+
1
5
  export const ERROR_CODES={
2
6
  usage:'ARCANE_USAGE',
3
7
  workspaceInvalid:'ARCANE_WORKSPACE_INVALID',
@@ -17,7 +21,7 @@ export class ArcaneError extends Error{
17
21
  this.name='ArcaneError';
18
22
  this.code=code||ERROR_CODES.operationFailed;
19
23
  this.details=details;
20
- this.exitCode=Number.isInteger(exitCode)?exitCode:1;
24
+ this.exitCode=is.integer(exitCode)?exitCode:1;
21
25
  }
22
26
  }
23
27
 
@@ -49,7 +53,7 @@ export function normalizeError(error,fallbackCode=ERROR_CODES.operationFailed){
49
53
  );
50
54
  }
51
55
 
52
- const reportedCode=typeof error?.code==='string'&&/^ARCANE_[A-Z0-9_]+$/u.test(error.code)
56
+ const reportedCode=is.string(error?.code)&&/^ARCANE_[A-Z0-9_]+$/u.test(error.code)
53
57
  ?error.code
54
58
  :fallbackCode;
55
59
 
@@ -59,7 +63,7 @@ export function normalizeError(error,fallbackCode=ERROR_CODES.operationFailed){
59
63
  {
60
64
  cause:error,
61
65
  details:error?.details,
62
- exitCode:Number.isInteger(error?.exitCode)?error.exitCode:undefined
66
+ exitCode:is.integer(error?.exitCode)?error.exitCode:undefined
63
67
  }
64
68
  );
65
69
  }