arcane-os 0.5.17 → 0.5.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (186) hide show
  1. package/CHANGELOG.md +34 -1
  2. package/README.md +9 -9
  3. package/bin/arcane-test.mjs +16 -13
  4. package/browser-runtime/ai/browser-speech-artifacts.mjs +41 -38
  5. package/browser-runtime/ai/browser-speech-providers.mjs +46 -43
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +158 -135
  7. package/browser-runtime/ai/browser-wasm.mjs +6 -3
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +104 -23
  9. package/browser-runtime/ai/model-controller.mjs +94 -91
  10. package/browser-runtime/ai/speech-worker-client.mjs +12 -9
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +56 -53
  12. package/browser-runtime/dependencies/strong-type/index.js +207 -82
  13. package/browser-runtime/dependencies/strong-type/package.json +15 -6
  14. package/browser-runtime/dom-event-instrumentation.mjs +24 -20
  15. package/browser-runtime/event-manager.mjs +111 -108
  16. package/browser-runtime/speech-text.mjs +5 -1
  17. package/docs/architecture.md +1 -1
  18. package/docs/publishing.md +10 -4
  19. package/docs/reference/ai/browser-speech.md +198 -5
  20. package/docs/reference/ai/browser-wasm.md +29 -6
  21. package/docs/reference/availability-and-normalization.md +40 -0
  22. package/docs/reference/inventory/package-api.json +1 -1
  23. package/docs/reference/inventory/runtime-components.json +1 -1
  24. package/docs/reference/inventory/runtime-modules.json +20 -24
  25. package/docs/reference/protocols.md +1 -1
  26. package/docs/reference/runtime-modules.md +125 -35
  27. package/docs/reference/sdk-api.md +23 -7
  28. package/examples/wasm-ai-demo/index.html +1 -0
  29. package/node_modules/event-pubsub/node_modules/strong-type/README.md +408 -0
  30. package/node_modules/event-pubsub/node_modules/strong-type/assets/strong-type-header.png +0 -0
  31. package/node_modules/event-pubsub/node_modules/strong-type/index.js +1151 -0
  32. package/node_modules/event-pubsub/node_modules/strong-type/licence +21 -0
  33. package/node_modules/event-pubsub/node_modules/strong-type/node.js +125 -0
  34. package/node_modules/event-pubsub/node_modules/strong-type/package.json +61 -0
  35. package/node_modules/strong-type/README.md +42 -24
  36. package/node_modules/strong-type/benchmark/core.js +324 -0
  37. package/node_modules/strong-type/index.js +207 -82
  38. package/node_modules/strong-type/package.json +14 -6
  39. package/package.json +3 -3
  40. package/runtime/arcane/components/app-bar.html +5 -2
  41. package/runtime/arcane/components/assistant-panel.html +5 -2
  42. package/runtime/arcane/components/chart.html +15 -12
  43. package/runtime/arcane/components/chat.html +170 -148
  44. package/runtime/arcane/components/dashboard-config.html +6 -3
  45. package/runtime/arcane/components/data-view.html +4 -1
  46. package/runtime/arcane/components/directory-picker.html +5 -2
  47. package/runtime/arcane/components/document-inspector.html +6 -3
  48. package/runtime/arcane/components/file-drop.html +5 -2
  49. package/runtime/arcane/components/file-inspector.html +9 -6
  50. package/runtime/arcane/components/file-manager.html +16 -13
  51. package/runtime/arcane/components/local-ai-status.html +5 -2
  52. package/runtime/arcane/components/markdown-document.html +14 -11
  53. package/runtime/arcane/components/markdown-editor.html +4 -1
  54. package/runtime/arcane/components/modal.html +4 -1
  55. package/runtime/arcane/components/output-panel.html +6 -3
  56. package/runtime/arcane/components/preferences-form.html +4 -1
  57. package/runtime/arcane/components/record-timeline.html +8 -5
  58. package/runtime/arcane/components/relationship-board.html +9 -6
  59. package/runtime/arcane/components/source-code-viewer.html +12 -9
  60. package/runtime/arcane/components/source-explanation.html +8 -5
  61. package/runtime/arcane/components/speech.html +24 -21
  62. package/runtime/arcane/components/summary-strip.html +4 -1
  63. package/runtime/arcane/components/task-progress.html +6 -3
  64. package/runtime/arcane/components/terminal-workspace.html +4 -1
  65. package/runtime/arcane/components/voice-transcription.html +11 -8
  66. package/runtime/arcane/components/web-navigator.html +5 -2
  67. package/runtime/arcane/entities/ApiModelRecord.js +5 -2
  68. package/runtime/arcane/entities/Calculation.js +5 -2
  69. package/runtime/arcane/entities/Chat.js +33 -33
  70. package/runtime/arcane/entities/CommunicationMessage.js +4 -1
  71. package/runtime/arcane/entities/CommunicationThread.js +4 -1
  72. package/runtime/arcane/entities/File.js +1 -1
  73. package/runtime/arcane/entities/Image.js +8 -5
  74. package/runtime/arcane/entities/IntentEnvelope.js +20 -17
  75. package/runtime/arcane/entities/Preference.js +9 -6
  76. package/runtime/arcane/entities/Theme.js +5 -2
  77. package/runtime/arcane/entities/User.js +11 -11
  78. package/runtime/arcane/entities/Weather.js +5 -2
  79. package/runtime/arcane/modules/AI.js +491 -165
  80. package/runtime/arcane/modules/AIPreferenceRuntime.js +5 -2
  81. package/runtime/arcane/modules/AIPreferenceTuple.js +9 -6
  82. package/runtime/arcane/modules/AIProviderRuntime.js +88 -85
  83. package/runtime/arcane/modules/AIResponseURLPolicy.js +148 -62
  84. package/runtime/arcane/modules/AIRuntimeState.js +29 -26
  85. package/runtime/arcane/modules/AnsiText.js +4 -1
  86. package/runtime/arcane/modules/ApiModelDatabase.js +22 -19
  87. package/runtime/arcane/modules/AppDataScope.js +8 -5
  88. package/runtime/arcane/modules/ArcaneCommunicationBridge.js +4 -1
  89. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +8 -5
  90. package/runtime/arcane/modules/ArcaneNetworkPolicy.js +18 -15
  91. package/runtime/arcane/modules/AsyncBoundary.js +13 -10
  92. package/runtime/arcane/modules/BrowserTestSuite.js +19 -16
  93. package/runtime/arcane/modules/CalculatorEngine.js +5 -2
  94. package/runtime/arcane/modules/ChatRecords.js +18 -15
  95. package/runtime/arcane/modules/CommunicationAppController.js +9 -6
  96. package/runtime/arcane/modules/CommunicationHub.js +9 -6
  97. package/runtime/arcane/modules/CommunicationPreferences.js +4 -1
  98. package/runtime/arcane/modules/CommunicationProviderRegistry.js +4 -1
  99. package/runtime/arcane/modules/ComponentContracts.js +56 -53
  100. package/runtime/arcane/modules/ConfiguredAIChatSession.js +30 -27
  101. package/runtime/arcane/modules/ConversationActionItems.js +15 -12
  102. package/runtime/arcane/modules/ConversationClosingReport.js +11 -8
  103. package/runtime/arcane/modules/ConversationTimebox.js +28 -25
  104. package/runtime/arcane/modules/CoreLocalModelCatalog.js +17 -14
  105. package/runtime/arcane/modules/DBLS.js +7 -4
  106. package/runtime/arcane/modules/DBOPFS.js +7 -7
  107. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +34 -31
  108. package/runtime/arcane/modules/DevelopmentWorkspace.js +4 -1
  109. package/runtime/arcane/modules/DirectoryPicker.js +8 -5
  110. package/runtime/arcane/modules/DocumentLexicalSearch.js +11 -8
  111. package/runtime/arcane/modules/DocumentNavigation.js +7 -4
  112. package/runtime/arcane/modules/Errors.js +28 -25
  113. package/runtime/arcane/modules/HTMLImport.js +7 -4
  114. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +15 -12
  115. package/runtime/arcane/modules/LocalAIReadiness.js +21 -18
  116. package/runtime/arcane/modules/LocalAIReadinessController.js +6 -3
  117. package/runtime/arcane/modules/MD.js +1 -1
  118. package/runtime/arcane/modules/Mail.js +46 -43
  119. package/runtime/arcane/modules/MailOutbox.mjs +48 -45
  120. package/runtime/arcane/modules/MailTransport.mjs +29 -26
  121. package/runtime/arcane/modules/MemoryRecords.js +7 -4
  122. package/runtime/arcane/modules/MessageAdvisory.js +6 -3
  123. package/runtime/arcane/modules/ModelDefinition.js +7 -4
  124. package/runtime/arcane/modules/Ollama.js +6 -3
  125. package/runtime/arcane/modules/OllamaModelIdentifier.js +4 -1
  126. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +18 -15
  127. package/runtime/arcane/modules/PersistentAIChatSession.js +33 -30
  128. package/runtime/arcane/modules/PreferenceStore.js +20 -17
  129. package/runtime/arcane/modules/PreparedSpeech.js +485 -0
  130. package/runtime/arcane/modules/Questionnaire.js +6 -3
  131. package/runtime/arcane/modules/RecordLinkIndex.js +4 -1
  132. package/runtime/arcane/modules/RecordPassageIndex.js +9 -6
  133. package/runtime/arcane/modules/RecordReviewStore.js +12 -9
  134. package/runtime/arcane/modules/RiskSignalAnalyzer.js +4 -1
  135. package/runtime/arcane/modules/ScopedOPFSCache.js +6 -3
  136. package/runtime/arcane/modules/ScreenCapture.js +20 -17
  137. package/runtime/arcane/modules/SpeechPlayback.js +29 -26
  138. package/runtime/arcane/modules/StaticDocumentCatalog.js +33 -30
  139. package/runtime/arcane/modules/SystemAppearance.js +5 -2
  140. package/runtime/arcane/modules/SystemToolRegistry.js +6 -3
  141. package/runtime/arcane/modules/TerminalClient.js +14 -11
  142. package/runtime/arcane/modules/TerminalCommandRegistry.js +4 -1
  143. package/runtime/arcane/modules/ThemeBootstrap.js +9 -6
  144. package/runtime/arcane/modules/ThemeManager.js +5 -2
  145. package/runtime/arcane/modules/TimeGuard.js +1 -1
  146. package/runtime/arcane/modules/ToolCallRouter.js +11 -8
  147. package/runtime/arcane/modules/WaitForComponent.js +19 -16
  148. package/runtime/arcane/modules/YouTubeMedia.js +4 -1
  149. package/runtime/strong-type/index.js +1276 -352
  150. package/runtime/strong-type/package.json +69 -45
  151. package/src/app-descriptor.mjs +17 -14
  152. package/src/application-tests.mjs +4 -1
  153. package/src/cli/main.mjs +8 -5
  154. package/src/constants.mjs +4 -1
  155. package/src/dev-server.mjs +8 -5
  156. package/src/doctor.mjs +5 -2
  157. package/src/dom-event-instrumentation.mjs +24 -20
  158. package/src/errors.mjs +7 -3
  159. package/src/event-manager.mjs +111 -108
  160. package/src/event-queue.mjs +8 -5
  161. package/src/events.mjs +12 -9
  162. package/src/import-map.mjs +35 -27
  163. package/src/installed-sdk-runtime.mjs +4 -1
  164. package/src/integrated-provider-loader.mjs +9 -6
  165. package/src/mail-credentials.mjs +16 -13
  166. package/src/mail-server.mjs +53 -50
  167. package/src/mail.mjs +18 -15
  168. package/src/native-plan.mjs +12 -9
  169. package/src/native-provider-loader.mjs +7 -4
  170. package/src/packager/core.mjs +18 -15
  171. package/src/process.mjs +6 -3
  172. package/src/release-bundle.mjs +14 -11
  173. package/src/runtime.mjs +4 -1
  174. package/src/scaffold.mjs +9 -6
  175. package/src/sdk-browser-runtime.mjs +4 -1
  176. package/src/source-server.mjs +17 -14
  177. package/src/targets/index.mjs +5 -2
  178. package/src/templates/workspace-template.mjs +13 -6
  179. package/src/testing-loader.mjs +7 -4
  180. package/src/testing.mjs +10 -7
  181. package/src/toolchain.mjs +13 -10
  182. package/src/update-check.mjs +9 -6
  183. package/src/workspace-operation-lock.mjs +14 -11
  184. package/src/workspace-runtime.mjs +4 -1
  185. package/src/workspace.mjs +17 -14
  186. package/runtime/arcane/modules/AIResponseLength.js +0 -32
