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
package/src/testing.mjs CHANGED
@@ -1,5 +1,8 @@
1
+ import Is from 'strong-type';
1
2
  import VanillaTest from 'vanilla-test';
2
3
 
4
+ const is = new Is(false);
5
+
3
6
  export const DEFAULT_TEST_TIMEOUT_MS=30_000;
4
7
 
5
8
  const MAX_TEST_TIMEOUT_MS=3_600_000;
@@ -26,14 +29,14 @@ class TestCancellationError extends Error{
26
29
  }
27
30
 
28
31
  function assertName(name){
29
- if(typeof name!=='string'||name.trim()===''){
32
+ if(!is.string(name)||name.trim()===''){
30
33
  throw new TypeError('A test name must be a nonempty string.');
31
34
  }
32
35
  }
33
36
 
34
37
  function testTimeout(options){
35
38
  const timeout=options.timeout??DEFAULT_TEST_TIMEOUT_MS;
36
- if(!Number.isSafeInteger(timeout)||timeout<1||timeout>MAX_TEST_TIMEOUT_MS){
39
+ if(!is.safeInteger(timeout)||timeout<1||timeout>MAX_TEST_TIMEOUT_MS){
37
40
  throw new TypeError(
38
41
  `Test timeout must be an integer from 1 through ${String(MAX_TEST_TIMEOUT_MS)} milliseconds.`
39
42
  );
@@ -43,13 +46,13 @@ function testTimeout(options){
43
46
 
44
47
  function definition(name,optionsOrCallback,maybeCallback){
45
48
  assertName(name);
46
- const hasOptions=typeof optionsOrCallback!=='function';
49
+ const hasOptions=!is.function(optionsOrCallback);
47
50
  const options=hasOptions?optionsOrCallback:{};
48
51
  const callback=hasOptions?maybeCallback:optionsOrCallback;
49
- if(options===null||typeof options!=='object'||Array.isArray(options)){
52
+ if(options===null||!is.object(options)||is.array(options)){
50
53
  throw new TypeError('Test options must be an object.');
51
54
  }
52
- if(typeof callback!=='function'){
55
+ if(!is.function(callback)){
53
56
  throw new TypeError(`Test "${name}" requires a callback function.`);
54
57
  }
55
58
  return {name,timeoutMs:testTimeout(options),callback};
@@ -226,7 +229,7 @@ async function executeDefinition(current,path,outcomes,{parentSignal,state,onPha
226
229
  if(state.cancelled||state.fatalError||controller.signal.aborted){
227
230
  throw state.fatalError??cancellationError(controller.signal);
228
231
  }
229
- if(typeof callback!=='function'){
232
+ if(!is.function(callback)){
230
233
  throw new TypeError('t.after() requires a callback function.');
231
234
  }
232
235
  cleanups.push(callback);
@@ -365,7 +368,7 @@ export async function runRegisteredTests({signal,requireTests=true,onPhase}={}){
365
368
  if(lifecycle!=='collecting'){
366
369
  throw new ReferenceError('The registered test suite can run only once.');
367
370
  }
368
- if(onPhase!==undefined&&typeof onPhase!=='function'){
371
+ if(onPhase!==undefined&&!is.function(onPhase)){
369
372
  throw new TypeError('onPhase must be a function when provided.');
370
373
  }
371
374
  lifecycle='running';
package/src/toolchain.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import Is from 'strong-type';
1
2
  import path from 'node:path';
2
3
  import {readdir,lstat,realpath} from 'node:fs/promises';
3
4
  import {createWorkspace,initWorkspace} from './scaffold.mjs';
@@ -39,6 +40,8 @@ import {
39
40
  verifyAppReleaseBundle
40
41
  } from './release-bundle.mjs';
41
42
 
43
+ const is = new Is(false);
44
+
42
45
  async function emit(onEvent,event){
43
46
  await onEvent?.(event);
44
47
  }
@@ -63,10 +66,10 @@ function isOuterCommitEvent(label,event){
63
66
 
64
67
  function hasCommittedOuterResult(label,result){
65
68
  if(label==='import-map')return result?.committed===true;
66
- if(label==='package')return typeof result?.outputRoot==='string';
67
- if(label==='bundle')return typeof result?.bundlePath==='string';
69
+ if(label==='package')return is.string(result?.outputRoot);
70
+ if(label==='bundle')return is.string(result?.bundlePath);
68
71
  if(label!=='build')return false;
69
- return result?.artifact!=null||typeof result?.release?.outputRoot==='string';
72
+ return result?.artifact!=null||is.string(result?.release?.outputRoot);
70
73
  }
71
74
 
72
75
  async function ownedWork(label,work,{
@@ -245,8 +248,8 @@ async function preparedWorkspace(options){
245
248
  });
246
249
  const external=workspace.workspaceMode==='external';
247
250
  if(external&&(!workspace.sdkInstallation
248
- ||typeof workspace.sdkInstallation.runtimeRoot!=='string'
249
- ||typeof workspace.sdkInstallation.browserRuntimeRoot!=='string')){
251
+ ||!is.string(workspace.sdkInstallation.runtimeRoot)
252
+ ||!is.string(workspace.sdkInstallation.browserRuntimeRoot))){
250
253
  throw new ArcaneError(
251
254
  ERROR_CODES.workspaceInvalid,
252
255
  'Validated external workspace is missing its SDK runtime directories.'
@@ -397,7 +400,7 @@ async function runPreparedApplicationTests(prepared,options={}){
397
400
  export async function testApplication(options={}){
398
401
  throwIfAborted(options.signal);
399
402
  if(operationScope(options)==='shared'){
400
- if(typeof options.testFile!=='string'||!options.testFile){
403
+ if(!is.string(options.testFile)||!options.testFile){
401
404
  throw new ArcaneError(
402
405
  ERROR_CODES.usage,
403
406
  'Shared testing requires one exact repo-relative .test.mjs file.'
@@ -521,7 +524,7 @@ export async function verifyApplication(options={}){
521
524
 
522
525
  export async function bundleApplication(options={}){
523
526
  assertApplicationScope(options,'Release bundling');
524
- if(options.overwrite!==undefined&&typeof options.overwrite!=='boolean'){
527
+ if(options.overwrite!==undefined&&!is.boolean(options.overwrite)){
525
528
  throw new ArcaneError(ERROR_CODES.usage,'overwrite must be a literal boolean.');
526
529
  }
527
530
  const prepared=await preparedWorkspace(options);
@@ -658,7 +661,7 @@ function sameOrDescendant(parent,candidate){
658
661
  }
659
662
 
660
663
  export function resolveNativeBuildOutputRoot({target,workspaceMode,workspaceRoot,outputRoot}={}){
661
- if(typeof target!=='string'||!target||target==='browser'){
664
+ if(!is.string(target)||!target||target==='browser'){
662
665
  throw new ArcaneError(ERROR_CODES.usage,'A native output root requires one explicit native target.');
663
666
  }
664
667
  if(workspaceMode==='integrated'&&!outputRoot){
@@ -696,7 +699,7 @@ function sameCanonicalPath(left,right){
696
699
 
697
700
  export function assertIntegratedNativeToolchain({workspaceMode,workspaceRoot,toolchainRoot,target}={}){
698
701
  if(workspaceMode!=='integrated')return;
699
- if(typeof toolchainRoot!=='string'||!sameCanonicalPath(workspaceRoot,toolchainRoot)){
702
+ if(!is.string(toolchainRoot)||!sameCanonicalPath(workspaceRoot,toolchainRoot)){
700
703
  throw new ArcaneError(
701
704
  ERROR_CODES.policyDenied,
702
705
  `An integrated ${target??'native'} build must use the same Arcane OS checkout for --workspace and --arcane-root.`
@@ -972,7 +975,7 @@ function nativeBuildResult(assembly){
972
975
 
973
976
  function usesWorkspaceNativeAssembly(options){
974
977
  return options.nativeBuilder!=null
975
- &&typeof options.workspaceRoot==='string'
978
+ &&is.string(options.workspaceRoot)
976
979
  &&options.release==null
977
980
  &&options.appReleaseRoot==null
978
981
  &&options.appDescriptor==null;
@@ -1,7 +1,10 @@
1
+ import Is from 'strong-type';
1
2
  import {ArcaneError,ERROR_CODES,throwIfAborted} from './errors.mjs';
2
3
  import {parseSemver} from './packager/core.mjs';
3
4
  import {SDK_NAME,SDK_VERSION} from './constants.mjs';
4
5
 
6
+ const is = new Is(false);
7
+
5
8
  export const SDK_UPDATE_REGISTRY='https://registry.npmjs.org/';
6
9
  export const SDK_UPDATE_TIMEOUT_MS=2500;
7
10
 
@@ -13,7 +16,7 @@ function updateFailure(message,{cause,details}={}){
13
16
  }
14
17
 
15
18
  function plainObject(value){
16
- return value!==null&&typeof value==='object'&&!Array.isArray(value);
19
+ return value!==null&&is.object(value)&&!is.array(value);
17
20
  }
18
21
 
19
22
  function canonicalPackageName(value){
@@ -41,7 +44,7 @@ function registryEndpoint(registry,packageName){
41
44
  }
42
45
 
43
46
  function validateTimeout(value){
44
- if(!Number.isSafeInteger(value)||value<1){
47
+ if(!is.safeInteger(value)||value<1){
45
48
  throw updateFailure('The SDK update timeout must be a positive integer of milliseconds.');
46
49
  }
47
50
  return value;
@@ -76,7 +79,7 @@ async function responseText(response){
76
79
  if(!/^(?:application\/json|application\/[A-Za-z0-9.+-]+\+json)(?:\s*;|$)/iu.test(contentType)){
77
80
  throw updateFailure('The npm registry returned a non-JSON update response.');
78
81
  }
79
- if(!response.body||typeof response.body.getReader!=='function'){
82
+ if(!response.body||!is.function(response.body.getReader)){
80
83
  throw updateFailure('The npm registry returned no readable update response.');
81
84
  }
82
85
  const reader=response.body.getReader();
@@ -113,7 +116,7 @@ function canonicalDistTags(value){
113
116
  const tags=Object.create(null);
114
117
  for(const [tag,version] of entries){
115
118
  if(!DIST_TAG_PATTERN.test(tag)||FORBIDDEN_DIST_TAG_KEYS.has(tag)
116
- ||typeof version!=='string'){
119
+ ||!is.string(version)){
117
120
  throw updateFailure('The npm registry update response contained an invalid dist-tag.');
118
121
  }
119
122
  try{
@@ -180,7 +183,7 @@ export async function checkForSdkUpdate({
180
183
  const selectedPackage=canonicalPackageName(packageName);
181
184
  const selectedRegistry=validateUpdateRegistry(registry);
182
185
  const selectedTimeout=validateTimeout(timeoutMs);
183
- if(typeof fetchImpl!=='function'){
186
+ if(!is.function(fetchImpl)){
184
187
  throw updateFailure('The SDK update HTTP client is unavailable.');
185
188
  }
186
189
  const tag=updateTagForVersion(currentVersion);
@@ -198,7 +201,7 @@ export async function checkForSdkUpdate({
198
201
  signal:linked.signal
199
202
  });
200
203
  throwIfAborted(signal);
201
- if(!response||typeof response.status!=='number'){
204
+ if(!response||!is.number(response.status)){
202
205
  throw updateFailure('The npm registry update response is unavailable.');
203
206
  }
204
207
  if(response.status!==200){
@@ -1,8 +1,11 @@
1
+ import Is from 'strong-type';
1
2
  import {randomUUID} from 'node:crypto';
2
3
  import {constants as FS_CONSTANTS} from 'node:fs';
3
4
  import {lstat,mkdir,open,readFile,realpath,rm} from 'node:fs/promises';
4
5
  import path from 'node:path';
5
6
 
7
+ const is = new Is(false);
8
+
6
9
  const LOCK_DIRECTORY='.arcane';
7
10
  const LOCK_NAME='workspace-operation.lock.json';
8
11
  const LOCK_TTL_MILLISECONDS=6*60*60*1000;
@@ -44,7 +47,7 @@ function samePath(left,right){
44
47
  }
45
48
 
46
49
  function validOperation(value){
47
- return typeof value==='string'&&/^[a-z][a-z0-9._-]*$/u.test(value);
50
+ return is.string(value)&&/^[a-z][a-z0-9._-]*$/u.test(value);
48
51
  }
49
52
 
50
53
  function trackedLeaseState(fields){
@@ -96,7 +99,7 @@ async function ensureLockDirectory(workspace){
96
99
  }
97
100
 
98
101
  function ownerIsAlive(pid){
99
- if(!Number.isSafeInteger(pid)||pid<1)return false;
102
+ if(!is.safeInteger(pid)||pid<1)return false;
100
103
  try{
101
104
  process.kill(pid,0);
102
105
  return true;
@@ -106,21 +109,21 @@ function ownerIsAlive(pid){
106
109
  }
107
110
 
108
111
  function lockDocument(value,{workspaceRoot,now=Date.now()}={}){
109
- if(!value||typeof value!=='object'||Array.isArray(value)
112
+ if(!value||!is.object(value)||is.array(value)
110
113
  ||value.schemaVersion!==1
111
114
  ||value.kind!=='arcane-workspace-operation-lock'
112
115
  ||!validOperation(value.operation)
113
- ||typeof value.nonce!=='string'||!value.nonce
114
- ||!value.owner||!Number.isSafeInteger(value.owner.pid)
115
- ||typeof value.scope!=='string'||!samePath(value.scope,workspaceRoot)
116
- ||typeof value.acquiredAt!=='string'
117
- ||typeof value.expiresAt!=='string'
116
+ ||!is.string(value.nonce)||!value.nonce
117
+ ||!value.owner||!is.safeInteger(value.owner.pid)
118
+ ||!is.string(value.scope)||!samePath(value.scope,workspaceRoot)
119
+ ||!is.string(value.acquiredAt)
120
+ ||!is.string(value.expiresAt)
118
121
  ||value.releaseProcedure!==RELEASE_PROCEDURE
119
122
  ||value.staleRecovery!==STALE_RECOVERY){
120
123
  return null;
121
124
  }
122
125
  const expiresAt=Date.parse(value.expiresAt);
123
- if(!Number.isFinite(expiresAt))return null;
126
+ if(!is.finite(expiresAt))return null;
124
127
  return {...value,expired:expiresAt<=now};
125
128
  }
126
129
 
@@ -227,7 +230,7 @@ async function acquire({
227
230
  onEvent
228
231
  }){
229
232
  throwIfAborted(signal);
230
- if(typeof workspaceRoot!=='string'||!workspaceRoot.trim()){
233
+ if(!is.string(workspaceRoot)||!workspaceRoot.trim()){
231
234
  throw new TypeError('workspaceRoot is required for an Arcane workspace operation lock.');
232
235
  }
233
236
  if(!validOperation(operation)){
@@ -404,7 +407,7 @@ function attachReleaseError(workError,releaseError){
404
407
  }
405
408
 
406
409
  export async function withWorkspaceOperationLock(options,work){
407
- if(typeof work!=='function')throw new TypeError('Workspace operation lock work must be a function.');
410
+ if(!is.function(work))throw new TypeError('Workspace operation lock work must be a function.');
408
411
  const acquired=await acquire(options);
409
412
  let result;
410
413
  let workError;
@@ -1,3 +1,4 @@
1
+ import Is from 'strong-type';
1
2
  import {randomUUID} from 'node:crypto';
2
3
  import {copyFile,lstat,mkdir,readFile,readdir,realpath,rename,rm,writeFile} from 'node:fs/promises';
3
4
  import path from 'node:path';
@@ -6,6 +7,8 @@ import {rewriteAssetReferences} from './import-map.mjs';
6
7
  import {getSdkRoot} from './runtime.mjs';
7
8
  import {getSdkBrowserRuntimeRoot} from './sdk-browser-runtime.mjs';
8
9
 
10
+ const is = new Is(false);
11
+
9
12
  const STAGING_PREFIX='.arcane-runtime-content-staging-';
10
13
  const BACKUP_PREFIX='.arcane-runtime-content-backup-';
11
14
 
@@ -23,7 +26,7 @@ function throwIfAborted(signal){
23
26
  }
24
27
 
25
28
  async function emit(onEvent,event){
26
- if(typeof onEvent==='function')await onEvent(event);
29
+ if(is.function(onEvent))await onEvent(event);
27
30
  }
28
31
 
29
32
  function compareText(left,right){
package/src/workspace.mjs CHANGED
@@ -1,3 +1,4 @@
1
+ import Is from 'strong-type';
1
2
  import {readFile,readdir,realpath,stat} from 'node:fs/promises';
2
3
  import path from 'node:path';
3
4
  import {
@@ -9,6 +10,8 @@ import {loadAppDescriptor} from './app-descriptor.mjs';
9
10
  import {SDK_NAME as EXPECTED_SDK_NAME} from './constants.mjs';
10
11
  import {inspectImportMapHtml} from './import-map.mjs';
11
12
 
13
+ const is = new Is(false);
14
+
12
15
  const APP_ID_PATTERN=/^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/;
13
16
  const NPM_PACKAGE_NAME_PATTERN=/^(?:@[a-z0-9][a-z0-9._-]*\/)?[a-z0-9][a-z0-9._-]*$/u;
14
17
  const ROOT_CONFIG_NAME='arcane-packager.json';
@@ -22,7 +25,7 @@ function fail(message,code='ARCANE_WORKSPACE_INVALID'){
22
25
  }
23
26
 
24
27
  function isObject(value){
25
- return value!==null&&typeof value==='object'&&!Array.isArray(value);
28
+ return value!==null&&is.object(value)&&!is.array(value);
26
29
  }
27
30
 
28
31
  function ordinal(left,right){
@@ -37,7 +40,7 @@ function throwIfAborted(signal){
37
40
  }
38
41
 
39
42
  async function emit(onEvent,event){
40
- if(typeof onEvent==='function')await onEvent(event);
43
+ if(is.function(onEvent))await onEvent(event);
41
44
  }
42
45
 
43
46
  async function readJson(filePath,label){
@@ -71,7 +74,7 @@ function sameDirectoryPath(left,right){
71
74
  }
72
75
 
73
76
  function sdkPackageSourceForDependency(dependencyName){
74
- if(typeof dependencyName!=='string'||dependencyName.length>214
77
+ if(!is.string(dependencyName)||dependencyName.length>214
75
78
  ||!NPM_PACKAGE_NAME_PATTERN.test(dependencyName)){
76
79
  fail(`Invalid installed SDK dependency name: ${String(dependencyName)}.`);
77
80
  }
@@ -85,7 +88,7 @@ function sdkPackageSourceForDependency(dependencyName){
85
88
  }
86
89
 
87
90
  function dependencyNameForSdkPackageSource(source){
88
- if(typeof source!=='string'||!source.startsWith('node_modules/'))return null;
91
+ if(!is.string(source)||!source.startsWith('node_modules/'))return null;
89
92
  const dependencyName=source.slice('node_modules/'.length);
90
93
  try{
91
94
  return sdkPackageSourceForDependency(dependencyName)===source?dependencyName:null;
@@ -95,7 +98,7 @@ function dependencyNameForSdkPackageSource(source){
95
98
  }
96
99
 
97
100
  function routeIncludeMatches(actual,wanted,optionalSecurity){
98
- if(!Array.isArray(actual)||!Array.isArray(wanted))return false;
101
+ if(!is.array(actual)||!is.array(wanted))return false;
99
102
  if(actual.length===wanted.length
100
103
  &&actual.every(function sameIncludedPath(value,index){return value===wanted[index];})){
101
104
  return true;
@@ -118,11 +121,11 @@ export function resolveSdkPackageDeclaration(rootPackage,{
118
121
  if(!isObject(group))fail(`package.json ${groupName} must be a JSON object.`);
119
122
  for(const [dependencyName,specifier] of Object.entries(group)){
120
123
  const canonicalName=dependencyName===EXPECTED_SDK_NAME;
121
- const aliasTarget=typeof specifier==='string'
124
+ const aliasTarget=is.string(specifier)
122
125
  &&(specifier===`npm:${EXPECTED_SDK_NAME}`
123
126
  ||specifier.startsWith(`npm:${EXPECTED_SDK_NAME}@`));
124
127
  if(!canonicalName&&!aliasTarget)continue;
125
- if(typeof specifier!=='string'||!specifier.trim()){
128
+ if(!is.string(specifier)||!specifier.trim()){
126
129
  fail(`package.json ${groupName}.${dependencyName} must declare an SDK package version or source.`);
127
130
  }
128
131
  candidates.push(completeValue({
@@ -155,7 +158,7 @@ export function resolveSdkPackageDeclaration(rootPackage,{
155
158
  function classifyRootConfig(config){
156
159
  const validated=validatePackagerRootConfig(config,ROOT_CONFIG_NAME);
157
160
  const routes=validated.sharedPayloads['browser-runtime'];
158
- if(!Array.isArray(routes))fail(`${ROOT_CONFIG_NAME} must define browser-runtime routes.`);
161
+ if(!is.array(routes))fail(`${ROOT_CONFIG_NAME} must define browser-runtime routes.`);
159
162
  const externalPackageSource=routes.length===2
160
163
  &&dependencyNameForSdkPackageSource(routes[1]?.source)!==null
161
164
  ?routes[1].source
@@ -194,7 +197,7 @@ function classifyRootConfig(config){
194
197
  optionalArcaneSecurity&&index===0
195
198
  &&wanted.source==='arcane'&&wanted.destination==='arcane'
196
199
  )
197
- &&Array.isArray(route.exclude)&&route.exclude.length===0;
200
+ &&is.array(route.exclude)&&route.exclude.length===0;
198
201
  });
199
202
  };
200
203
  let workspaceMode;
@@ -272,7 +275,7 @@ export async function inspectWorkspaceProfile(workspaceRoot=process.cwd()){
272
275
 
273
276
  export async function selectApp(workspaceRoot=process.cwd(),appId){
274
277
  const apps=await discoverApps(workspaceRoot);
275
- if(appId!==undefined&&(typeof appId!=='string'||!APP_ID_PATTERN.test(appId))){
278
+ if(appId!==undefined&&(!is.string(appId)||!APP_ID_PATTERN.test(appId))){
276
279
  fail(`Invalid app id: ${String(appId)}.`,'ARCANE_USAGE');
277
280
  }
278
281
  if(appId){
@@ -292,7 +295,7 @@ export async function resolveWorkspace({workspaceRoot=process.cwd(),appId}={}){
292
295
  const canonicalRoot=profile.workspaceRoot;
293
296
  const config=profile.config;
294
297
  const apps=await discoverAppsInRoot(canonicalRoot,config);
295
- if(appId!==undefined&&(typeof appId!=='string'||!APP_ID_PATTERN.test(appId))){
298
+ if(appId!==undefined&&(!is.string(appId)||!APP_ID_PATTERN.test(appId))){
296
299
  fail(`Invalid app id: ${String(appId)}.`,'ARCANE_USAGE');
297
300
  }
298
301
  let app;
@@ -330,7 +333,7 @@ export async function resolveInstalledSdkInstallation(workspaceRoot,declaration)
330
333
  path.join(canonicalPackageRoot,'package.json'),
331
334
  'installed SDK package manifest'
332
335
  );
333
- if(installedPackage.name!==EXPECTED_SDK_NAME||typeof installedPackage.version!=='string'){
336
+ if(installedPackage.name!==EXPECTED_SDK_NAME||!is.string(installedPackage.version)){
334
337
  fail(`Installed SDK package must identify as ${EXPECTED_SDK_NAME}.`);
335
338
  }
336
339
  const runtimeRoot=path.join(canonicalPackageRoot,'runtime');
@@ -427,8 +430,8 @@ export async function validateDiscoveredApplication({
427
430
  onEvent
428
431
  }={}){
429
432
  throwIfAborted(signal);
430
- if(!app||typeof app.appId!=='string'||!APP_ID_PATTERN.test(app.appId)
431
- ||typeof app.appRoot!=='string'
433
+ if(!app||!is.string(app.appId)||!APP_ID_PATTERN.test(app.appId)
434
+ ||!is.string(app.appRoot)
432
435
  ||!app.manifest||!app.descriptor){
433
436
  fail('A discovered Arcane application is required for focused validation.');
434
437
  }
@@ -1,32 +0,0 @@
1
- export const AI_RESPONSE_LENGTH_DEFAULT='medium';
2
-
3
- export const AI_RESPONSE_LENGTH_OPTIONS=[
4
- {value:'low',label:'Complete'},
5
- {value:'medium',label:'Complete'},
6
- {value:'high',label:'Complete'}
7
- ];
8
-
9
- const RESPONSE_LENGTH_BY_VALUE=new Map(
10
- AI_RESPONSE_LENGTH_OPTIONS.map(option=>[option.value,option])
11
- );
12
-
13
- export function normalizeAIResponseLength(value){
14
- const normalized=typeof value==='string'?value.trim().toLowerCase():'';
15
- return RESPONSE_LENGTH_BY_VALUE.has(normalized)
16
- ?normalized
17
- :AI_RESPONSE_LENGTH_DEFAULT;
18
- }
19
-
20
- export function aiResponseLengthInstruction(value){
21
- normalizeAIResponseLength(value);
22
- return '';
23
- }
24
-
25
- export function applyAIResponseLength(systemPrompt,value){
26
- if(typeof systemPrompt!=='string'){
27
- throw new TypeError('systemPrompt must be a string');
28
- }
29
-
30
- normalizeAIResponseLength(value);
31
- return systemPrompt;
32
- }