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,6 +1,9 @@
1
+ import Is from 'strong-type';
1
2
  import {AsyncLocalStorage} from 'node:async_hooks';
2
3
  import {arcaneEvents} from './event-manager.mjs';
3
4
 
5
+ const is = new Is(false);
6
+
4
7
  const deliveryStorage=new AsyncLocalStorage();
5
8
 
6
9
  function deliveryOccurrence(event){
@@ -17,13 +20,13 @@ function deliveryOccurrence(event){
17
20
  }
18
21
 
19
22
  function mirror(manager,event,eventMetadata,occurrence){
20
- if(!manager||!event||typeof event!=='object'||occurrence.managers.has(manager)){
23
+ if(!manager||!event||!is.object(event)||occurrence.managers.has(manager)){
21
24
  return;
22
25
  }
23
26
  occurrence.managers.add(manager);
24
27
  try{
25
28
  const result=manager.forward(event,eventMetadata);
26
- if(result&&typeof result.then==='function'){
29
+ if(result&&is.function(result.then)){
27
30
  void Promise.resolve(result).catch(()=>{});
28
31
  }
29
32
  }catch{
@@ -44,9 +47,9 @@ export function createEventQueue(onEvent,{
44
47
  eventManager=arcaneEvents,
45
48
  eventMetadata={source:'sdk',category:'operation'}
46
49
  }={}){
47
- const callback=typeof onEvent==='function'?onEvent:null;
50
+ const callback=is.function(onEvent)?onEvent:null;
48
51
  const manager=eventManager===null?null:eventManager;
49
- if(manager&&typeof manager.forward!=='function'){
52
+ if(manager&&!is.function(manager.forward)){
50
53
  throw new TypeError('The central event manager must provide forward(event, metadata).');
51
54
  }
52
55
  let firstError=null;
@@ -65,7 +68,7 @@ export function createEventQueue(onEvent,{
65
68
  resolveFailure(firstError);
66
69
  try{
67
70
  const result=onFailure?.(firstError);
68
- if(result&&typeof result.then==='function'){
71
+ if(result&&is.function(result.then)){
69
72
  void Promise.resolve(result).catch(()=>{});
70
73
  }
71
74
  }catch{
package/src/events.mjs CHANGED
@@ -1,22 +1,25 @@
1
+ import Is from 'strong-type';
1
2
  import {randomUUID} from 'node:crypto';
2
3
  import {CLI_EVENT_PROTOCOL,OUTPUT_MODES} from './constants.mjs';
3
4
  import {ArcaneError,ERROR_CODES,errorRecord} from './errors.mjs';
4
5
 
6
+ const is = new Is(false);
7
+
5
8
  function write(stream,value){
6
9
  stream.write(`${value}\n`);
7
10
  }
8
11
 
9
12
  function jsonSafe(value,seen=new WeakSet()){
10
- if(value===undefined||value===null||typeof value==='string'
11
- ||typeof value==='number'||typeof value==='boolean'){
13
+ if(value===undefined||value===null||is.string(value)
14
+ ||is.number(value)||is.boolean(value)){
12
15
  return value;
13
16
  }
14
17
 
15
- if(typeof value==='bigint'){
18
+ if(is.bigint(value)){
16
19
  return value.toString();
17
20
  }
18
21
 
19
- if(typeof value==='function'||typeof value==='symbol'){
22
+ if(is.function(value)||is.symbol(value)){
20
23
  return undefined;
21
24
  }
22
25
 
@@ -24,7 +27,7 @@ function jsonSafe(value,seen=new WeakSet()){
24
27
  return errorRecord(value);
25
28
  }
26
29
 
27
- if(Array.isArray(value)){
30
+ if(is.array(value)){
28
31
  if(seen.has(value)){
29
32
  return undefined;
30
33
  }
@@ -35,7 +38,7 @@ function jsonSafe(value,seen=new WeakSet()){
35
38
  return result;
36
39
  }
37
40
 
38
- if(typeof value==='object'){
41
+ if(is.object(value)){
39
42
  if(seen.has(value)){
40
43
  return undefined;
41
44
  }
@@ -152,7 +155,7 @@ export function createReporter({
152
155
  }));
153
156
  }else{
154
157
  if(result!==undefined){
155
- if(typeof result==='string'){
158
+ if(is.string(result)){
156
159
  write(stdout,result);
157
160
  }else{
158
161
  write(stdout,JSON.stringify(jsonSafe(result),null,2));
@@ -195,10 +198,10 @@ export function createReporter({
195
198
  }
196
199
 
197
200
  function forward(value,data){
198
- if(typeof value==='string'){
201
+ if(is.string(value)){
199
202
  return emit(value,data);
200
203
  }
201
- if(value&&typeof value==='object'){
204
+ if(value&&is.object(value)){
202
205
  const {type='operation.progress',message,...rest}=value;
203
206
  return emit(type,rest.data??rest,message);
204
207
  }
@@ -1,8 +1,11 @@
1
+ import Is from 'strong-type';
1
2
  import {lstat,mkdir,readFile as readFileFromDisk,readdir,realpath,writeFile} from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import {pathToFileURL} from 'node:url';
4
5
  import {SDK_VERSION} from './constants.mjs';
5
6
 
7
+ const is = new Is(false);
8
+
6
9
  export const IMPORT_MAP_RELATIVE_PATH='modules/arcane.importmap.json';
7
10
  export const MANAGED_IMPORT_MAP_ATTRIBUTE='data-arcane-import-map';
8
11
 
@@ -20,6 +23,7 @@ const STATIC_RUNTIME_PACKAGE_IMPORTS=new Map([
20
23
  const SDK_BROWSER_SELF_IMPORTS=new Map([
21
24
  ['arcane-os/event-manager',SDK_BROWSER_ENTRY],
22
25
  ['arcane-os/logging','sdk/logging.mjs'],
26
+ ['arcane-os/speech-text','sdk/speech-text.mjs'],
23
27
  ['arcane-os/ai/browser-wasm',SDK_BROWSER_AI_ENTRY],
24
28
  ['arcane-os/ai/browser-speech',SDK_BROWSER_SPEECH_ENTRY]
25
29
  ]);
@@ -37,7 +41,7 @@ function throwIfAborted(signal){
37
41
  }
38
42
 
39
43
  async function emit(onEvent,event){
40
- if(typeof onEvent!=='function')return null;
44
+ if(!is.function(onEvent))return null;
41
45
  try{
42
46
  await onEvent(event);
43
47
  return null;
@@ -55,7 +59,7 @@ function compareText(left,right){
55
59
  }
56
60
 
57
61
  function safeRelativePath(value,label='path'){
58
- if(typeof value!=='string'||!value||value.includes('\\')||value.includes('\0')
62
+ if(!is.string(value)||!value||value.includes('\\')||value.includes('\0')
59
63
  ||path.posix.isAbsolute(value)||path.posix.normalize(value)!==value
60
64
  ||value==='.'||value.startsWith('../')||value.includes('/../')){
61
65
  fail(`Import-map ${label} is unsafe: ${String(value)}.`);
@@ -65,7 +69,7 @@ function safeRelativePath(value,label='path'){
65
69
 
66
70
  function normalizedDocumentPaths(entry,documents){
67
71
  if(documents===undefined)return [entry];
68
- if(!Array.isArray(documents)||documents.length===0){
72
+ if(!is.array(documents)||documents.length===0){
69
73
  fail('Import-map documents must be a non-empty array of application-relative paths.');
70
74
  }
71
75
  const normalized=[];
@@ -265,7 +269,7 @@ function classHeader(tokens){
265
269
 
266
270
  function functionHeader(tokens,closingParenthesis,enclosingBraceKind){
267
271
  const openIndex=closingParenthesis?.openTokenIndex;
268
- if(!Number.isInteger(openIndex))return null;
272
+ if(!is.integer(openIndex))return null;
269
273
  for(let index=openIndex-1;index>=0;index-=1){
270
274
  const token=tokens[index];
271
275
  if(new Set([';', '{', '}']).has(token.value))break;
@@ -296,7 +300,7 @@ function arrowFunctionContext(tokens){
296
300
  if(arrow?.value!=='=>')return null;
297
301
  const parameter=tokens.at(-2);
298
302
  let async=false;
299
- if(parameter?.value===')'&&Number.isInteger(parameter.openTokenIndex)){
303
+ if(parameter?.value===')'&&is.integer(parameter.openTokenIndex)){
300
304
  const beforeOpen=tokens[parameter.openTokenIndex-1];
301
305
  async=beforeOpen?.type==='identifier'&&beforeOpen.value==='async';
302
306
  }else{
@@ -374,7 +378,7 @@ function contextualForOf(tokens,parentheses){
374
378
 
375
379
  function functionParameterDeclaration(tokens,parentheses){
376
380
  const context=parentheses.at(-1);
377
- if(!Number.isInteger(context?.openTokenIndex))return false;
381
+ if(!is.integer(context?.openTokenIndex))return false;
378
382
  for(let index=context.openTokenIndex-1;index>=0;index-=1){
379
383
  const token=tokens[index];
380
384
  if(new Set([';','{','}']).has(token.value))return false;
@@ -715,7 +719,7 @@ function importRecord(kind,token){
715
719
  }
716
720
 
717
721
  export function scanModuleImports(source,{importer='<module>'}={}){
718
- if(typeof source!=='string')throw new TypeError('scanModuleImports source must be a string.');
722
+ if(!is.string(source))throw new TypeError('scanModuleImports source must be a string.');
719
723
  return moduleImportsFromTokens(tokenize(source),importer);
720
724
  }
721
725
 
@@ -793,12 +797,12 @@ function moduleImportsFromTokens(tokens,importer){
793
797
  }
794
798
 
795
799
  export function versionAssetUrl(value,version=SDK_VERSION){
796
- if(typeof value!=='string')return value;
800
+ if(!is.string(value))return value;
797
801
  return applyReferenceEdits(value,assetUrlVersionEdits(value,version));
798
802
  }
799
803
 
800
804
  function assetUrlVersionEdits(value,version){
801
- if(typeof value!=='string'||!value||!version||value.startsWith('#')
805
+ if(!is.string(value)||!value||!version||value.startsWith('#')
802
806
  ||value.startsWith('//')||/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(value)
803
807
  ||/^\s/u.test(value))return [];
804
808
  const fragmentStart=value.indexOf('#');
@@ -948,7 +952,7 @@ function literalResourceFetch(argumentsList){
948
952
  }
949
953
 
950
954
  function reportAssetReference(onReference,url,kind,baseKind){
951
- if(typeof onReference==='function')onReference({url,kind,baseHref:null,...(baseKind?{baseKind}:{})});
955
+ if(is.function(onReference))onReference({url,kind,baseHref:null,...(baseKind?{baseKind}:{})});
952
956
  }
953
957
 
954
958
  function javascriptReferenceEdits(source,version,onReference){
@@ -1157,7 +1161,7 @@ function importMapReferenceEdits(source,version,onReference){
1157
1161
  }
1158
1162
 
1159
1163
  export function rewriteAssetReferences(source,{filePath,version=SDK_VERSION,onReference}={}){
1160
- if(typeof source!=='string')throw new TypeError('Asset reference source must be a string.');
1164
+ if(!is.string(source))throw new TypeError('Asset reference source must be a string.');
1161
1165
  const extension=path.extname(String(filePath??'')).toLowerCase();
1162
1166
  if(extension==='.js'||extension==='.mjs'){
1163
1167
  return applyReferenceEdits(source,javascriptReferenceEdits(source,version,onReference));
@@ -1192,7 +1196,7 @@ function importMapUrlVersionEdits(value,version){
1192
1196
  }
1193
1197
 
1194
1198
  function validateInventory(files){
1195
- if(!Array.isArray(files))throw new TypeError('buildImportMap files must be an array.');
1199
+ if(!is.array(files))throw new TypeError('buildImportMap files must be an array.');
1196
1200
  const exact=new Set();
1197
1201
  for(const value of [...files].sort(compareText)){
1198
1202
  const relative=safeRelativePath(value,'runtime inventory path');
@@ -1248,6 +1252,11 @@ export async function buildImportMap({files,signal,version=SDK_VERSION}={}){
1248
1252
  );
1249
1253
  }
1250
1254
  if(inventory.has('dependencies/strong-type/index.js')){
1255
+ registerSpecifier(
1256
+ namedRegistry,
1257
+ 'strong-type',
1258
+ './arcane/dependencies/strong-type/index.js'
1259
+ );
1251
1260
  registerSpecifier(
1252
1261
  namedRegistry,
1253
1262
  './node_modules/strong-type/index.js',
@@ -1343,7 +1352,7 @@ export async function readWorkspaceAssetVersion(workspaceRoot){
1343
1352
  }
1344
1353
  const document=JSON.parse(source);
1345
1354
  const version=document?.sdk?.version;
1346
- return typeof version==='string'&&version?version:SDK_VERSION;
1355
+ return is.string(version)&&version?version:SDK_VERSION;
1347
1356
  }
1348
1357
 
1349
1358
  function asciiLower(value){
@@ -1431,7 +1440,7 @@ function decodeStructuralAttribute(value,label){
1431
1440
  return {amp:'&',apos:"'",gt:'>',lt:'<',quot:'"'}[asciiLower(named)];
1432
1441
  }
1433
1442
  const point=Number.parseInt(decimal??hex,decimal?10:16);
1434
- if(!Number.isSafeInteger(point)||point<=0||point>0x10ffff
1443
+ if(!is.safeInteger(point)||point<=0||point>0x10ffff
1435
1444
  ||(point>=0xd800&&point<=0xdfff)){
1436
1445
  fail(`Application HTML ${label} contains an invalid character reference.`);
1437
1446
  }
@@ -1715,7 +1724,7 @@ function htmlAttributeView(value){
1715
1724
  const point=entity[1]||entity[2]
1716
1725
  ?Number.parseInt(entity[1]??entity[2],entity[1]?10:16)
1717
1726
  :null;
1718
- if(point!==null&&(!Number.isSafeInteger(point)||point<=0||point>0x10ffff))return null;
1727
+ if(point!==null&&(!is.safeInteger(point)||point<=0||point>0x10ffff))return null;
1719
1728
  const character=point===null
1720
1729
  ?{amp:'&',apos:"'",gt:'>',lt:'<',quot:'"'}[asciiLower(entity[3])]
1721
1730
  :String.fromCodePoint(point);
@@ -1787,7 +1796,7 @@ function htmlReferenceEdits(source,version,onReference){
1787
1796
  const baseHref=base?structuralAttribute(parseTagAttributes(base.open),'href','base'):null;
1788
1797
  if(baseHref&&(/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(baseHref)||baseHref.startsWith('//')))version=null;
1789
1798
  function reportHtmlReference(reference){
1790
- if(typeof onReference==='function')onReference({...reference,baseHref});
1799
+ if(is.function(onReference))onReference({...reference,baseHref});
1791
1800
  }
1792
1801
  function addNestedEdits(offset,nested){
1793
1802
  for(const edit of nested)edits.push({...edit,start:offset+edit.start,end:offset+edit.end});
@@ -2114,7 +2123,7 @@ async function writeGeneratedFiles({root,files,signal,onEvent}){
2114
2123
  }
2115
2124
 
2116
2125
  function resolvedAppRoot(workspaceRoot,appId,appRoot){
2117
- if(typeof appId!=='string'||appId.trim()===''){
2126
+ if(!is.string(appId)||appId.trim()===''){
2118
2127
  throw new TypeError('Import-map app id must be a nonempty string.');
2119
2128
  }
2120
2129
  const resolved=path.resolve(appRoot??path.join(workspaceRoot,'apps',appId));
@@ -2128,13 +2137,13 @@ export async function createApplicationTestImportMapContext({
2128
2137
  imports={},
2129
2138
  signal
2130
2139
  }={}){
2131
- if(typeof applicationRoot!=='string'||applicationRoot.trim()===''){
2140
+ if(!is.string(applicationRoot)||applicationRoot.trim()===''){
2132
2141
  throw new TypeError('applicationRoot must be a nonempty string.');
2133
2142
  }
2134
2143
  if(!['source','dist','test'].includes(boundary)){
2135
2144
  throw new TypeError('boundary must be source, dist, or test.');
2136
2145
  }
2137
- if(imports===null||typeof imports!=='object'||Array.isArray(imports)){
2146
+ if(imports===null||!is.object(imports)||is.array(imports)){
2138
2147
  throw new TypeError('imports must be a plain object.');
2139
2148
  }
2140
2149
  throwIfAborted(signal);
@@ -2158,7 +2167,7 @@ export async function createApplicationTestImportMapContext({
2158
2167
  const selectedImports={};
2159
2168
  for(const [specifier,target] of Object.entries(imports)){
2160
2169
  throwIfAborted(signal);
2161
- if(typeof specifier!=='string'||specifier===''||typeof target!=='string'
2170
+ if(!is.string(specifier)||specifier===''||!is.string(target)
2162
2171
  ||!target.startsWith('./')){
2163
2172
  fail(`Application test import-map entry is invalid: ${String(specifier)}.`);
2164
2173
  }
@@ -2191,10 +2200,10 @@ export async function readApplicationTestImportMapContext({
2191
2200
  applicationRoot,
2192
2201
  signal
2193
2202
  }={}){
2194
- if(typeof workspaceRoot!=='string'||workspaceRoot.trim()===''){
2203
+ if(!is.string(workspaceRoot)||workspaceRoot.trim()===''){
2195
2204
  throw new TypeError('workspaceRoot must be a nonempty string.');
2196
2205
  }
2197
- if(typeof applicationRoot!=='string'||applicationRoot.trim()===''){
2206
+ if(!is.string(applicationRoot)||applicationRoot.trim()===''){
2198
2207
  throw new TypeError('applicationRoot must be a nonempty string.');
2199
2208
  }
2200
2209
  throwIfAborted(signal);
@@ -2222,9 +2231,9 @@ export async function readApplicationTestImportMapContext({
2222
2231
  catch(error){
2223
2232
  fail(`Application test import-map artifact is not valid JSON: ${error.message}`);
2224
2233
  }
2225
- if(document===null||typeof document!=='object'||Array.isArray(document)
2226
- ||document.imports===null||typeof document.imports!=='object'
2227
- ||Array.isArray(document.imports)){
2234
+ if(document===null||!is.object(document)||is.array(document)
2235
+ ||document.imports===null||!is.object(document.imports)
2236
+ ||is.array(document.imports)){
2228
2237
  fail('Application test import-map artifact must contain an imports object.');
2229
2238
  }
2230
2239
  return createApplicationTestImportMapContext({
@@ -2244,7 +2253,7 @@ async function generateImportMapUnlocked({
2244
2253
  signal,
2245
2254
  onEvent
2246
2255
  }={}){
2247
- if(typeof workspaceRoot!=='string'||workspaceRoot.trim()===''){
2256
+ if(!is.string(workspaceRoot)||workspaceRoot.trim()===''){
2248
2257
  throw new TypeError('generateImportMap workspaceRoot must be a nonempty string.');
2249
2258
  }
2250
2259
  throwIfAborted(signal);
@@ -2347,7 +2356,7 @@ async function generateImportMapUnlocked({
2347
2356
 
2348
2357
  export async function generateImportMap(options={}){
2349
2358
  const {workspaceRoot}=options??{};
2350
- if(typeof workspaceRoot!=='string'||workspaceRoot.trim()===''){
2359
+ if(!is.string(workspaceRoot)||workspaceRoot.trim()===''){
2351
2360
  throw new TypeError('generateImportMap workspaceRoot must be a nonempty string.');
2352
2361
  }
2353
2362
  return generateImportMapUnlocked(options);
@@ -1,3 +1,4 @@
1
+ import Is from 'strong-type';
1
2
  import {lstat,readFile,realpath,stat,writeFile} from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import {materializeWorkspaceRuntimeContent} from './workspace-runtime.mjs';
@@ -8,6 +9,8 @@ import {
8
9
  resolveSdkPackageDeclaration
9
10
  } from './workspace.mjs';
10
11
 
12
+ const is = new Is(false);
13
+
11
14
  function fail(message,code='ARCANE_INSTALLED_SDK_RUNTIME_INVALID'){
12
15
  const error=new Error(message);
13
16
  error.code=code;
@@ -22,7 +25,7 @@ function throwIfAborted(signal){
22
25
  }
23
26
 
24
27
  async function canonicalWorkspaceRoot(workspaceRoot){
25
- if(typeof workspaceRoot!=='string'||!workspaceRoot.trim()){
28
+ if(!is.string(workspaceRoot)||!workspaceRoot.trim()){
26
29
  fail('workspaceRoot is required to materialize an installed SDK runtime.');
27
30
  }
28
31
  const requested=path.resolve(workspaceRoot);
@@ -1,9 +1,12 @@
1
+ import Is from 'strong-type';
1
2
  import {stat} from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import {pathToFileURL} 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
  export const INTEGRATED_TOOLCHAIN_PROTOCOL='arcane-integrated-toolchain/1';
8
11
  export const ARCANE_INTEGRATED_PROVIDER_RELATIVE_PATH=[
9
12
  'tools',
@@ -19,7 +22,7 @@ async function emit(onEvent,event){
19
22
  }
20
23
 
21
24
  async function selectedProvider(arcaneRoot){
22
- if(typeof arcaneRoot!=='string'||!arcaneRoot.trim()){
25
+ if(!is.string(arcaneRoot)||!arcaneRoot.trim()){
23
26
  fail('An explicit Arcane OS root is required for shared development.',ERROR_CODES.usage);
24
27
  }
25
28
  const toolchainRoot=path.resolve(arcaneRoot);
@@ -53,10 +56,10 @@ async function selectedProvider(arcaneRoot){
53
56
  async function importedProvider(providerPath){
54
57
  const namespace=await import(pathToFileURL(providerPath).href);
55
58
  const provider=namespace?.integratedDevelopmentProvider;
56
- if(!provider||typeof provider!=='object'
57
- ||typeof provider.describe!=='function'
58
- ||typeof provider.prepare!=='function'
59
- ||typeof provider.execute!=='function'){
59
+ if(!provider||!is.object(provider)
60
+ ||!is.function(provider.describe)
61
+ ||!is.function(provider.prepare)
62
+ ||!is.function(provider.execute)){
60
63
  fail('The integrated provider must export integratedDevelopmentProvider.');
61
64
  }
62
65
  return provider;
@@ -69,7 +72,7 @@ export async function loadArcaneIntegratedProvider({
69
72
  run=runProcess
70
73
  }={}){
71
74
  throwIfAborted(signal);
72
- if(typeof run!=='function'){
75
+ if(!is.function(run)){
73
76
  fail('The integrated provider process runner must be a function.',ERROR_CODES.usage);
74
77
  }
75
78
  await emit(onEvent,{
@@ -1,8 +1,11 @@
1
+ import Is from 'strong-type';
1
2
  import {spawn} from 'node:child_process';
2
3
  import path from 'node:path';
3
4
  import {TextDecoder} from 'node:util';
4
5
  import {ArcaneError,ERROR_CODES} from './errors.mjs';
5
6
 
7
+ const is = new Is(false);
8
+
6
9
  export const RESEND_CREDENTIAL_TARGET_PREFIX='ArcaneOSSDK/mail/resend/';
7
10
 
8
11
  const CREDENTIAL_STORE='windows-credential-manager';
@@ -264,7 +267,7 @@ function assertNotAborted(signal){
264
267
  }
265
268
 
266
269
  export function validateMailCredentialProfile(profile){
267
- if(typeof profile!=='string'||profile.length>MAX_PROFILE_LENGTH
270
+ if(!is.string(profile)||profile.length>MAX_PROFILE_LENGTH
268
271
  ||!PROFILE_PATTERN.test(profile)){
269
272
  throw usageError(
270
273
  'A credential profile must be 1-64 lowercase letters, digits, dots, underscores, or hyphens, and must begin and end with a letter or digit.'
@@ -278,7 +281,7 @@ export function mailCredentialTarget(profile){
278
281
  }
279
282
 
280
283
  function validateSecret(secret){
281
- if(typeof secret!=='string'||!SECRET_PATTERN.test(secret)
284
+ if(!is.string(secret)||!SECRET_PATTERN.test(secret)
282
285
  ||Buffer.byteLength(secret,'utf8')>MAX_CREDENTIAL_BYTES){
283
286
  throw usageError(
284
287
  'A Resend API key must be a nonempty printable ASCII string no larger than 2,560 bytes.'
@@ -292,7 +295,7 @@ function validatePlatform(platform){
292
295
  }
293
296
 
294
297
  function validateTimeout(timeoutMs){
295
- if(!Number.isSafeInteger(timeoutMs)||timeoutMs<1||timeoutMs>MAX_HELPER_TIMEOUT_MS){
298
+ if(!is.safeInteger(timeoutMs)||timeoutMs<1||timeoutMs>MAX_HELPER_TIMEOUT_MS){
296
299
  throw usageError(
297
300
  `Credential helper timeout must be an integer from 1 through ${String(MAX_HELPER_TIMEOUT_MS)} milliseconds.`
298
301
  );
@@ -301,7 +304,7 @@ function validateTimeout(timeoutMs){
301
304
  }
302
305
 
303
306
  function powershellExecutable(systemRoot){
304
- if(typeof systemRoot!=='string'||systemRoot.length===0
307
+ if(!is.string(systemRoot)||systemRoot.length===0
305
308
  ||systemRoot.includes('\0')||!path.win32.isAbsolute(systemRoot)){
306
309
  throw unavailableError();
307
310
  }
@@ -327,7 +330,7 @@ function helperArguments(){
327
330
  }
328
331
 
329
332
  function helperEnvironment(systemRoot,temporaryDirectory){
330
- if(typeof temporaryDirectory!=='string'||temporaryDirectory.length===0
333
+ if(!is.string(temporaryDirectory)||temporaryDirectory.length===0
331
334
  ||temporaryDirectory.includes('\0')||!path.win32.isAbsolute(temporaryDirectory)){
332
335
  throw unavailableError();
333
336
  }
@@ -352,7 +355,7 @@ function parseResponse(buffer){
352
355
  try{
353
356
  responseText=buffer.toString('utf8');
354
357
  const response=JSON.parse(responseText);
355
- if(response===null||typeof response!=='object'||Array.isArray(response)
358
+ if(response===null||!is.object(response)||is.array(response)
356
359
  ||response.ok!==true){
357
360
  throw operationError();
358
361
  }
@@ -374,7 +377,7 @@ function runCredentialProcess({
374
377
  signal,
375
378
  timeoutMs
376
379
  }){
377
- if(typeof spawnImpl!=='function')throw usageError('spawnImpl must be a function.');
380
+ if(!is.function(spawnImpl))throw usageError('spawnImpl must be a function.');
378
381
  let input=stdin;
379
382
  return new Promise(function executeCredentialHelper(resolve,reject){
380
383
  let child;
@@ -494,7 +497,7 @@ async function runWindowsCredentialHelper(request,{
494
497
  }={}){
495
498
  validatePlatform(platform);
496
499
  assertNotAborted(signal);
497
- if(typeof runner!=='function')throw usageError('runner must be a function.');
500
+ if(!is.function(runner))throw usageError('runner must be a function.');
498
501
  const executable=powershellExecutable(systemRoot);
499
502
  const args=helperArguments();
500
503
  const boundedTimeout=validateTimeout(timeoutMs);
@@ -527,7 +530,7 @@ async function runWindowsCredentialHelper(request,{
527
530
  invocation.stdin='';
528
531
  requestText='';
529
532
  }
530
- if(typeof output==='string')output=Buffer.from(output,'utf8');
533
+ if(is.string(output))output=Buffer.from(output,'utf8');
531
534
  if(!Buffer.isBuffer(output))throw operationError();
532
535
  if(output.length>MAX_HELPER_OUTPUT_BYTES){
533
536
  output.fill(0);
@@ -557,7 +560,7 @@ function credentialStatus(profile,exists){
557
560
  }
558
561
 
559
562
  function validateOptions(options){
560
- if(options===null||typeof options!=='object'||Array.isArray(options)){
563
+ if(options===null||!is.object(options)||is.array(options)){
561
564
  throw usageError('Mail credential options must be an object.');
562
565
  }
563
566
  return options;
@@ -601,7 +604,7 @@ export async function setMailCredential(options={}){
601
604
  }
602
605
 
603
606
  function decodeSecret(value){
604
- if(typeof value!=='string'||value.length===0||value.length%4!==0
607
+ if(!is.string(value)||value.length===0||value.length%4!==0
605
608
  ||!BASE64_PATTERN.test(value)){
606
609
  throw operationError();
607
610
  }
@@ -648,7 +651,7 @@ export async function getMailCredentialStatus(options={}){
648
651
  {operation:'status',target},
649
652
  helperOptions(options)
650
653
  );
651
- if(typeof response.configured!=='boolean')throw operationError();
654
+ if(!is.boolean(response.configured))throw operationError();
652
655
  return credentialStatus(validatedProfile,response.configured);
653
656
  }
654
657
 
@@ -660,7 +663,7 @@ export async function deleteMailCredential(options={}){
660
663
  {operation:'delete',target},
661
664
  helperOptions(options)
662
665
  );
663
- if(typeof response.deleted!=='boolean'||response.configured!==false){
666
+ if(!is.boolean(response.deleted)||response.configured!==false){
664
667
  throw operationError();
665
668
  }
666
669
  return credentialStatus(validatedProfile,false);