package/CHANGELOG.md CHANGED
@@ -1,6 +1,39 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 0.5.19
4
+
5
+ - Report observed Wllama initialization stages and runtime activity through
6
+ direct provider loading and the shared AI lifecycle. Keep initialization
7
+ indeterminate when the runtime supplies no meaningful completion total.
8
+ - Show the current initialization stage, stage duration, and time since runtime
9
+ activity in shared chat instead of a completed file count during activation.
10
+ Preserve download progress, cancellation, and complete runtime logging.
11
+ - Remove an adjacent duplicate cancellation check while retaining cancellation
12
+ handling before initialization and after loading.
13
+
14
+ ## 0.5.18
15
+
16
+ - Use `strong-type` predicates throughout SDK-owned toolchain, runtime,
17
+ browser-provider, and component code while retaining existing defaults,
18
+ domain rules, public errors, and nominal constructor behavior. Align the
19
+ direct dependency and both shipped projections at `strong-type` 2.0.1,
20
+ replacing the portable runtime's 1.1.0 snapshot and avoiding Error allocation
21
+ for ordinary false predicates. Keep managed browser imports and Node reference
22
+ loading on the same declared dependency.
23
+ - Remove `AIResponseLength` exports; applications own response verbosity.
24
+ Parse URL-audit HTML with the native HTML parser.
25
+
26
+ - Add `AI.prepareTTS()` for detached punctuation-segmented synthesis, optional
27
+ DBOPFS audio storage, complete semantic-input reuse, shared pending work and
28
+ independent preparation cancellation. Persist MIME metadata with each audio
29
+ segment and retain successful segments after interruption or partial failure.
30
+ - Add `AI.playPreparedTTS()` to replay prepared or pending audio through the
31
+ existing audio-clock scheduler. Playback has its own completion, pause,
32
+ resume, stop, state and error surface; stopping playback keeps detached
33
+ preparation alive. Cached audio plays without loading a speech model.
34
+ - Keep Markdown cleanup at its shared owner, preserve the existing TTS APIs,
35
+ and share provider capacity across preparation requests. Applications own
36
+ content grouping, storage keys, preparation order and retention policy.
4
37
 
