arcane-os 0.5.17 → 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 (183) hide show
  1. package/CHANGELOG.md +24 -0
  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 +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 +5 -1
  17. package/docs/reference/ai/browser-speech.md +198 -5
  18. package/docs/reference/availability-and-normalization.md +40 -0
  19. package/docs/reference/inventory/package-api.json +1 -1
  20. package/docs/reference/inventory/runtime-components.json +1 -1
  21. package/docs/reference/inventory/runtime-modules.json +20 -24
  22. package/docs/reference/protocols.md +1 -1
  23. package/docs/reference/runtime-modules.md +125 -35
  24. package/docs/reference/sdk-api.md +23 -7
  25. package/examples/wasm-ai-demo/index.html +1 -0
  26. package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
  27. package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
  28. package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
  29. package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
  30. package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
  31. package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
  32. package/node_modules/strong-type/README.md +42 -24
  33. package/node_modules/strong-type/benchmark/core.js +324 -0
  34. package/node_modules/strong-type/index.js +207 -82
  35. package/node_modules/strong-type/package.json +14 -6
  36. package/package.json +3 -3
  37. package/runtime/arcane/components/app-bar.html +5 -2
  38. package/runtime/arcane/components/assistant-panel.html +5 -2
  39. package/runtime/arcane/components/chart.html +15 -12
  40. package/runtime/arcane/components/chat.html +117 -138
  41. package/runtime/arcane/components/dashboard-config.html +6 -3
  42. package/runtime/arcane/components/data-view.html +4 -1
  43. package/runtime/arcane/components/directory-picker.html +5 -2
  44. package/runtime/arcane/components/document-inspector.html +6 -3
  45. package/runtime/arcane/components/file-drop.html +5 -2
  46. package/runtime/arcane/components/file-inspector.html +9 -6
  47. package/runtime/arcane/components/file-manager.html +16 -13
  48. package/runtime/arcane/components/local-ai-status.html +5 -2
  49. package/runtime/arcane/components/markdown-document.html +14 -11
  50. package/runtime/arcane/components/markdown-editor.html +4 -1
  51. package/runtime/arcane/components/modal.html +4 -1
  52. package/runtime/arcane/components/output-panel.html +6 -3
  53. package/runtime/arcane/components/preferences-form.html +4 -1
  54. package/runtime/arcane/components/record-timeline.html +8 -5
  55. package/runtime/arcane/components/relationship-board.html +9 -6
  56. package/runtime/arcane/components/source-code-viewer.html +12 -9
  57. package/runtime/arcane/components/source-explanation.html +8 -5
  58. package/runtime/arcane/components/speech.html +24 -21
  59. package/runtime/arcane/components/summary-strip.html +4 -1
  60. package/runtime/arcane/components/task-progress.html +6 -3
  61. package/runtime/arcane/components/terminal-workspace.html +4 -1
  62. package/runtime/arcane/components/voice-transcription.html +11 -8
  63. package/runtime/arcane/components/web-navigator.html +5 -2
  64. package/runtime/arcane/entities/ApiModelRecord.js +5 -2
  65. package/runtime/arcane/entities/Calculation.js +5 -2
  66. package/runtime/arcane/entities/Chat.js +33 -33
  67. package/runtime/arcane/entities/CommunicationMessage.js +4 -1
  68. package/runtime/arcane/entities/CommunicationThread.js +4 -1
  69. package/runtime/arcane/entities/File.js +1 -1
  70. package/runtime/arcane/entities/Image.js +8 -5
  71. package/runtime/arcane/entities/IntentEnvelope.js +20 -17
  72. package/runtime/arcane/entities/Preference.js +9 -6
  73. package/runtime/arcane/entities/Theme.js +5 -2
  74. package/runtime/arcane/entities/User.js +11 -11
  75. package/runtime/arcane/entities/Weather.js +5 -2
  76. package/runtime/arcane/modules/AI.js +491 -165
  77. package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
  78. package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
  79. package/runtime/arcane/modules/AIProviderRuntime.js +88 -85
  80. package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
  81. package/runtime/arcane/modules/AIRuntimeState.js +29 -26
  82. package/runtime/arcane/modules/AnsiText.js +4 -1
  83. package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
  84. package/runtime/arcane/modules/AppDataScope.js +8 -5
  85. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
  86. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
  87. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
  88. package/runtime/arcane/modules/AsyncBoundary.js +13 -10
  89. package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
  90. package/runtime/arcane/modules/CalculatorEngine.js +5 -2
  91. package/runtime/arcane/modules/ChatRecords.js +18 -15
  92. package/runtime/arcane/modules/CommunicationAppController.js +9 -6
  93. package/runtime/arcane/modules/CommunicationHub.js +9 -6
  94. package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
  95. package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
  96. package/runtime/arcane/modules/ComponentContracts.js +56 -53
  97. package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
  98. package/runtime/arcane/modules/ConversationActionItems.js +15 -12
  99. package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
  100. package/runtime/arcane/modules/ConversationTimebox.js +28 -25
  101. package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
  102. package/runtime/arcane/modules/DBLS.js +7 -4
  103. package/runtime/arcane/modules/DBOPFS.js +7 -7
  104. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
  105. package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
  106. package/runtime/arcane/modules/DirectoryPicker.js +8 -5
  107. package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
  108. package/runtime/arcane/modules/DocumentNavigation.js +7 -4
  109. package/runtime/arcane/modules/Errors.js +28 -25
  110. package/runtime/arcane/modules/HTMLImport.js +7 -4
  111. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
  112. package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
  113. package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
  114. package/runtime/arcane/modules/MD.js +1 -1
  115. package/runtime/arcane/modules/Mail.js +46 -43
  116. package/runtime/arcane/modules/MailOutbox.mjs +48 -45
  117. package/runtime/arcane/modules/MailTransport.mjs +29 -26
  118. package/runtime/arcane/modules/MemoryRecords.js +7 -4
  119. package/runtime/arcane/modules/MessageAdvisory.js +6 -3
  120. package/runtime/arcane/modules/ModelDefinition.js +7 -4
  121. package/runtime/arcane/modules/Ollama.js +6 -3
  122. package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
  123. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
  124. package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
  125. package/runtime/arcane/modules/PreferenceStore.js +20 -17
  126. package/runtime/arcane/modules/PreparedSpeech.js +485 -0
  127. package/runtime/arcane/modules/Questionnaire.js +6 -3
  128. package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
  129. package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
  130. package/runtime/arcane/modules/RecordReviewStore.js +12 -9
  131. package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
  132. package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
  133. package/runtime/arcane/modules/ScreenCapture.js +20 -17
  134. package/runtime/arcane/modules/SpeechPlayback.js +29 -26
  135. package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
  136. package/runtime/arcane/modules/SystemAppearance.js +5 -2
  137. package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
  138. package/runtime/arcane/modules/TerminalClient.js +14 -11
  139. package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
  140. package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
  141. package/runtime/arcane/modules/ThemeManager.js +5 -2
  142. package/runtime/arcane/modules/TimeGuard.js +1 -1
  143. package/runtime/arcane/modules/ToolCallRouter.js +11 -8
  144. package/runtime/arcane/modules/WaitForComponent.js +19 -16
  145. package/runtime/arcane/modules/YouTubeMedia.js +4 -1
  146. package/runtime/strong-type/index.js +1276 -352
  147. package/runtime/strong-type/package.json +69 -45
  148. package/src/app-descriptor.mjs +17 -14
  149. package/src/application-tests.mjs +4 -1
  150. package/src/cli/main.mjs +8 -5
  151. package/src/constants.mjs +4 -1
  152. package/src/dev-server.mjs +8 -5
  153. package/src/doctor.mjs +5 -2
  154. package/src/dom-event-instrumentation.mjs +24 -20
  155. package/src/errors.mjs +7 -3
  156. package/src/event-manager.mjs +111 -108
  157. package/src/event-queue.mjs +8 -5
  158. package/src/events.mjs +12 -9
  159. package/src/import-map.mjs +35 -27
  160. package/src/installed-sdk-runtime.mjs +4 -1
  161. package/src/integrated-provider-loader.mjs +9 -6
  162. package/src/mail-credentials.mjs +16 -13
  163. package/src/mail-server.mjs +53 -50
  164. package/src/mail.mjs +18 -15
  165. package/src/native-plan.mjs +12 -9
  166. package/src/native-provider-loader.mjs +7 -4
  167. package/src/packager/core.mjs +18 -15
  168. package/src/process.mjs +6 -3
  169. package/src/release-bundle.mjs +14 -11
  170. package/src/runtime.mjs +4 -1
  171. package/src/scaffold.mjs +9 -6
  172. package/src/sdk-browser-runtime.mjs +4 -1
  173. package/src/source-server.mjs +17 -14
  174. package/src/targets/index.mjs +5 -2
  175. package/src/templates/workspace-template.mjs +13 -6
  176. package/src/testing-loader.mjs +7 -4
  177. package/src/testing.mjs +10 -7
  178. package/src/toolchain.mjs +13 -10
  179. package/src/update-check.mjs +9 -6
  180. package/src/workspace-operation-lock.mjs +14 -11
  181. package/src/workspace-runtime.mjs +4 -1
  182. package/src/workspace.mjs +17 -14
  183. 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
 
