arcane-os 0.5.17 → 0.5.19

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 (186) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/README.md +9 -9
  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 +46 -43
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +158 -135
  7. package/browser-runtime/ai/browser-wasm.mjs +6 -3
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +104 -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 +5 -1
  17. package/docs/architecture.md +1 -1
  18. package/docs/publishing.md +10 -4
  19. package/docs/reference/ai/browser-speech.md +198 -5
  20. package/docs/reference/ai/browser-wasm.md +29 -6
  21. package/docs/reference/availability-and-normalization.md +40 -0
  22. package/docs/reference/inventory/package-api.json +1 -1
  23. package/docs/reference/inventory/runtime-components.json +1 -1
  24. package/docs/reference/inventory/runtime-modules.json +20 -24
  25. package/docs/reference/protocols.md +1 -1
  26. package/docs/reference/runtime-modules.md +125 -35
  27. package/docs/reference/sdk-api.md +23 -7
  28. package/examples/wasm-ai-demo/index.html +1 -0
  29. package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
  30. package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
  31. package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
  32. package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
  33. package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
  34. package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
  35. package/node_modules/strong-type/README.md +42 -24
  36. package/node_modules/strong-type/benchmark/core.js +324 -0
  37. package/node_modules/strong-type/index.js +207 -82
  38. package/node_modules/strong-type/package.json +14 -6
  39. package/package.json +3 -3
  40. package/runtime/arcane/components/app-bar.html +5 -2
  41. package/runtime/arcane/components/assistant-panel.html +5 -2
  42. package/runtime/arcane/components/chart.html +15 -12
  43. package/runtime/arcane/components/chat.html +170 -148
  44. package/runtime/arcane/components/dashboard-config.html +6 -3
  45. package/runtime/arcane/components/data-view.html +4 -1
  46. package/runtime/arcane/components/directory-picker.html +5 -2
  47. package/runtime/arcane/components/document-inspector.html +6 -3
  48. package/runtime/arcane/components/file-drop.html +5 -2
  49. package/runtime/arcane/components/file-inspector.html +9 -6
  50. package/runtime/arcane/components/file-manager.html +16 -13
  51. package/runtime/arcane/components/local-ai-status.html +5 -2
  52. package/runtime/arcane/components/markdown-document.html +14 -11
  53. package/runtime/arcane/components/markdown-editor.html +4 -1
  54. package/runtime/arcane/components/modal.html +4 -1
  55. package/runtime/arcane/components/output-panel.html +6 -3
  56. package/runtime/arcane/components/preferences-form.html +4 -1
  57. package/runtime/arcane/components/record-timeline.html +8 -5
  58. package/runtime/arcane/components/relationship-board.html +9 -6
  59. package/runtime/arcane/components/source-code-viewer.html +12 -9
  60. package/runtime/arcane/components/source-explanation.html +8 -5
  61. package/runtime/arcane/components/speech.html +24 -21
  62. package/runtime/arcane/components/summary-strip.html +4 -1
  63. package/runtime/arcane/components/task-progress.html +6 -3
  64. package/runtime/arcane/components/terminal-workspace.html +4 -1
  65. package/runtime/arcane/components/voice-transcription.html +11 -8
  66. package/runtime/arcane/components/web-navigator.html +5 -2
  67. package/runtime/arcane/entities/ApiModelRecord.js +5 -2
  68. package/runtime/arcane/entities/Calculation.js +5 -2
  69. package/runtime/arcane/entities/Chat.js +33 -33
  70. package/runtime/arcane/entities/CommunicationMessage.js +4 -1
  71. package/runtime/arcane/entities/CommunicationThread.js +4 -1
  72. package/runtime/arcane/entities/File.js +1 -1
  73. package/runtime/arcane/entities/Image.js +8 -5
  74. package/runtime/arcane/entities/IntentEnvelope.js +20 -17
  75. package/runtime/arcane/entities/Preference.js +9 -6
  76. package/runtime/arcane/entities/Theme.js +5 -2
  77. package/runtime/arcane/entities/User.js +11 -11
  78. package/runtime/arcane/entities/Weather.js +5 -2
  79. package/runtime/arcane/modules/AI.js +491 -165
  80. package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
  81. package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
  82. package/runtime/arcane/modules/AIProviderRuntime.js +88 -85
  83. package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
  84. package/runtime/arcane/modules/AIRuntimeState.js +29 -26
  85. package/runtime/arcane/modules/AnsiText.js +4 -1
  86. package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
  87. package/runtime/arcane/modules/AppDataScope.js +8 -5
  88. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
  89. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
  90. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
  91. package/runtime/arcane/modules/AsyncBoundary.js +13 -10
  92. package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
  93. package/runtime/arcane/modules/CalculatorEngine.js +5 -2
  94. package/runtime/arcane/modules/ChatRecords.js +18 -15
  95. package/runtime/arcane/modules/CommunicationAppController.js +9 -6
  96. package/runtime/arcane/modules/CommunicationHub.js +9 -6
  97. package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
  98. package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
  99. package/runtime/arcane/modules/ComponentContracts.js +56 -53
  100. package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
  101. package/runtime/arcane/modules/ConversationActionItems.js +15 -12
  102. package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
  103. package/runtime/arcane/modules/ConversationTimebox.js +28 -25
  104. package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
  105. package/runtime/arcane/modules/DBLS.js +7 -4
  106. package/runtime/arcane/modules/DBOPFS.js +7 -7
  107. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
  108. package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
  109. package/runtime/arcane/modules/DirectoryPicker.js +8 -5
  110. package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
  111. package/runtime/arcane/modules/DocumentNavigation.js +7 -4
  112. package/runtime/arcane/modules/Errors.js +28 -25
  113. package/runtime/arcane/modules/HTMLImport.js +7 -4
  114. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
  115. package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
  116. package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
  117. package/runtime/arcane/modules/MD.js +1 -1
  118. package/runtime/arcane/modules/Mail.js +46 -43
  119. package/runtime/arcane/modules/MailOutbox.mjs +48 -45
  120. package/runtime/arcane/modules/MailTransport.mjs +29 -26
  121. package/runtime/arcane/modules/MemoryRecords.js +7 -4
  122. package/runtime/arcane/modules/MessageAdvisory.js +6 -3
  123. package/runtime/arcane/modules/ModelDefinition.js +7 -4
  124. package/runtime/arcane/modules/Ollama.js +6 -3
  125. package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
  126. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
  127. package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
  128. package/runtime/arcane/modules/PreferenceStore.js +20 -17
  129. package/runtime/arcane/modules/PreparedSpeech.js +485 -0
  130. package/runtime/arcane/modules/Questionnaire.js +6 -3
  131. package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
  132. package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
  133. package/runtime/arcane/modules/RecordReviewStore.js +12 -9
  134. package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
  135. package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
  136. package/runtime/arcane/modules/ScreenCapture.js +20 -17
  137. package/runtime/arcane/modules/SpeechPlayback.js +29 -26
  138. package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
  139. package/runtime/arcane/modules/SystemAppearance.js +5 -2
  140. package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
  141. package/runtime/arcane/modules/TerminalClient.js +14 -11
  142. package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
  143. package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
  144. package/runtime/arcane/modules/ThemeManager.js +5 -2
  145. package/runtime/arcane/modules/TimeGuard.js +1 -1
  146. package/runtime/arcane/modules/ToolCallRouter.js +11 -8
  147. package/runtime/arcane/modules/WaitForComponent.js +19 -16
  148. package/runtime/arcane/modules/YouTubeMedia.js +4 -1
  149. package/runtime/strong-type/index.js +1276 -352
  150. package/runtime/strong-type/package.json +69 -45
  151. package/src/app-descriptor.mjs +17 -14
  152. package/src/application-tests.mjs +4 -1
  153. package/src/cli/main.mjs +8 -5
  154. package/src/constants.mjs +4 -1
  155. package/src/dev-server.mjs +8 -5
  156. package/src/doctor.mjs +5 -2
  157. package/src/dom-event-instrumentation.mjs +24 -20
  158. package/src/errors.mjs +7 -3
  159. package/src/event-manager.mjs +111 -108
  160. package/src/event-queue.mjs +8 -5
  161. package/src/events.mjs +12 -9
  162. package/src/import-map.mjs +35 -27
  163. package/src/installed-sdk-runtime.mjs +4 -1
  164. package/src/integrated-provider-loader.mjs +9 -6
  165. package/src/mail-credentials.mjs +16 -13
  166. package/src/mail-server.mjs +53 -50
  167. package/src/mail.mjs +18 -15
  168. package/src/native-plan.mjs +12 -9
  169. package/src/native-provider-loader.mjs +7 -4
  170. package/src/packager/core.mjs +18 -15
  171. package/src/process.mjs +6 -3
  172. package/src/release-bundle.mjs +14 -11
  173. package/src/runtime.mjs +4 -1
  174. package/src/scaffold.mjs +9 -6
  175. package/src/sdk-browser-runtime.mjs +4 -1
  176. package/src/source-server.mjs +17 -14
  177. package/src/targets/index.mjs +5 -2
  178. package/src/templates/workspace-template.mjs +13 -6
  179. package/src/testing-loader.mjs +7 -4
  180. package/src/testing.mjs +10 -7
  181. package/src/toolchain.mjs +13 -10
  182. package/src/update-check.mjs +9 -6
  183. package/src/workspace-operation-lock.mjs +14 -11
  184. package/src/workspace-runtime.mjs +4 -1
  185. package/src/workspace.mjs +17 -14
  186. package/runtime/arcane/modules/AIResponseLength.js +0 -32
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  import DocumentLexicalSearch,{
2
5
  documentContextExcerpt,
3
6
  documentSearchTokens,
@@ -31,8 +34,8 @@ function fail(message,code='DBOPFS_DOCUMENT_INVALID',ErrorType=TypeError){
31
34
 
32
35
  function isPlainRecord(value){
33
36
  return Boolean(value)
34
- &&typeof value==='object'
35
- &&!Array.isArray(value)
37
+ &&is.object(value)
38
+ &&!is.array(value)
36
39
  &&Object.getPrototypeOf(value)===Object.prototype;
37
40
  }
38
41
 
@@ -42,7 +45,7 @@ function assertKnownKeys(value,allowed,label){
42
45
  }
43
46
 
44
47
  function boundedInteger(value,label,{minimum,maximum}){
45
- if(!Number.isSafeInteger(value)||value<minimum||(maximum!==undefined&&value>maximum)){
48
+ if(!is.safeInteger(value)||value<minimum||(maximum!==undefined&&value>maximum)){
46
49
  const range=maximum===undefined?`${minimum} or greater`:`${minimum} through ${maximum}`;
47
50
  fail(`${label} must be an integer from ${range}.`,'DBOPFS_DOCUMENT_INVALID_LIMIT',RangeError);
48
51
  }
@@ -51,7 +54,7 @@ function boundedInteger(value,label,{minimum,maximum}){
51
54
 
52
55
  function normalizedText(value,label,{optional=false}={}){
53
56
  if(optional&&(value===undefined||value===null||value==='')) return '';
54
- if(typeof value!=='string') fail(`${label} must be a string.`);
57
+ if(!is.string(value)) fail(`${label} must be a string.`);
55
58
  const text=value;
56
59
  if(!text.trim()&&!optional) fail(`${label} cannot be empty.`);
57
60
  return text;
@@ -59,10 +62,10 @@ function normalizedText(value,label,{optional=false}={}){
59
62
 
60
63
  function signalLike(value){
61
64
  return value===undefined||value===null||(
62
- typeof value==='object'
63
- &&typeof value.aborted==='boolean'
64
- &&typeof value.addEventListener==='function'
65
- &&typeof value.removeEventListener==='function'
65
+ is.object(value)
66
+ &&is.boolean(value.aborted)
67
+ &&is.function(value.addEventListener)
68
+ &&is.function(value.removeEventListener)
66
69
  );
67
70
  }
68
71
 
@@ -80,7 +83,7 @@ async function yieldEvaluationTask(signal){
80
83
  throwIfAborted(signal);
81
84
  await new Promise((resolve,reject)=>{
82
85
  try{
83
- if(typeof globalThis.MessageChannel==='function'){
86
+ if(is.function(globalThis.MessageChannel)){
84
87
  const channel=new globalThis.MessageChannel();
85
88
  channel.port1.onmessage=()=>{
86
89
  channel.port1.close();
@@ -122,13 +125,13 @@ function normalizeSchema(input){
122
125
 
123
126
  function stringList(value,label){
124
127
  if(value===undefined||value===null) return [];
125
- if(!Array.isArray(value)) fail(`${label} must be an array.`);
128
+ if(!is.array(value)) fail(`${label} must be an array.`);
126
129
  return value.map((item,index)=>normalizedText(item,`${label} entry ${index+1}`));
127
130
  }
128
131
 
129
132
  function headings(value){
130
133
  if(value===undefined||value===null) return [];
131
- if(!Array.isArray(value)) fail('Document headings must be an array.');
134
+ if(!is.array(value)) fail('Document headings must be an array.');
132
135
  return value.map((item,index)=>{
133
136
  if(!isPlainRecord(item)) fail(`Document heading ${index+1} must be a plain object.`);
134
137
  assertKnownKeys(item,new Set(['id','level','text']),`Document heading ${index+1}`);
@@ -153,7 +156,7 @@ function normalizeDocument(input,schema,index,{stored=false}={}){
153
156
  const id=normalizedText(input[fields.id],`Document ${index+1} id`);
154
157
  if(!IDENTIFIER.test(id)) fail(`Document ${index+1} id is invalid.`);
155
158
  const body=input[fields.body];
156
- if(typeof body!=='string') fail(`Document ${id} body must be a string.`);
159
+ if(!is.string(body)) fail(`Document ${id} body must be a string.`);
157
160
  const mediaType=normalizedText(input[fields.mediaType]??'text/markdown',`Document ${id} mediaType`);
158
161
  if(!['text/markdown','text/plain'].includes(mediaType)) fail(`Document ${id} mediaType is unsupported.`);
159
162
  const kind=normalizedText(input[fields.kind]??'document',`Document ${id} kind`).toLowerCase();
@@ -213,7 +216,7 @@ function manifestKey(schema){
213
216
 
214
217
  function generationId(){
215
218
  const value=globalThis.crypto?.randomUUID?.();
216
- if(typeof value==='string'&&value)return value;
219
+ if(is.string(value)&&value)return value;
217
220
  generationSequence+=1;
218
221
  return `generation-${Date.now().toString(36)}-${generationSequence.toString(36)}`;
219
222
  }
@@ -248,7 +251,7 @@ function readFailureError(message,errors,failures){
248
251
  function sourceFailureKey(file,index,schema){
249
252
  for(const field of ['id','sourcePath','path']){
250
253
  const value=file?.[schema.fields[field]];
251
- if(typeof value==='string'&&value.trim()) return normalizedFailureText(value,`source:${index+1}`);
254
+ if(is.string(value)&&value.trim()) return normalizedFailureText(value,`source:${index+1}`);
252
255
  }
253
256
  return `source:${index+1}`;
254
257
  }
@@ -258,11 +261,11 @@ function normalizeReadCoverage(input,count){
258
261
  if(
259
262
  !isPlainRecord(input)
260
263
  ||Object.keys(input).some(key=>!['errors','failures','readable','total'].includes(key))
261
- ||!Array.isArray(input.failures)
264
+ ||!is.array(input.failures)
262
265
  ||Object.keys(input.failures).length!==input.failures.length
263
- ||!Number.isSafeInteger(input.errors)
264
- ||!Number.isSafeInteger(input.readable)
265
- ||!Number.isSafeInteger(input.total)
266
+ ||!is.safeInteger(input.errors)
267
+ ||!is.safeInteger(input.readable)
268
+ ||!is.safeInteger(input.total)
266
269
  ) fail('Stored document read coverage is invalid.','DBOPFS_DOCUMENT_INCOMPLETE');
267
270
  const failures=input.failures.map((item,index)=>{
268
271
  if(!isPlainRecord(item)||item.phase!=='source-read'
@@ -416,7 +419,7 @@ async function readEvaluationSources(sources,options){
416
419
  body=await read(descriptor.source,{
417
420
  ordinal:descriptor.ordinal,signal:signal??null
418
421
  });
419
- if(typeof body!=='string') fail('read must resolve to document text.');
422
+ if(!is.string(body)) fail('read must resolve to document text.');
420
423
  }catch(error){return {error};}
421
424
  return {body,record:{...descriptor.record,body}};
422
425
  }));
@@ -478,7 +481,7 @@ class DBOPFSDocumentLibrary{
478
481
  'concurrency','db','maxCorpusCharacters','maxDocumentCharacters','maxSearchCharacters','schema'
479
482
  ]),'DBOPFS document library options');
480
483
  const db=options.db??globalThis.dbopfs;
481
- if(!db||typeof db.get!=='function'||typeof db.set!=='function'||typeof db.getAllKeys!=='function'||typeof db.delete!=='function'){
484
+ if(!db||!is.function(db.get)||!is.function(db.set)||!is.function(db.getAllKeys)||!is.function(db.delete)){
482
485
  fail('A DBOPFS-compatible db with get, set, getAllKeys, and delete is required.','DBOPFS_DOCUMENT_STORAGE_UNAVAILABLE');
483
486
  }
484
487
  this.#db=db;
@@ -511,9 +514,9 @@ class DBOPFSDocumentLibrary{
511
514
  new Set(['files','onProgress','read','readFailurePolicy','signal']),
512
515
  'Document bootstrap options',
513
516
  );
514
- if(!Array.isArray(options.files)) fail('Document bootstrap files must be an array.');
515
- if(options.onProgress!==undefined&&typeof options.onProgress!=='function') fail('onProgress must be a function.');
516
- if(options.read!==undefined&&typeof options.read!=='function') fail('read must be a function.');
517
+ if(!is.array(options.files)) fail('Document bootstrap files must be an array.');
518
+ if(options.onProgress!==undefined&&!is.function(options.onProgress)) fail('onProgress must be a function.');
519
+ if(options.read!==undefined&&!is.function(options.read)) fail('read must be a function.');
517
520
  const readFailurePolicy=options.readFailurePolicy??'preserve-readable';
518
521
  if(!READ_FAILURE_POLICIES.has(readFailurePolicy)){
519
522
  fail('readFailurePolicy must be "reject" or "preserve-readable".');
@@ -537,9 +540,9 @@ class DBOPFSDocumentLibrary{
537
540
  this.#concurrency,
538
541
  options.signal,
539
542
  async file=>{
540
- if(typeof file?.[this.#schema.fields.body]==='string') return file;
543
+ if(is.string(file?.[this.#schema.fields.body])) return file;
541
544
  const body=await options.read({...file},{signal:options.signal??null});
542
- if(typeof body!=='string') fail('read must resolve to document text.');
545
+ if(!is.string(body)) fail('read must resolve to document text.');
543
546
  return {...file,[this.#schema.fields.body]:body};
544
547
  },
545
548
  (_result,file)=>reportProgress(options.onProgress,{
@@ -689,9 +692,9 @@ class DBOPFSDocumentLibrary{
689
692
  ||manifest.schemaId!==this.#schema.id
690
693
  ||manifest.table!==this.#schema.table
691
694
  ||manifest.schemaVersion!==this.#schema.version
692
- ||typeof manifest.generation!=='string'
695
+ ||!is.string(manifest.generation)
693
696
  ||!manifest.generation
694
- ||!Array.isArray(manifest.keys)
697
+ ||!is.array(manifest.keys)
695
698
  ) fail('The DBOPFS document corpus has not completed bootstrap.','DBOPFS_DOCUMENT_NOT_BOOTSTRAPPED');
696
699
  const readCoverage=normalizeReadCoverage(manifest.readCoverage,manifest.keys.length);
697
700
  if((manifest.completed===PARTIAL_COMPLETION)!==(readCoverage.errors>0)){
@@ -700,7 +703,7 @@ class DBOPFSDocumentLibrary{
700
703
  const keys=[...manifest.keys];
701
704
  if(
702
705
  new Set(keys).size!==keys.length
703
- ||keys.some(key=>typeof key!=='string'||!key)
706
+ ||keys.some(key=>!is.string(key)||!key)
704
707
  ) fail('The DBOPFS document corpus differs from its completion manifest.','DBOPFS_DOCUMENT_INCOMPLETE');
705
708
  const settled=await boundedMap(
706
709
  keys,
@@ -776,7 +779,7 @@ class DBOPFSDocumentLibrary{
776
779
  'maxScoringCharacters','onProgress','read','readFailurePolicy','signal','sources','tags'
777
780
  ]),'Document evaluation options');
778
781
  if(!signalLike(options.signal)) fail('signal must be an AbortSignal.');
779
- if(options.onProgress!==undefined&&typeof options.onProgress!=='function') fail('onProgress must be a function.');
782
+ if(options.onProgress!==undefined&&!is.function(options.onProgress)) fail('onProgress must be a function.');
780
783
  throwIfAborted(options.signal);
781
784
  new DocumentLexicalSearch([]).rank(query,{
782
785
  kinds:options.kinds,
@@ -784,8 +787,8 @@ class DBOPFSDocumentLibrary{
784
787
  });
785
788
  const filters=normalizedEvaluationFilters(options.kinds,options.tags);
786
789
 
787
- if(!Array.isArray(options.sources)) fail('Document evaluation sources must be an array.');
788
- if(typeof options.read!=='function') fail('Source evaluation requires a read function.');
790
+ if(!is.array(options.sources)) fail('Document evaluation sources must be an array.');
791
+ if(!is.function(options.read)) fail('Source evaluation requires a read function.');
789
792
  const readFailurePolicy=options.readFailurePolicy??'preserve-readable';
790
793
  if(!READ_FAILURE_POLICIES.has(readFailurePolicy)){
791
794
  fail('readFailurePolicy must be "reject" or "preserve-readable".');
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  function workspaceRoot(value){
2
5
  const root=String(value??'').trim();
3
6
  if(!root||/[\u0000-\u001f]/.test(root)){
@@ -44,7 +47,7 @@ export default class DevelopmentWorkspace{
44
47
  }
45
48
 
46
49
  require(method){
47
- if(!this.available||typeof this.api?.[method]!=='function'){
50
+ if(!this.available||!is.function(this.api?.[method])){
48
51
  throw new Error('The Arcane development-workspace capability is unavailable. Open this application through an installed Arcane OS developer build.');
49
52
  }
50
53
  return this.api[method].bind(this.api);
@@ -1,7 +1,10 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  function isRecord(value){
2
5
  return Boolean(value)
3
- &&typeof value==='object'
4
- &&!Array.isArray(value);
6
+ &&is.object(value)
7
+ &&!is.array(value);
5
8
  }
6
9
 
7
10
  function coded(error,code){
@@ -11,7 +14,7 @@ function coded(error,code){
11
14
 
12
15
  function optionalText(value,label){
13
16
  if(value===undefined||value===null) return value;
14
- if(typeof value!=='string') throw new TypeError(`${label} must be a string when provided.`);
17
+ if(!is.string(value)) throw new TypeError(`${label} must be a string when provided.`);
15
18
  return value;
16
19
  }
17
20
 
@@ -30,7 +33,7 @@ function normalizeDirectoryPickerOptions(input={}){
30
33
  function normalizeDirectorySelection(input){
31
34
  if(
32
35
  !isRecord(input)
33
- ||typeof input.cancelled!=='boolean'
36
+ ||!is.boolean(input.cancelled)
34
37
  ){
35
38
  throw coded(
36
39
  new TypeError('The directory picker provider returned an invalid result.'),
@@ -73,7 +76,7 @@ export default class DirectoryPicker{
73
76
  }
74
77
 
75
78
  get available(){
76
- return typeof this.provider?.selectDirectory==='function';
79
+ return is.function(this.provider?.selectDirectory);
77
80
  }
78
81
 
79
82
  async select(options={}){
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  const DOCUMENT_SEARCH_FIELD_ORDER=[
2
5
  'title','searchTerms','tags','headings','summary','category','navigationGroup',
3
6
  'navigationParent','audiences','platforms','sourcePath','path','language','id'
@@ -17,8 +20,8 @@ function fail(message,code='DOCUMENT_SEARCH_INVALID'){
17
20
 
18
21
  function isPlainRecord(value){
19
22
  return Boolean(value)
20
- &&typeof value==='object'
21
- &&!Array.isArray(value)
23
+ &&is.object(value)
24
+ &&!is.array(value)
22
25
  &&Object.getPrototypeOf(value)===Object.prototype;
23
26
  }
24
27
 
@@ -32,7 +35,7 @@ function documentSearchTokens(value){
32
35
  }
33
36
 
34
37
  function list(value,mapper=normalizedDocumentSearchText){
35
- return Array.isArray(value)?value.map(mapper):[];
38
+ return is.array(value)?value.map(mapper):[];
36
39
  }
37
40
 
38
41
  function createDocumentLexicalIndex(record){
@@ -125,7 +128,7 @@ function compareText(left,right){
125
128
  }
126
129
 
127
130
  function normalizeQuery(value){
128
- if(typeof value!=='string') fail('Search query must be a string.','DOCUMENT_SEARCH_INVALID_QUERY');
131
+ if(!is.string(value)) fail('Search query must be a string.','DOCUMENT_SEARCH_INVALID_QUERY');
129
132
  const query=value.trim();
130
133
  if(CONTROL_CHARACTERS.test(query)){
131
134
  fail('Search query must be plain text.','DOCUMENT_SEARCH_INVALID_QUERY');
@@ -135,9 +138,9 @@ function normalizeQuery(value){
135
138
 
136
139
  function normalizeFilter(value,label){
137
140
  if(value===undefined) return null;
138
- if(!Array.isArray(value)) fail(`${label} must be an array.`,'DOCUMENT_SEARCH_INVALID_QUERY');
141
+ if(!is.array(value)) fail(`${label} must be an array.`,'DOCUMENT_SEARCH_INVALID_QUERY');
139
142
  return new Set(value.map((item,index)=>{
140
- if(typeof item!=='string'||!item.trim()){
143
+ if(!is.string(item)||!item.trim()){
141
144
  fail(`${label} entry ${index+1} must contain text.`,'DOCUMENT_SEARCH_INVALID_QUERY');
142
145
  }
143
146
  return canonicalKey(item.trim());
@@ -152,7 +155,7 @@ function lineNumberAt(value,offset){
152
155
  }
153
156
 
154
157
  function documentContextExcerpt(value){
155
- if(typeof value!=='string') fail('Document context must be text.');
158
+ if(!is.string(value)) fail('Document context must be text.');
156
159
  return {
157
160
  lineEnd:lineNumberAt(value,Math.max(0,value.length-1)),
158
161
  lineStart:1,
@@ -165,7 +168,7 @@ class DocumentLexicalSearch{
165
168
  #records;
166
169
 
167
170
  constructor(records){
168
- if(!Array.isArray(records)) fail('Document search records must be an array.');
171
+ if(!is.array(records)) fail('Document search records must be an array.');
169
172
  this.#records=[...records];
170
173
  this.#indexes=new Map(this.#records.map(record=>[record.id,createDocumentLexicalIndex(record)]));
171
174
  }
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  const documentNavigationBindings=new WeakMap();
2
5
  const documentNavigationForms=new WeakSet();
3
6
 
@@ -41,7 +44,7 @@ function nearestDocumentNavigationDetails(group){
41
44
  }
42
45
 
43
46
  function documentNavigationDetailsIsOpen(details){
44
- return typeof details.open==='boolean'
47
+ return is.boolean(details.open)
45
48
  ?details.open
46
49
  :details.hasAttribute('open');
47
50
  }
@@ -113,8 +116,8 @@ function revealCurrentDocumentNavigationItem(binding){
113
116
  if(
114
117
  !currentItem
115
118
  ||currentItem.hidden
116
- ||typeof currentItem.getBoundingClientRect!=='function'
117
- ||typeof binding.navigation.getBoundingClientRect!=='function'
119
+ ||!is.function(currentItem.getBoundingClientRect)
120
+ ||!is.function(binding.navigation.getBoundingClientRect)
118
121
  )return false;
119
122
 
120
123
  const navigationBounds=binding.navigation.getBoundingClientRect();
@@ -204,7 +207,7 @@ function autoInitializeDocumentNavigation(){
204
207
  initializeDocumentNavigation(globalThis.document);
205
208
  }
206
209
 
207
- if(typeof globalThis.document!=='undefined'){
210
+ if(!is.undefined(globalThis.document)){
208
211
  if(globalThis.document.readyState==='loading'){
209
212
  globalThis.document.addEventListener(
210
213
  'DOMContentLoaded',
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  import waitForComponent from './WaitForComponent.js';
2
5
  import { arcaneLogging,readArcaneDeveloperMode } from 'arcane-os/logging';
3
6
  import {
@@ -22,7 +25,7 @@ const ERROR_REASONS={
22
25
  unhandledRejectionCaptured:'unhandled-promise-rejection-captured'
23
26
  };
24
27
  const RUNTIME_OCCURRENCE_PREFIX=(
25
- typeof globalThis.crypto?.randomUUID==='function'
28
+ is.function(globalThis.crypto?.randomUUID)
26
29
  ? globalThis.crypto.randomUUID()
27
30
  : `${Date.now().toString(36)}-${Math.random().toString(36).replace(/^0\./,'')}`
28
31
  );
@@ -43,7 +46,7 @@ function safeText(value,fallback=''){
43
46
  }
44
47
 
45
48
  try{
46
- const text=typeof value==='string' ? value:String(value);
49
+ const text=is.string(value) ? value:String(value);
47
50
  return text||fallback;
48
51
  }catch{
49
52
  return fallback;
@@ -52,7 +55,7 @@ function safeText(value,fallback=''){
52
55
 
53
56
  function safeNumber(value){
54
57
  const number=Number(value);
55
- return Number.isFinite(number) ? number:null;
58
+ return is.finite(number) ? number:null;
56
59
  }
57
60
 
58
61
  function safeIso(timestamp){
@@ -111,7 +114,7 @@ export function normalizeErrorEvent(event={},target=globalThis.window){
111
114
  export function normalizeRejectionEvent(event={},target=globalThis.window){
112
115
  const reason=event?.reason;
113
116
  const reasonIsObject=reason!==null
114
- && (typeof reason==='object'||typeof reason==='function');
117
+ && (is.object(reason)||is.function(reason));
115
118
 
116
119
  return {
117
120
  type:'unhandledrejection',
@@ -144,11 +147,11 @@ function defaultStorage(target){
144
147
  }
145
148
 
146
149
  async function sendWithWindowMail(target,...args){
147
- if(typeof target?.mail?.send!=='function'){
150
+ if(!is.function(target?.mail?.send)){
148
151
  await import('./Mail.js');
149
152
  }
150
153
 
151
- if(typeof target?.mail?.send!=='function'){
154
+ if(!is.function(target?.mail?.send)){
152
155
  throw new Error('Mail notification service is unavailable');
153
156
  }
154
157
 
@@ -178,8 +181,8 @@ function buildDeveloperIncidentContent(document,incident,occurrenceId){
178
181
  const details=document.createElement('dl');
179
182
  const source=[
180
183
  safeText(incident?.filename),
181
- Number.isFinite(incident?.lineno) ? incident.lineno:'',
182
- Number.isFinite(incident?.colno) ? incident.colno:'',
184
+ is.finite(incident?.lineno) ? incident.lineno:'',
185
+ is.finite(incident?.colno) ? incident.colno:'',
183
186
  ].filter(value => value!=='').join(':');
184
187
 
185
188
  content.className='developer-error-content';
@@ -262,7 +265,7 @@ async function presentDeveloperIncidentModal(target,incident,occurrenceId){
262
265
  };
263
266
  const onPageHide=() => {
264
267
  try{
265
- if(typeof modal.close==='function'){
268
+ if(is.function(modal.close)){
266
269
  modal.close(undefined,true);
267
270
  }else{
268
271
  modal.remove();
@@ -308,7 +311,7 @@ class Errors {
308
311
 
309
312
  constructor(options={}) {
310
313
  const target=options.target||globalThis.window;
311
- if(!target||typeof target.addEventListener!=='function'){
314
+ if(!target||!is.function(target.addEventListener)){
312
315
  throw new TypeError('Errors requires an event target');
313
316
  }
314
317
 
@@ -334,15 +337,15 @@ class Errors {
334
337
  ? defaultStorage(target)
335
338
  : options.storage;
336
339
  this.storageHealthy=(
337
- typeof this.storage?.getItem==='function'
338
- && typeof this.storage?.setItem==='function'
340
+ is.function(this.storage?.getItem)
341
+ && is.function(this.storage?.setItem)
339
342
  );
340
343
  this.sendMail=options.sendMail
341
344
  || sendWithWindowMail.bind(null,target);
342
- this.isDeveloperMode=typeof options.isDeveloperMode==='function'
345
+ this.isDeveloperMode=is.function(options.isDeveloperMode)
343
346
  ? options.isDeveloperMode
344
347
  : readArcaneDeveloperMode.bind(null,target);
345
- this.presentDeveloperIncident=typeof options.presentDeveloperIncident==='function'
348
+ this.presentDeveloperIncident=is.function(options.presentDeveloperIncident)
346
349
  ? options.presentDeveloperIncident
347
350
  : presentDeveloperIncidentModal.bind(null,target);
348
351
 
@@ -397,12 +400,12 @@ class Errors {
397
400
 
398
401
  try{
399
402
  const value=JSON.parse(this.storage.getItem(LEDGER_STORAGE_KEY));
400
- if(!value||typeof value!=='object'){
403
+ if(!value||!is.object(value)){
401
404
  return empty;
402
405
  }
403
406
 
404
407
  return {
405
- pending:Array.isArray(value.pending)
408
+ pending:is.array(value.pending)
406
409
  ? value.pending
407
410
  : []
408
411
  };
@@ -470,7 +473,7 @@ class Errors {
470
473
  }
471
474
 
472
475
  restorePending(records){
473
- if(!Array.isArray(records)){
476
+ if(!is.array(records)){
474
477
  return;
475
478
  }
476
479
 
@@ -478,23 +481,23 @@ class Errors {
478
481
  for(const storedRecord of records){
479
482
  if(
480
483
  !storedRecord
481
- || typeof storedRecord!=='object'
484
+ || !is.object(storedRecord)
482
485
  || !storedRecord.incident
483
- || typeof storedRecord.incident!=='object'
486
+ || !is.object(storedRecord.incident)
484
487
  ){
485
488
  this.warn('A persisted error delivery could not be restored.');
486
489
  continue;
487
490
  }
488
491
 
489
- const capturedAt=Number.isFinite(storedRecord.capturedAt)
492
+ const capturedAt=is.finite(storedRecord.capturedAt)
490
493
  ? storedRecord.capturedAt
491
- : Number.isFinite(storedRecord.firstSeen)
494
+ : is.finite(storedRecord.firstSeen)
492
495
  ? storedRecord.firstSeen
493
496
  : timestamp;
494
- const dueAt=Number.isFinite(storedRecord.dueAt)
497
+ const dueAt=is.finite(storedRecord.dueAt)
495
498
  ? storedRecord.dueAt:capturedAt+this.delayMs;
496
499
  let occurrenceId=(
497
- typeof storedRecord.occurrenceId==='string'
500
+ is.string(storedRecord.occurrenceId)
498
501
  && storedRecord.occurrenceId.trim()
499
502
  )
500
503
  ? storedRecord.occurrenceId
@@ -777,7 +780,7 @@ class Errors {
777
780
  const modal=this.target.document?.querySelector?.(
778
781
  'html-import[data-global-error-modal]'
779
782
  );
780
- if(typeof modal?.close==='function'){
783
+ if(is.function(modal?.close)){
781
784
  modal.close(undefined,true);
782
785
  }else{
783
786
  modal?.remove?.();
@@ -812,7 +815,7 @@ class Errors {
812
815
  }
813
816
  }
814
817
 
815
- if(typeof window!=='undefined'&&window.errors?.[HANDLER_MARKER]!==true){
818
+ if(!is.undefined(globalThis.window)&&window.errors?.[HANDLER_MARKER]!==true){
816
819
  new Errors();
817
820
  }
818
821
 
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  import { arcaneLogging } from 'arcane-os/logging';
2
5
  import {
3
6
  createArcaneEventSource,
@@ -17,7 +20,7 @@ const htmlImportModuleURL=new URL(import.meta.url);
17
20
  const htmlImportAssetVersion=htmlImportModuleURL.searchParams.get('arcaneVersion');
18
21
 
19
22
  function versionComponentResource(value,baseHref){
20
- if(!htmlImportAssetVersion||typeof value!=='string'||!value||value.startsWith('#')){
23
+ if(!htmlImportAssetVersion||!is.string(value)||!value||value.startsWith('#')){
21
24
  return value;
22
25
  }
23
26
  let resolvedURL;
@@ -64,7 +67,7 @@ function componentRuntimeRoot(resolvedHref){
64
67
  }
65
68
 
66
69
  function resolveComponentResource(value,runtimeRoot){
67
- if(!runtimeRoot||typeof value!=='string'||!value.startsWith('./arcane/')){
70
+ if(!runtimeRoot||!is.string(value)||!value.startsWith('./arcane/')){
68
71
  return value;
69
72
  }
70
73
  return new URL(value.slice('./arcane/'.length),runtimeRoot).href;
@@ -509,7 +512,7 @@ class HTMLImport extends HTMLElement {
509
512
  htmlImportHostRegistry.set(hostToken,binding);
510
513
  try{
511
514
  document.head.appendChild(executable);
512
- if(!binding.promise||typeof binding.promise.then!=='function'){
515
+ if(!binding.promise||!is.function(binding.promise.then)){
513
516
  throw new Error('The HTML import script did not start.');
514
517
  }
515
518
  await binding.promise;
@@ -522,7 +525,7 @@ class HTMLImport extends HTMLElement {
522
525
  }finally{
523
526
  const installedDescriptor=Object.getOwnPropertyDescriptor(this,'destroy')??null;
524
527
  const installedDestroy=this.destroy;
525
- if(typeof installedDestroy==='function'
528
+ if(is.function(installedDestroy)
526
529
  &&installedDestroy!==previousDestroy
527
530
  &&!samePropertyDescriptor(installedDescriptor,previousDescriptor)){
528
531
  this.#importedDestroy={
@@ -1,3 +1,6 @@
1
+ import Is from 'strong-type';
2
+ const is=new Is(false);
3
+
1
4
  const SENTENCE_BOUNDARY=/[.!?]+(?:["'\\u2019\\u201d)\\]}]+)?(?=\\s|$)/gu;
2
5
  const WORD_OR_NUMBER=/[\\p{L}\\p{N}]/u;
3
6
 
@@ -9,12 +12,12 @@ function codedError(code,message,ErrorType=Error){
9
12
 
10
13
  function isPlainRecord(value){
11
14
  return Boolean(value)
12
- &&typeof value==='object'
13
- &&!Array.isArray(value);
15
+ &&is.object(value)
16
+ &&!is.array(value);
14
17
  }
15
18
 
16
19
  function countSentences(value){
17
- if(typeof value!=='string'){
20
+ if(!is.string(value)){
18
21
  throw new TypeError('Sentence counting requires a string.');
19
22
  }
20
23
  if(!value.trim()){
@@ -36,8 +39,8 @@ function countSentences(value){
36
39
 
37
40
  function requireLocalAI(localAI){
38
41
  if(!localAI
39
- ||typeof localAI.inspectIsolatedModel!=='function'
40
- ||typeof localAI.runIsolatedQuestion!=='function'){
42
+ ||!is.function(localAI.inspectIsolatedModel)
43
+ ||!is.function(localAI.runIsolatedQuestion)){
41
44
  throw codedError(
42
45
  'ARCANE_ISOLATED_MODEL_API_UNAVAILABLE',
43
46
  'The Arcane isolated-model API is unavailable. Open this application through a compatible Arcane OS host.'
@@ -52,14 +55,14 @@ class IsolatedModelQuestionRunner{
52
55
  }
53
56
 
54
57
  async inspectModel(model,expectedModel,contextTokens){
55
- if(typeof model!=='string'||!model.trim()){
58
+ if(!is.string(model)||!model.trim()){
56
59
  throw codedError(
57
60
  'INVALID_ISOLATED_MODEL_RUNNER_REQUEST',
58
61
  'The isolated-model inspection requires a model.',
59
62
  TypeError
60
63
  );
61
64
  }
62
- if(contextTokens!==undefined&&(!Number.isSafeInteger(contextTokens)||contextTokens<1)){
65
+ if(contextTokens!==undefined&&(!is.safeInteger(contextTokens)||contextTokens<1)){
63
66
  throw codedError(
64
67
  'INVALID_ISOLATED_MODEL_RUNNER_REQUEST',
65
68
  'The isolated-model inspection context token value must be positive when provided.',
@@ -81,12 +84,12 @@ class IsolatedModelQuestionRunner{
81
84
  );
82
85
  }
83
86
  const {onPhase,...request}=input;
84
- if(typeof request.model!=='string'
87
+ if(!is.string(request.model)
85
88
  ||!request.model.trim()
86
- ||typeof request.prompt!=='string'
87
- ||(Object.hasOwn(request,'systemPrompt')&&typeof request.systemPrompt!=='string')
89
+ ||!is.string(request.prompt)
90
+ ||(Object.hasOwn(request,'systemPrompt')&&!is.string(request.systemPrompt))
88
91
  ||(Object.hasOwn(request,'options')&&!isPlainRecord(request.options))
89
- ||(onPhase!==undefined&&typeof onPhase!=='function')){
92
+ ||(onPhase!==undefined&&!is.function(onPhase))){
90
93
  throw codedError(
91
94
  'INVALID_ISOLATED_MODEL_RUNNER_REQUEST',
92
95
  'The isolated-model question request contains invalid values.',
@@ -95,7 +98,7 @@ class IsolatedModelQuestionRunner{
95
98
  }
96
99
  const streamOptions=onPhase===undefined?{}:{onPhase};
97
100
  const result=await this.localAI.runIsolatedQuestion(request,streamOptions);
98
- if(!isPlainRecord(result)||typeof result.answer!=='string'){
101
+ if(!isPlainRecord(result)||!is.string(result.answer)){
99
102
  throw codedError(
100
103
  'ARCANE_ISOLATED_MODEL_RESPONSE_INVALID',
101
104
  'Arcane Core returned an invalid isolated-model response.'