5
38
  ## 0.5.17
6
39
 
package/README.md CHANGED
@@ -19,7 +19,7 @@ version-locked SDK runtime, while an integrated Arcane checkout uses its live
19
19
  `arcane/` runtime. Both profiles preserve the same app URLs, theme, packaging,
20
20
  event, cancellation, and browser run contracts.
21
21
 
22
- This checkout defines the `0.5.17` SDK contract. Applications pin one exact npm
22
+ This checkout defines the `0.5.19` SDK contract. Applications pin one exact npm
23
23
  version and lockfile; registry state is deliberately not baked into application
24
24
  artifacts.
25
25
 
@@ -35,7 +35,7 @@ Create one browser application, install its pinned SDK, and start its source
35
35
  server:
36
36
 
37
37
  ```bash
38
- npx arcane-os@0.5.17 new hello-speech --path ./hello-speech --target browser
38
+ npx arcane-os@0.5.19 new hello-speech --path ./hello-speech --target browser
39
39
  cd hello-speech
40
40
  npm install
41
41
  npm run dev
@@ -332,7 +332,7 @@ Streaming calls also recognize a run split across chunks. Single marks and
332
332
  ordinary repeated punctuation remain literal. No application option is needed;
333
333
  an existing `textFormat` extra is ignored and cannot disable cleanup. Original
334
334
  messages and caller payloads remain unchanged for display, storage, and model
335
- input. The dependency-free `arcane-os/speech-text` entrypoint exports the same
335
+ input. The `arcane-os/speech-text` entrypoint exports the same
336
336
  `MarkdownSpeech` streaming class and `stripSpeechFormatting()` one-pass helper
337
337
  for code that needs the speech-only transformation directly.
338
338
 
@@ -357,7 +357,7 @@ uses the same controller for automatic memory extraction.
357
357
  Create a new repository-shaped Arcane application with the exact stable SDK:
358
358
 
359
359
  ```bash