@@ -38,7 +41,7 @@ function throwIfAborted(signal){
38
41
  }
39
42
 
40
43
  async function emit(onEvent,event){
41
- if(typeof onEvent!=='function')return null;
44
+ if(!is.function(onEvent))return null;
42
45
  try{
43
46
  await onEvent(event);
44
47
  return null;
@@ -56,7 +59,7 @@ function compareText(left,right){
56
59
  }
57
60
 
58
61
  function safeRelativePath(value,label='path'){
59
- if(typeof value!=='string'||!value||value.includes('\\')||value.includes('\0')
62
+ if(!is.string(value)||!value||value.includes('\\')||value.includes('\0')
60
63
  ||path.posix.isAbsolute(value)||path.posix.normalize(value)!==value
61
64
  ||value==='.'||value.startsWith('../')||value.includes('/../')){
62
65
  fail(`Import-map ${label} is unsafe: ${String(value)}.`);
@@ -66,7 +69,7 @@ function safeRelativePath(value,label='path'){
66
69
 
67
70
  function normalizedDocumentPaths(entry,documents){
68
71
  if(documents===undefined)return [entry];
69
- if(!Array.isArray(documents)||documents.length===0){
72
+ if(!is.array(documents)||documents.length===0){
70
73
  fail('Import-map documents must be a non-empty array of application-relative paths.');
71
74
  }
72
75
  const normalized=[];
@@ -266,7 +269,7 @@ function classHeader(tokens){
266
269
 
267
270
  function functionHeader(tokens,closingParenthesis,enclosingBraceKind){
268
271
  const openIndex=closingParenthesis?.openTokenIndex;
269
- if(!Number.isInteger(openIndex))return null;
272
+ if(!is.integer(openIndex))return null;
270
273
  for(let index=openIndex-1;index>=0;index-=1){
271
274
  const token=tokens[index];
272
275
  if(new Set([';', '{', '}']).has(token.value))break;
@@ -297,7 +300,7 @@ function arrowFunctionContext(tokens){
297
300
  if(arrow?.value!=='=>')return null;
298
301
  const parameter=tokens.at(-2);
299
302
  let async=false;
300
- if(parameter?.value===')'&&Number.isInteger(parameter.openTokenIndex)){
303
+ if(parameter?.value===')'&&is.integer(parameter.openTokenIndex)){
301
304
  const beforeOpen=tokens[parameter.openTokenIndex-1];
302
305
  async=beforeOpen?.type==='identifier'&&beforeOpen.value==='async';
303
306
  }else{
@@ -375,7 +378,7 @@ function contextualForOf(tokens,parentheses){
375
378
 
376
379
  function functionParameterDeclaration(tokens,parentheses){
377
380
  const context=parentheses.at(-1);
378
- if(!Number.isInteger(context?.openTokenIndex))return false;
381
+ if(!is.integer(context?.openTokenIndex))return false;
379
382
  for(let index=context.openTokenIndex-1;index>=0;index-=1){
380
383
  const token=tokens[index];
381
384
  if(new Set([';','{','}']).has(token.value))return false;
@@ -716,7 +719,7 @@ function importRecord(kind,token){
716
719
  }
717
720
 
718
721
  export function scanModuleImports(source,{importer='<module>'}={}){
719
- 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.');
720
723
  return moduleImportsFromTokens(tokenize(source),importer);
721
724
  }
722
725
 
@@ -794,12 +797,12 @@ function moduleImportsFromTokens(tokens,importer){
794
797
  }
795
798
 
796
799
  export function versionAssetUrl(value,version=SDK_VERSION){
797
- if(typeof value!=='string')return value;
800
+ if(!is.string(value))return value;
798
801
  return applyReferenceEdits(value,assetUrlVersionEdits(value,version));
799
802
  }
800
803
 
801
804
  function assetUrlVersionEdits(value,version){
802
- if(typeof value!=='string'||!value||!version||value.startsWith('#')
805
+ if(!is.string(value)||!value||!version||value.startsWith('#')
803
806
  ||value.startsWith('//')||/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(value)
804
807
  ||/^\s/u.test(value))return [];
805
808
  const fragmentStart=value.indexOf('#');
@@ -949,7 +952,7 @@ function literalResourceFetch(argumentsList){
949
952
  }
950
953
 
951
954
  function reportAssetReference(onReference,url,kind,baseKind){
952
- if(typeof onReference==='function')onReference({url,kind,baseHref:null,...(baseKind?{baseKind}:{})});
955
+ if(is.function(onReference))onReference({url,kind,baseHref:null,...(baseKind?{baseKind}:{})});
953
956
  }
954
957
 
955
958
  function javascriptReferenceEdits(source,version,onReference){
@@ -1158,7 +1161,7 @@ function importMapReferenceEdits(source,version,onReference){
1158
1161
  }
1159
1162
 
1160
1163
  export function rewriteAssetReferences(source,{filePath,version=SDK_VERSION,onReference}={}){
1161
- 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.');
1162
1165
  const extension=path.extname(String(filePath??'')).toLowerCase();
1163
1166
  if(extension==='.js'||extension==='.mjs'){
1164
1167
  return applyReferenceEdits(source,javascriptReferenceEdits(source,version,onReference));
@@ -1193,7 +1196,7 @@ function importMapUrlVersionEdits(value,version){
1193
1196
  }
1194
1197
 
1195
1198
  function validateInventory(files){
1196
- 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.');
1197
1200
  const exact=new Set();
1198
1201
  for(const value of [...files].sort(compareText)){
1199
1202
  const relative=safeRelativePath(value,'runtime inventory path');
@@ -1249,6 +1252,11 @@ export async function buildImportMap({files,signal,version=SDK_VERSION}={}){
1249
1252
  );
1250
1253
  }
1251
1254
  if(inventory.has('dependencies/strong-type/index.js')){
1255
+ registerSpecifier(
1256
+ namedRegistry,
1257
+ 'strong-type',
1258
+ './arcane/dependencies/strong-type/index.js'
1259
+ );
1252
1260
  registerSpecifier(
1253
1261
  namedRegistry,
1254
1262
  './node_modules/strong-type/index.js',
@@ -1344,7 +1352,7 @@ export async function readWorkspaceAssetVersion(workspaceRoot){
1344
1352
  }
1345
1353
  const document=JSON.parse(source);
1346
1354
  const version=document?.sdk?.version;
1347
- return typeof version==='string'&&version?version:SDK_VERSION;
1355
+ return is.string(version)&&version?version:SDK_VERSION;
1348
1356
  }
1349
1357
 
1350
1358
  function asciiLower(value){
@@ -1432,7 +1440,7 @@ function decodeStructuralAttribute(value,label){
1432
1440
  return {amp:'&',apos:"'",gt:'>',lt:'<',quot:'"'}[asciiLower(named)];
1433
1441
  }
1434
1442
  const point=Number.parseInt(decimal??hex,decimal?10:16);
1435
- if(!Number.isSafeInteger(point)||point<=0||point>0x10ffff
1443
+ if(!is.safeInteger(point)||point<=0||point>0x10ffff
1436
1444
  ||(point>=0xd800&&point<=0xdfff)){
1437
1445
  fail(`Application HTML ${label} contains an invalid character reference.`);
1438
1446
  }
@@ -1716,7 +1724,7 @@ function htmlAttributeView(value){
1716
1724
  const point=entity[1]||entity[2]
1717
1725
  ?Number.parseInt(entity[1]??entity[2],entity[1]?10:16)
1718
1726
  :null;
1719
- 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;
1720
1728
  const character=point===null
1721
1729
  ?{amp:'&',apos:"'",gt:'>',lt:'<',quot:'"'}[asciiLower(entity[3])]
1722
1730
  :String.fromCodePoint(point);
@@ -1788,7 +1796,7 @@ function htmlReferenceEdits(source,version,onReference){
1788
1796
  const baseHref=base?structuralAttribute(parseTagAttributes(base.open),'href','base'):null;
1789
1797
  if(baseHref&&(/^[A-Za-z][A-Za-z0-9+.-]*:/u.test(baseHref)||baseHref.startsWith('//')))version=null;
1790
1798
  function reportHtmlReference(reference){
1791
- if(typeof onReference==='function')onReference({...reference,baseHref});
1799
+ if(is.function(onReference))onReference({...reference,baseHref});
1792
1800
  }
1793
1801
  function addNestedEdits(offset,nested){
1794
1802
  for(const edit of nested)edits.push({...edit,start:offset+edit.start,end:offset+edit.end});
@@ -2115,7 +2123,7 @@ async function writeGeneratedFiles({root,files,signal,onEvent}){
2115
2123
  }
2116
2124
 
2117
2125
  function resolvedAppRoot(workspaceRoot,appId,appRoot){
2118
- if(typeof appId!=='string'||appId.trim()===''){
2126
+ if(!is.string(appId)||appId.trim()===''){
2119
2127
  throw new TypeError('Import-map app id must be a nonempty string.');
2120
2128
  }
2121
2129
  const resolved=path.resolve(appRoot??path.join(workspaceRoot,'apps',appId));
@@ -2129,13 +2137,13 @@ export async function createApplicationTestImportMapContext({
2129
2137
  imports={},
2130
2138
  signal
2131
2139
  }={}){
2132
- if(typeof applicationRoot!=='string'||applicationRoot.trim()===''){
2140
+ if(!is.string(applicationRoot)||applicationRoot.trim()===''){
2133
2141
  throw new TypeError('applicationRoot must be a nonempty string.');
2134
2142
  }
2135
2143
  if(!['source','dist','test'].includes(boundary)){
2136
2144
  throw new TypeError('boundary must be source, dist, or test.');
2137
2145
  }
2138
- if(imports===null||typeof imports!=='object'||Array.isArray(imports)){
2146
+ if(imports===null||!is.object(imports)||is.array(imports)){
2139
2147
  throw new TypeError('imports must be a plain object.');
2140
2148
  }
2141
2149
  throwIfAborted(signal);
@@ -2159,7 +2167,7 @@ export async function createApplicationTestImportMapContext({
2159
2167
  const selectedImports={};
2160
2168
  for(const [specifier,target] of Object.entries(imports)){
2161
2169
  throwIfAborted(signal);
2162
- if(typeof specifier!=='string'||specifier===''||typeof target!=='string'
2170
+ if(!is.string(specifier)||specifier===''||!is.string(target)
2163
2171
  ||!target.startsWith('./')){
2164
2172
  fail(`Application test import-map entry is invalid: ${String(specifier)}.`);
2165
2173
  }
@@ -2192,10 +2200,10 @@ export async function readApplicationTestImportMapContext({
2192
2200
  applicationRoot,
2193
2201
  signal
2194
2202
  }={}){
2195
- if(typeof workspaceRoot!=='string'||workspaceRoot.trim()===''){
2203
+ if(!is.string(workspaceRoot)||workspaceRoot.trim()===''){
2196
2204
  throw new TypeError('workspaceRoot must be a nonempty string.');
2197
2205
  }
2198
- if(typeof applicationRoot!=='string'||applicationRoot.trim()===''){
2206
+ if(!is.string(applicationRoot)||applicationRoot.trim()===''){
2199
2207
  throw new TypeError('applicationRoot must be a nonempty string.');
2200
2208
  }
2201
2209
  throwIfAborted(signal);
@@ -2223,9 +2231,9 @@ export async function readApplicationTestImportMapContext({
2223
2231
  catch(error){
2224
2232
  fail(`Application test import-map artifact is not valid JSON: ${error.message}`);
2225
2233
  }
2226
- if(document===null||typeof document!=='object'||Array.isArray(document)
2227
- ||document.imports===null||typeof document.imports!=='object'
2228
- ||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)){
2229
2237
  fail('Application test import-map artifact must contain an imports object.');
2230
2238
  }
2231
2239
  return createApplicationTestImportMapContext({
@@ -2245,7 +2253,7 @@ async function generateImportMapUnlocked({
2245
2253
  signal,
2246
2254
  onEvent
2247
2255
  }={}){
2248
- if(typeof workspaceRoot!=='string'||workspaceRoot.trim()===''){
2256
+ if(!is.string(workspaceRoot)||workspaceRoot.trim()===''){
2249
2257
  throw new TypeError('generateImportMap workspaceRoot must be a nonempty string.');
2250
2258
  }
2251
2259
  throwIfAborted(signal);
@@ -2348,7 +2356,7 @@ async function generateImportMapUnlocked({
2348
2356
 
2349
2357
  export async function generateImportMap(options={}){
2350
2358
  const {workspaceRoot}=options??{};
2351
- if(typeof workspaceRoot!=='string'||workspaceRoot.trim()===''){
2359
+ if(!is.string(workspaceRoot)||workspaceRoot.trim()===''){
2352
2360
  throw new TypeError('generateImportMap workspaceRoot must be a nonempty string.');
2353
2361
  }
2354
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);