360
- npx arcane-os@0.5.17 new my-app --path ./my-app --target portable --git
360
+ npx arcane-os@0.5.19 new my-app --path ./my-app --target portable --git
361
361
  cd my-app
362
362
  npm install
363
363
  npm run dev
@@ -367,7 +367,7 @@ To enroll an existing repository, install the exact SDK and initialize only
367
367
  missing Arcane files:
368
368
 
369
369
  ```bash
370
- npm install --save-dev --save-exact arcane-os@0.5.17
370
+ npm install --save-dev --save-exact arcane-os@0.5.19
371
371
  npm exec -- arcane init my-app --target portable
372
372
  ```
373
373
 
@@ -383,7 +383,7 @@ npm exec -- arcane-os targets
383
383
  No global SDK install or standalone Arcane CLI is required. The application
384
384
  repository's exact npm dependency and lockfile own the CLI and toolchain version.
385
385
 
386
- Use `npx arcane-os@0.5.17` for the initial bootstrap because it names this npm
386
+ Use `npx arcane-os@0.5.19` for the initial bootstrap because it names this npm
387
387
  package explicitly; bare `npx arcane` outside an installed project could resolve
388
388
  a different package. Both installed commands invoke the same headless toolchain.
389
389
  Project-local npm scripts use the SDK pinned by that app's `package-lock.json`,
@@ -403,7 +403,7 @@ node ./bin/arcane.mjs new local-app --path ../local-app --target portable --git
403
403
 
404
404
  # From the generated app repository
405
405
  cd ../local-app
406
- npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.5.17.tgz
406
+ npm install --save-dev --save-exact ../arcane-os-sdk/arcane-os-0.5.19.tgz
407
407
  npm ci
408
408
  ```
409
409
 
@@ -412,7 +412,7 @@ same location. The lockfile retains the selected package dependency while
412
412
  Arcane uses the installed package name and version. Local directory `file:` dependencies are not
413
413
  accepted because npm may install them as links; use a packed `.tgz`. A GitHub
414
414
  runner also needs that tarball at the locked path. After publication, replace
415
- the local declaration with the exact `arcane-os@0.5.17` registry package and
415
+ the local declaration with the exact `arcane-os@0.5.19` registry package and
416
416
  commit the regenerated lock.
417
417
 
418
418
  Generated repositories use `npm ci --ignore-scripts` in CI. Run dependency
@@ -551,7 +551,7 @@ package installation, or assertions.
551
551
 
552
552
  ## Current target support
553
553
 
554
- Version `0.5.17` exposes one browser target and five explicitly paired
554
+ Version `0.5.19` exposes one browser target and five explicitly paired
555
555
  native development targets: a non-runnable portable directory, a
556
556
  Windows x64 unsigned-local-test EXE bundle, Linux x64 and Linux ARM64
557
557
  unsigned-local-test DEBs, and an Android development-signed APK. The
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env node
2
+ import Is from 'strong-type';
2
3
  import {spawn} from 'node:child_process';
3
4
  import {lstat,readdir,realpath} from 'node:fs/promises';
4
5
  import {register} from 'node:module';
@@ -6,6 +7,8 @@ import path from 'node:path';
6
7
  import {fileURLToPath,pathToFileURL} from 'node:url';
7
8
  import test,{DEFAULT_TEST_TIMEOUT_MS,runRegisteredTests} from '../src/testing.mjs';
8
9
 
10
+ const is = new Is(false);
11
+
9
12
  const SINGLE_FILE_ARGUMENT='--arcane-single-test-file';
10
13
  const RUNNER_PATH=fileURLToPath(import.meta.url);
11
14
  const TEST_FILE_PATTERN=/\.test\.(?:mjs|cjs|js)$/u;
@@ -27,7 +30,7 @@ const COORDINATOR_MANAGED_IMPORT_MAP=!ISOLATED_MODE
27
30
  &&MANAGED_IMPORT_MAP_SOURCE!==''
28
31
  ?MANAGED_IMPORT_MAP_SOURCE
29
32
  :undefined;
30
- const ISOLATED_SEND=ISOLATED_MODE&&typeof process.send==='function'
33
+ const ISOLATED_SEND=ISOLATED_MODE&&is.function(process.send)
31
34
  ?process.send.bind(process)
32
35
  :null;
33
36
  delete process.env[MANAGED_IMPORT_MAP_ENV];
@@ -55,12 +58,12 @@ async function readManagedImportMapContext(source,signal,testFile){
55
58
  let context;
56
59
  try{context=JSON.parse(source);}
57
60
  catch(error){importMapFailure(`Managed import-map context is not valid JSON: ${error.message}`);}
58
- if(context===null||typeof context!=='object'||Array.isArray(context)
61
+ if(context===null||!is.object(context)||is.array(context)
59
62
  ||context.protocol!==MANAGED_IMPORT_MAP_PROTOCOL
60
63
  ||!APPLICATION_MAP_BOUNDARIES.has(context.boundary)
61
- ||typeof context.baseURL!=='string'
62
- ||context.imports===null||typeof context.imports!=='object'
63
- ||Array.isArray(context.imports)){
64
+ ||!is.string(context.baseURL)
65
+ ||context.imports===null||!is.object(context.imports)
66
+ ||is.array(context.imports)){
64
67
  importMapFailure('Managed import-map context is malformed.');
65
68
  }
66
69
  let suppliedBase;
@@ -281,8 +284,8 @@ function parseTaskkillTargets(output,leaderPid){
281
284
  const target=Number(matches[0][1]);
282
285
  const parent=matches.length===2?Number(matches[1][1]):null;
283
286
  if(
284
- !Number.isSafeInteger(target)||target<=0
285
- ||(parent!==null&&(!Number.isSafeInteger(parent)||parent<=0))
287
+ !is.safeInteger(target)||target<=0
288
+ ||(parent!==null&&(!is.safeInteger(parent)||parent<=0))
286
289
  ||/\d/u.test(line.replaceAll(/\bPID\s+\d+\b/giu,'PID'))
287
290
  )return null;
288
291
  entries.push({target,parent});
@@ -592,21 +595,21 @@ function runIsolatedFile(file,signal){
592
595
  const onMessage=message=>{
593
596
  if(
594
597
  message?.protocol!==IPC_PROTOCOL
595
- ||typeof message.type!=='string'
598
+ ||!is.string(message.type)
596
599
  )return;
597
600
  if(message.type==='phase'){
598
601
  if(
599
602
  completion!==null
600
603
  ||watchdogFailure!==null
601
- ||typeof message.id!=='string'
604
+ ||!is.string(message.id)
602
605
  ||message.id===''
603
606
  )return;
604
607
  if(message.status==='started'){
605
608
  if(
606
- typeof message.kind!=='string'
607
- ||typeof message.name!=='string'
609
+ !is.string(message.kind)
610
+ ||!is.string(message.name)
608
611
  ||(message.timeoutMs!==null&&(
609
- !Number.isSafeInteger(message.timeoutMs)
612
+ !is.safeInteger(message.timeoutMs)
610
613
  ||message.timeoutMs<1
611
614
  ||message.timeoutMs>3_600_000
612
615
  ))
@@ -796,7 +799,7 @@ async function main(){
796
799
  ||message?.type!=='abort'
797
800
  )return;
798
801
  const error=new Error(
799
- typeof message.reason==='string'?message.reason:'The coordinator cancelled the test file.'
802
+ is.string(message.reason)?message.reason:'The coordinator cancelled the test file.'
800
803
  );
801
804
  error.code='ARCANE_TEST_CANCELLED';
802
805
  controller.abort(error);
@@ -1,7 +1,10 @@
1
+ import Is from "../dependencies/strong-type/index.js";
1
2
  import {
2
3
  normalizeModelSecurity,
3
4
  } from "./model-controller.mjs";
4
5
 
6
+ const is = new Is(false);
7
+
5
8
  const completeValue = (value) => value;
6
9
 
7
10
  export const BROWSER_SPEECH_ARTIFACT_PROTOCOL =
@@ -20,10 +23,10 @@ const ARTIFACT_GRAPHS = new WeakSet();
20
23
  const ARTIFACT_GRAPH_METADATA = new WeakMap();
21
24
  const ARTIFACT_ERRORS = new WeakSet();
22
25
  const STORES = new WeakSet();
23
- const PLATFORM_CREATE_OBJECT_URL = typeof globalThis.URL?.createObjectURL === "function"
26
+ const PLATFORM_CREATE_OBJECT_URL = is.function(globalThis.URL?.createObjectURL)
24
27
  ? globalThis.URL.createObjectURL.bind(globalThis.URL)
25
28
  : null;
26
- const PLATFORM_REVOKE_OBJECT_URL = typeof globalThis.URL?.revokeObjectURL === "function"
29
+ const PLATFORM_REVOKE_OBJECT_URL = is.function(globalThis.URL?.revokeObjectURL)
27
30
  ? globalThis.URL.revokeObjectURL.bind(globalThis.URL)
28
31
  : null;
29
32
  const ARTIFACT_ERROR_REASONS = completeValue({
@@ -70,7 +73,7 @@ function speechError(code, message, cause, reason = ARTIFACT_ERROR_REASONS[code]
70
73
  : new Error(message, { cause });
71
74
  error.name = "ArcaneBrowserSpeechError";
72
75
  error.code = code;
73
- if (typeof reason === "string" && reason) error.reason = reason;
76
+ if (is.string(reason) && reason) error.reason = reason;
74
77
  ARTIFACT_ERRORS.add(error);
75
78
  return error;
76
79
  }
@@ -104,7 +107,7 @@ function throwIfAborted(signal) {
104
107
  }
105
108
 
106
109
  function requiredText(value, label) {
107
- if (typeof value !== "string" || !value.trim()) {
110
+ if (!is.string(value) || !value.trim()) {
108
111
  throw new TypeError(`${label} must be a nonempty string.`);
109
112
  }
110
113
  return value.trim();
@@ -115,7 +118,7 @@ function identifier(value, label) {
115
118
  }
116
119
 
117
120
  function artifactGraphText(value, label, reason = "artifact-graph-field-text-required") {
118
- if (typeof value !== "string" || !value.trim()) {
121
+ if (!is.string(value) || !value.trim()) {
119
122
  throw artifactGraphTypeError(reason, `${label} must be a nonempty string.`);
120
123
  }
121
124
  return value.trim();
@@ -176,7 +179,7 @@ function graphPositiveInteger(
176
179
  label,
177
180
  reason = "artifact-graph-positive-safe-integer-required",
178
181
  ) {
179
- if (!Number.isSafeInteger(value) || value < 1) {
182
+ if (!is.safeInteger(value) || value < 1) {
180
183
  throw artifactGraphTypeError(
181
184
  reason,
182
185
  `${label} must be a positive safe integer.`,
@@ -272,7 +275,7 @@ function lexicalCompare(left, right) {
272
275
  }
273
276
 
274
277
  function normalizeFile(value, kind, index) {
275
- if (!value || typeof value !== "object" || Array.isArray(value)) {
278
+ if (!value || !is.object(value) || is.array(value)) {
276
279
  throw new TypeError(`${kind} file ${String(index)} must be an object.`);
277
280
  }
278
281
  const path = requiredText(value.path ?? value.name, `${kind} file path`);
@@ -289,7 +292,7 @@ function normalizeFile(value, kind, index) {
289
292
  index,
290
293
  path,
291
294
  url,
292
- mediaType: typeof value.mediaType === "string" && value.mediaType.trim()
295
+ mediaType: is.string(value.mediaType) && value.mediaType.trim()
293
296
  ? value.mediaType.trim()
294
297
  : kind === "runtime" && /\.(?:m?js)$/iu.test(path)
295
298
  ? "text/javascript"
@@ -300,7 +303,7 @@ function normalizeFile(value, kind, index) {
300
303
  function uniqueFiles(files, label, kind, {
301
304
  allowEmpty = false,
302
305
  } = {}) {
303
- if (!Array.isArray(files) || (!allowEmpty && files.length < 1)) {
306
+ if (!is.array(files) || (!allowEmpty && files.length < 1)) {
304
307
  throw new TypeError(
305
308
  allowEmpty
306
309
  ? `${label} files must be an array.`
@@ -330,7 +333,7 @@ function publicFile(file) {
330
333
  }
331
334
 
332
335
  function normalizeArtifactGraphFile(value, index) {
333
- if (!value || typeof value !== "object" || Array.isArray(value)) {
336
+ if (!value || !is.object(value) || is.array(value)) {
334
337
  throw artifactGraphTypeError(
335
338
  "artifact-graph-file-descriptor-not-object",
336
339
  `Artifact graph file ${String(index)} must be an object.`,
@@ -398,7 +401,7 @@ function normalizeArtifactGraphFile(value, index) {
398
401
  );
399
402
  }
400
403
  const requestUrls = value.runtimeRequestUrls ?? [];
401
- if (!Array.isArray(requestUrls)) {
404
+ if (!is.array(requestUrls)) {
402
405
  throw artifactGraphTypeError(
403
406
  "artifact-graph-runtime-request-routes-not-array",
404
407
  `Artifact graph file ${path} runtimeRequestUrls must be an array.`,
@@ -461,14 +464,14 @@ function normalizeGraphTargetPath(value, label, filesByPath, javascript = false)
461
464
  }
462
465
 
463
466
  function normalizeArtifactGraphVoices(value, defaultVoice, filesByPath) {
464
- if (!Array.isArray(value) || value.length < 1) {
467
+ if (!is.array(value) || value.length < 1) {
465
468
  throw artifactGraphTypeError(
466
469
  "artifact-graph-voice-inventory-missing",
467
470
  "A TTS artifact graph requires a nonempty voices array.",
468
471
  );
469
472
  }
470
473
  const voices = value.map((voice, index) => {
471
- if (!voice || typeof voice !== "object" || Array.isArray(voice)) {
474
+ if (!voice || !is.object(voice) || is.array(voice)) {
472
475
  throw artifactGraphTypeError(
473
476
  "artifact-graph-voice-descriptor-not-object",
474
477
  `Artifact graph voice ${String(index)} must be an object.`,
@@ -567,19 +570,19 @@ export function createBrowserSpeechArtifactGraph({
567
570
  "artifact-graph-provider-id-missing",
568
571
  "artifact-graph-provider-id-length-exceeded",
569
572
  );
570
- if (!model || typeof model !== "object" || Array.isArray(model)) {
573
+ if (!model || !is.object(model) || is.array(model)) {
571
574
  throw artifactGraphTypeError(
572
575
  "artifact-graph-model-descriptor-missing",
573
576
  "Browser speech artifact graph model descriptor is required.",
574
577
  );
575
578
  }
576
- if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
579
+ if (!runtime || !is.object(runtime) || is.array(runtime)) {
577
580
  throw artifactGraphTypeError(
578
581
  "artifact-graph-runtime-descriptor-missing",
579
582
  "Browser speech artifact graph runtime descriptor is required.",
580
583
  );
581
584
  }
582
- if (!Array.isArray(files) || files.length < 1) {
585
+ if (!is.array(files) || files.length < 1) {
583
586
  throw artifactGraphTypeError(
584
587
  "artifact-graph-file-inventory-missing",
585
588
  "Browser speech artifact graph requires a nonempty files array.",
@@ -677,7 +680,7 @@ export function createBrowserSpeechArtifactGraph({
677
680
  );
678
681
  }
679
682
  const onnxWasm = runtime.onnxWasm;
680
- if (!onnxWasm || typeof onnxWasm !== "object" || Array.isArray(onnxWasm)) {
683
+ if (!onnxWasm || !is.object(onnxWasm) || is.array(onnxWasm)) {
681
684
  throw artifactGraphTypeError(
682
685
  "artifact-graph-onnx-wasm-descriptor-missing",
683
686
  "Browser speech artifact graph runtime onnxWasm descriptor is required.",
@@ -880,10 +883,10 @@ export function createBrowserSpeechAuthority({
880
883
  if (role !== "stt" && role !== "tts") {
881
884
  throw new TypeError('Browser speech role must be "stt" or "tts".');
882
885
  }
883
- if (!model || typeof model !== "object" || Array.isArray(model)) {
886
+ if (!model || !is.object(model) || is.array(model)) {
884
887
  throw new TypeError("Browser speech model descriptor is required.");
885
888
  }
886
- if (!runtime || typeof runtime !== "object" || Array.isArray(runtime)) {
889
+ if (!runtime || !is.object(runtime) || is.array(runtime)) {
887
890
  throw new TypeError("Browser speech runtime descriptor is required.");
888
891
  }
889
892
  const normalizedSecurity = normalizeModelSecurity(
@@ -1084,14 +1087,14 @@ function storageNames(authority, files) {
1084
1087
  }
1085
1088
 
1086
1089
  async function* byteChunks(body, signal) {
1087
- if (body instanceof Uint8Array || body instanceof ArrayBuffer || ArrayBuffer.isView(body)) {
1090
+ if (is.instanceCheck(body, Uint8Array) || is.instanceCheck(body, ArrayBuffer) || is.arrayBufferView(body)) {
1088
1091
  throwIfAborted(signal);
1089
- yield body instanceof Uint8Array
1092
+ yield is.instanceCheck(body, Uint8Array)
1090
1093
  ? body
1091
1094
  : new Uint8Array(body.buffer ?? body, body.byteOffset ?? 0, body.byteLength);
1092
1095
  return;
1093
1096
  }
1094
- if (body && typeof body.getReader === "function") {
1097
+ if (body && is.function(body.getReader)) {
1095
1098
  const reader = body.getReader();
1096
1099
  const abort = () => void reader.cancel(signal?.reason).catch(() => undefined);
1097
1100
  signal?.addEventListener?.("abort", abort, { once: true });
@@ -1100,7 +1103,7 @@ async function* byteChunks(body, signal) {
1100
1103
  throwIfAborted(signal);
1101
1104
  const { done, value } = await reader.read();
1102
1105
  if (done) return;
1103
- yield value instanceof Uint8Array ? value : new Uint8Array(value);
1106
+ yield is.instanceCheck(value, Uint8Array) ? value : new Uint8Array(value);
1104
1107
  }
1105
1108
  } finally {
1106
1109
  signal?.removeEventListener?.("abort", abort);
@@ -1347,7 +1350,7 @@ function ordinaryArtifactUrl(value, base) {
1347
1350
  }
1348
1351
 
1349
1352
  function isBareModuleSpecifier(value) {
1350
- return typeof value === "string"
1353
+ return is.string(value)
1351
1354
  && !value.startsWith("./")
1352
1355
  && !value.startsWith("../")
1353
1356
  && !value.startsWith("/")
@@ -1696,7 +1699,7 @@ async function createOrdinaryArtifactObjectUrls(
1696
1699
  ) {
1697
1700
  const create = objectUrlFactory?.create ?? PLATFORM_CREATE_OBJECT_URL;
1698
1701
  const revoke = objectUrlFactory?.revoke ?? PLATFORM_REVOKE_OBJECT_URL;
1699
- if (typeof create !== "function" || typeof revoke !== "function") {
1702
+ if (!is.function(create) || !is.function(revoke)) {
1700
1703
  throw artifactGraphError(
1701
1704
  "artifact-graph-object-url-platform-unavailable",
1702
1705
  "Artifact materialization requires native Blob URL creation and revocation.",
@@ -1706,11 +1709,11 @@ async function createOrdinaryArtifactObjectUrls(
1706
1709
  const materializedByPath = new Map();
1707
1710
  const created = [];
1708
1711
  async function materialize(descriptor, body) {
1709
- const blob = body instanceof Blob && body.type === descriptor.mediaType
1712
+ const blob = is.instanceCheck(body, Blob) && body.type === descriptor.mediaType
1710
1713
  ? body
1711
1714
  : new Blob([body], { type: descriptor.mediaType });
1712
1715
  const moduleUrl = create(blob);
1713
- if (typeof moduleUrl !== "string") {
1716
+ if (!is.string(moduleUrl)) {
1714
1717
  throw artifactGraphError(
1715
1718
  "artifact-graph-object-url-platform-unavailable",
1716
1719
  `Artifact file ${descriptor.path} did not produce an object URL.`,
@@ -1777,7 +1780,7 @@ async function createOrdinaryArtifactObjectUrls(
1777
1780
  function createObjectUrls(files, factory) {
1778
1781
  const create = factory?.create ?? ((blob) => URL.createObjectURL(blob));
1779
1782
  const revoke = factory?.revoke ?? ((url) => URL.revokeObjectURL(url));
1780
- if (typeof create !== "function" || typeof revoke !== "function") {
1783
+ if (!is.function(create) || !is.function(revoke)) {
1781
1784
  throw new TypeError("Browser speech objectUrlFactory requires create() and revoke().");
1782
1785
  }
1783
1786
  const created = [];
@@ -1830,14 +1833,14 @@ export function createDbopfsSpeechArtifactStore({
1830
1833
  fetchImpl = null,
1831
1834
  objectUrlFactory = null,
1832
1835
  } = {}) {
1833
- if (!dbopfs || typeof dbopfs.getTableHandle !== "function") {
1836
+ if (!dbopfs || !is.function(dbopfs.getTableHandle)) {
1834
1837
  throw new TypeError("createDbopfsSpeechArtifactStore requires an existing DBOPFS instance.");
1835
1838
  }
1836
- if (dbopfs.readyPromise !== undefined && typeof dbopfs.readyPromise?.then !== "function") {
1839
+ if (dbopfs.readyPromise !== undefined && !is.function(dbopfs.readyPromise?.then)) {
1837
1840
  throw new TypeError("The DBOPFS readyPromise must be thenable.");
1838
1841
  }
1839
1842
  const locks = dbopfs.lockManager ?? globalThis.navigator?.locks;
1840
- if (!locks || typeof locks.request !== "function") {
1843
+ if (!locks || !is.function(locks.request)) {
1841
1844
  throw new TypeError(
1842
1845
  "createDbopfsSpeechArtifactStore requires the browser Web Locks API.",
1843
1846
  );
@@ -1873,7 +1876,7 @@ export function createDbopfsSpeechArtifactStore({
1873
1876
  if (dbopfs.readyPromise) await dbopfs.readyPromise;
1874
1877
  tablePromise ||= Promise.resolve(dbopfs.getTableHandle(tableName));
1875
1878
  const result = await tablePromise;
1876
- if (!result || typeof result.getFileHandle !== "function" || typeof result.removeEntry !== "function") {
1879
+ if (!result || !is.function(result.getFileHandle) || !is.function(result.removeEntry)) {
1877
1880
  throw speechError("ARCANE_AI_STORAGE_UNAVAILABLE", "DBOPFS did not provide a speech artifact table.");
1878
1881
  }
1879
1882
  return result;
@@ -1934,7 +1937,7 @@ export function createDbopfsSpeechArtifactStore({
1934
1937
  const metadata = artifactMetadata(authority);
1935
1938
  const names = storageNames(authority, metadata.files);
1936
1939
  const priorSelection = await readJsonFile(names.selection);
1937
- const priorCount = Array.isArray(priorSelection?.files)
1940
+ const priorCount = is.array(priorSelection?.files)
1938
1941
  ? priorSelection.files.length
1939
1942
  : 0;
1940
1943
  const fileNames = Array.from(
@@ -2000,7 +2003,7 @@ export function createDbopfsSpeechArtifactStore({
2000
2003
  const metadata = artifactMetadata(authority);
2001
2004
  const names = storageNames(authority, metadata.files);
2002
2005
  const fetchFunction = fetchImpl ?? globalThis.fetch?.bind(globalThis);
2003
- if (typeof fetchFunction !== "function") {
2006
+ if (!is.function(fetchFunction)) {
2004
2007
  throw speechError("ARCANE_AI_ARTIFACT_SOURCE_UNAVAILABLE", "Browser fetch is unavailable.");
2005
2008
  }
2006
2009
  await removeUnlocked(authority);
@@ -2039,16 +2042,16 @@ export function createDbopfsSpeechArtifactStore({
2039
2042
  let responseOk;
2040
2043
  let responseStatus;
2041
2044
  try {
2042
- if (!response || typeof response !== "object") {
2045
+ if (!response || !is.object(response)) {
2043
2046
  throw new TypeError("The artifact fetch result is not an object.");
2044
2047
  }
2045
2048
  responseBody = response.body;
2046
2049
  responseOk = response.ok;
2047
2050
  responseStatus = response.status;
2048
2051
  if (
2049
- typeof responseOk !== "boolean"
2050
- || !Number.isInteger(responseStatus)
2051
- || (responseBody !== null && typeof responseBody?.getReader !== "function")
2052
+ !is.boolean(responseOk)
2053
+ || !is.integer(responseStatus)
2054
+ || (responseBody !== null && !is.function(responseBody?.getReader))
2052
2055
  ) {
2053
2056
  throw new TypeError("The artifact fetch result is not a readable Fetch Response.");
2054
2057
  }