arcane-os 0.3.0 → 0.3.2

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 (153) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +86 -117
  3. package/bin/arcane-test.mjs +170 -46
  4. package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
  5. package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
  6. package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
  7. package/browser-runtime/ai/browser-wasm.mjs +24 -35
  8. package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
  9. package/browser-runtime/ai/model-controller.mjs +584 -181
  10. package/browser-runtime/ai/speech-worker-client.mjs +8 -146
  11. package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
  12. package/browser-runtime/dom-event-instrumentation.mjs +55 -147
  13. package/browser-runtime/event-manager.mjs +239 -624
  14. package/package.json +5 -6
  15. package/runtime/arcane/components/app-bar.html +3 -15
  16. package/runtime/arcane/components/assistant-panel.html +10 -10
  17. package/runtime/arcane/components/calculator.html +1 -1
  18. package/runtime/arcane/components/chat.html +1359 -135
  19. package/runtime/arcane/components/conversation-view.html +2 -2
  20. package/runtime/arcane/components/document-inspector.html +11 -17
  21. package/runtime/arcane/components/file-manager.html +13 -56
  22. package/runtime/arcane/components/markdown-document.html +82 -281
  23. package/runtime/arcane/components/markdown-editor.html +7 -10
  24. package/runtime/arcane/components/media-embed.html +6 -6
  25. package/runtime/arcane/components/screen-capture.html +4 -4
  26. package/runtime/arcane/components/source-explanation.html +2 -2
  27. package/runtime/arcane/components/speech.html +112 -68
  28. package/runtime/arcane/components/terminal-workspace.html +4 -4
  29. package/runtime/arcane/components/theme-editor.html +1 -1
  30. package/runtime/arcane/components/unified-inbox.html +2 -2
  31. package/runtime/arcane/components/voice-transcription.html +31 -21
  32. package/runtime/arcane/entities/Calculation.js +2 -3
  33. package/runtime/arcane/entities/Chat.js +228 -43
  34. package/runtime/arcane/entities/Preference.js +3 -5
  35. package/runtime/arcane/entities/Weather.js +5 -5
  36. package/runtime/arcane/modules/AI.js +1050 -427
  37. package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
  38. package/runtime/arcane/modules/AIResponseLength.js +9 -19
  39. package/runtime/arcane/modules/AIRuntimeState.js +109 -72
  40. package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
  41. package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
  42. package/runtime/arcane/modules/CalculatorEngine.js +9 -9
  43. package/runtime/arcane/modules/CommunicationAppController.js +3 -7
  44. package/runtime/arcane/modules/ComponentContracts.js +30 -32
  45. package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
  46. package/runtime/arcane/modules/ConversationActionItems.js +26 -59
  47. package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
  48. package/runtime/arcane/modules/ConversationTimebox.js +27 -15
  49. package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
  50. package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
  51. package/runtime/arcane/modules/HTMLImport.js +54 -1
  52. package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
  53. package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
  54. package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
  55. package/runtime/arcane/modules/MD.js +1 -45
  56. package/runtime/arcane/modules/Mail.js +51 -103
  57. package/runtime/arcane/modules/MailOutbox.mjs +95 -193
  58. package/runtime/arcane/modules/MailTransport.mjs +36 -57
  59. package/runtime/arcane/modules/ModelDefinition.js +22 -106
  60. package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
  61. package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
  62. package/runtime/arcane/modules/PreferenceStore.js +102 -30
  63. package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
  64. package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
  65. package/runtime/arcane/modules/ScreenCapture.js +175 -128
  66. package/runtime/arcane/modules/SpeechPlayback.js +46 -149
  67. package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
  68. package/runtime/arcane/modules/ToolCallRouter.js +25 -12
  69. package/runtime/arcane/modules/YouTubeMedia.js +6 -5
  70. package/schemas/arcane-app-bundle.schema.json +13 -78
  71. package/schemas/arcane-app.schema.json +9 -25
  72. package/schemas/arcane-lock.schema.json +18 -151
  73. package/schemas/arcane-package.schema.json +2 -16
  74. package/schemas/native-build-plan.schema.json +119 -122
  75. package/src/app-descriptor.mjs +75 -132
  76. package/src/application-tests.mjs +200 -0
  77. package/src/cli/main.mjs +27 -46
  78. package/src/constants.mjs +3 -4
  79. package/src/dev-server.mjs +30 -324
  80. package/src/doctor.mjs +92 -154
  81. package/src/dom-event-instrumentation.mjs +55 -147
  82. package/src/errors.mjs +2 -3
  83. package/src/event-manager.mjs +239 -624
  84. package/src/event-queue.mjs +3 -3
  85. package/src/import-map.mjs +273 -1028
  86. package/src/index.mjs +14 -16
  87. package/src/installed-sdk-runtime.mjs +40 -62
  88. package/src/integrated-provider-loader.mjs +53 -382
  89. package/src/mail-api.mjs +0 -2
  90. package/src/mail-server.mjs +224 -580
  91. package/src/mail.mjs +4 -10
  92. package/src/native-plan.mjs +163 -598
  93. package/src/native-provider-loader.mjs +104 -1063
  94. package/src/packager/core.mjs +485 -3229
  95. package/src/process.mjs +5 -10
  96. package/src/release-bundle.mjs +292 -2405
  97. package/src/runtime.mjs +76 -396
  98. package/src/scaffold.mjs +30 -80
  99. package/src/sdk-browser-runtime.mjs +70 -626
  100. package/src/source-server.mjs +588 -0
  101. package/src/targets/index.mjs +78 -188
  102. package/src/templates/workspace-template.mjs +19 -135
  103. package/src/testing-loader.mjs +164 -0
  104. package/src/testing.mjs +1 -1
  105. package/src/toolchain.mjs +131 -544
  106. package/src/update-check.mjs +26 -64
  107. package/src/workspace-operation-lock.mjs +139 -430
  108. package/src/workspace-runtime.mjs +112 -779
  109. package/src/workspace.mjs +40 -302
  110. package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
  111. package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
  112. package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
  113. package/browser-runtime/ai/internal/sha256.mjs +0 -166
  114. package/docs/architecture.md +0 -344
  115. package/docs/compatibility.md +0 -36
  116. package/docs/event-manager.md +0 -294
  117. package/docs/platform-targets.md +0 -108
  118. package/docs/publishing.md +0 -201
  119. package/docs/reference/README.md +0 -187
  120. package/docs/reference/ai/browser-speech-package-authority.json +0 -835
  121. package/docs/reference/ai/browser-speech.md +0 -1252
  122. package/docs/reference/ai/browser-wasm.md +0 -530
  123. package/docs/reference/arcane-ollama.md +0 -288
  124. package/docs/reference/availability-and-normalization.md +0 -183
  125. package/docs/reference/behavioral-testing.md +0 -133
  126. package/docs/reference/cli.md +0 -779
  127. package/docs/reference/core/README.md +0 -62
  128. package/docs/reference/core/arcane-ai-contracts.md +0 -907
  129. package/docs/reference/core/arcane-api.md +0 -601
  130. package/docs/reference/core/arcane-entities.md +0 -65
  131. package/docs/reference/core/arcane-events.md +0 -134
  132. package/docs/reference/core/ollama-module.md +0 -181
  133. package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
  134. package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
  135. package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
  136. package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
  137. package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
  138. package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
  139. package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
  140. package/docs/reference/event-manager.md +0 -1511
  141. package/docs/reference/inventory/package-api.json +0 -3284
  142. package/docs/reference/inventory/runtime-components.json +0 -1011
  143. package/docs/reference/inventory/runtime-entities.json +0 -26
  144. package/docs/reference/inventory/runtime-modules.json +0 -1431
  145. package/docs/reference/mail.md +0 -316
  146. package/docs/reference/protocols.md +0 -677
  147. package/docs/reference/runtime-components.md +0 -1366
  148. package/docs/reference/runtime-entities.md +0 -303
  149. package/docs/reference/runtime-modules.md +0 -2960
  150. package/docs/reference/sdk-api.md +0 -6694
  151. package/docs/roadmap.md +0 -79
  152. package/docs/work-amplification.md +0 -129
  153. package/runtime/ARCANE_RUNTIME_RELEASE.json +0 -826
@@ -2,7 +2,7 @@ import {createArcaneEventSource} from 'arcane-os/event-manager';
2
2
  import Preference,{preferenceSchema} from '../entities/Preference.js';
3
3
  import {resolveApplicationLocalStorageKey} from './AppDataScope.js';
4
4
 
5
- export const PREFERENCE_STORE_ERROR_CODES=Object.freeze({
5
+ export const PREFERENCE_STORE_ERROR_CODES={
6
6
  adapterInvalid:'ARCANE_PREFERENCE_STORE_ADAPTER_INVALID',
7
7
  disposed:'ARCANE_PREFERENCE_STORE_DISPOSED',
8
8
  eventDetailInvalid:'ARCANE_PREFERENCE_EVENT_DETAIL_INVALID',
@@ -10,13 +10,13 @@ export const PREFERENCE_STORE_ERROR_CODES=Object.freeze({
10
10
  operationAborted:'ARCANE_PREFERENCE_STORE_OPERATION_ABORTED',
11
11
  operationOptionsInvalid:'ARCANE_PREFERENCE_STORE_OPERATION_OPTIONS_INVALID',
12
12
  valuesInvalid:'ARCANE_PREFERENCE_VALUES_INVALID'
13
- });
13
+ };
14
14
 
15
- export const PREFERENCE_STORE_EVENT_TYPES=Object.freeze({
15
+ export const PREFERENCE_STORE_EVENT_TYPES={
16
16
  change:'preference-change',
17
17
  load:'preference-load',
18
18
  reset:'preference-reset'
19
- });
19
+ };
20
20
 
21
21
  function preferenceStoreError(code,reason,message,ErrorType=Error,cause){
22
22
  const error=new ErrorType(message);
@@ -89,7 +89,7 @@ function normalizeOperationOptions(value={}){
89
89
  TypeError
90
90
  );
91
91
  }
92
- return Object.freeze({signal});
92
+ return {signal};
93
93
  }
94
94
 
95
95
  function setDataProperty(target,key,value){
@@ -100,21 +100,23 @@ function setDataProperty(target,key,value){
100
100
  );
101
101
  }
102
102
 
103
- function frozenPreferenceValues(values){
103
+ function completePreferenceValues(values){
104
104
  const copy={};
105
105
  for(const [key,value] of Object.entries(values)) setDataProperty(copy,key,value);
106
- return Object.freeze(copy);
106
+ return copy;
107
107
  }
108
108
 
109
109
  function validateAdapter(adapter){
110
110
  if(!adapter
111
111
  ||typeof adapter.get!=='function'
112
112
  ||typeof adapter.set!=='function'
113
- ||typeof adapter.delete!=='function'){
113
+ ||typeof adapter.delete!=='function'
114
+ ||(Reflect.has(adapter,'setMany')&&typeof adapter.setMany!=='function')){
114
115
  throw preferenceStoreError(
115
116
  PREFERENCE_STORE_ERROR_CODES.adapterInvalid,
116
117
  'preference-storage-adapter-invalid',
117
- 'The preference storage adapter must provide get, set, and delete methods.',
118
+ 'The preference storage adapter must provide get, set, and delete methods; '
119
+ +'setMany must be a function when provided.',
118
120
  TypeError
119
121
  );
120
122
  }
@@ -142,7 +144,9 @@ function localAdapter(prefix){
142
144
 
143
145
  function nativeAdapter(){
144
146
  const preferences=globalThis.Arcane?.preferences;
145
- if(!preferences?.get||!preferences?.set||!preferences?.delete) return null;
147
+ if(typeof preferences?.get!=='function'
148
+ ||typeof preferences?.set!=='function'
149
+ ||typeof preferences?.delete!=='function') return null;
146
150
  return preferences;
147
151
  }
148
152
 
@@ -162,10 +166,12 @@ function preferenceAdapter(){
162
166
  }catch(error){
163
167
  if(active!==native||!isUnsupportedNativeAdapter(error)) throw error;
164
168
  active=local;
169
+ delete adapter.setMany;
170
+ if(typeof active[method]!=='function') throw error;
165
171
  return active[method](...args);
166
172
  }
167
173
  }
168
- return {
174
+ const adapter={
169
175
  async get(key){
170
176
  return call('get',[key]);
171
177
  },
@@ -176,6 +182,12 @@ function preferenceAdapter(){
176
182
  return call('delete',[key]);
177
183
  }
178
184
  };
185
+ if(typeof native.setMany==='function'){
186
+ adapter.setMany=async function setMany(entries,context){
187
+ return call('setMany',[entries,context]);
188
+ };
189
+ }
190
+ return adapter;
179
191
  }
180
192
 
181
193
  export default class PreferenceStore extends EventTarget{
@@ -195,7 +207,7 @@ export default class PreferenceStore extends EventTarget{
195
207
  this,
196
208
  {
197
209
  source:'preference-store',
198
- eventTypes:Object.freeze(Object.values(PREFERENCE_STORE_EVENT_TYPES))
210
+ eventTypes:Object.values(PREFERENCE_STORE_EVENT_TYPES)
199
211
  }
200
212
  );
201
213
  }
@@ -210,7 +222,7 @@ export default class PreferenceStore extends EventTarget{
210
222
  for(const definition of this.schema){
211
223
  setDataProperty(values,definition.key,definition.defaultValue);
212
224
  }
213
- return frozenPreferenceValues(values);
225
+ return completePreferenceValues(values);
214
226
  }
215
227
 
216
228
  storageKey(key){return `${this.namespace}.${key}`;}
@@ -240,7 +252,7 @@ export default class PreferenceStore extends EventTarget{
240
252
  for(const definition of store.schema){
241
253
  const result=await store.adapter.get(
242
254
  store.storageKey(definition.key),
243
- Object.freeze({operationId,signal:operation.signal})
255
+ {operationId,signal:operation.signal}
244
256
  );
245
257
  store.#assertOperationActive(operation.signal);
246
258
  setDataProperty(
@@ -251,9 +263,9 @@ export default class PreferenceStore extends EventTarget{
251
263
  :definition.defaultValue
252
264
  );
253
265
  }
254
- store.values=frozenPreferenceValues(values);
266
+ store.values=completePreferenceValues(values);
255
267
  store.#publish(PREFERENCE_STORE_EVENT_TYPES.load,{},operationId);
256
- return frozenPreferenceValues(store.values);
268
+ return completePreferenceValues(store.values);
257
269
  },
258
270
  operation.signal
259
271
  );
@@ -270,10 +282,10 @@ export default class PreferenceStore extends EventTarget{
270
282
  await store.adapter.set(
271
283
  store.storageKey(key),
272
284
  normalized,
273
- Object.freeze({operationId,signal:operation.signal})
285
+ {operationId,signal:operation.signal}
274
286
  );
275
287
  store.#assertOperationActive(operation.signal);
276
- store.values=frozenPreferenceValues({...store.values,[key]:normalized});
288
+ store.values=completePreferenceValues({...store.values,[key]:normalized});
277
289
  store.#publish(
278
290
  PREFERENCE_STORE_EVENT_TYPES.change,
279
291
  {key,value:normalized},
@@ -295,13 +307,64 @@ export default class PreferenceStore extends EventTarget{
295
307
  TypeError
296
308
  );
297
309
  }
310
+ const selected=[];
298
311
  for(const definition of this.schema){
299
312
  if(Object.prototype.hasOwnProperty.call(values,definition.key)){
300
- await this.set(definition.key,values[definition.key],operation);
313
+ selected.push({
314
+ key:definition.key,
315
+ storageKey:this.storageKey(definition.key),
316
+ value:definition.value(values[definition.key])
317
+ });
301
318
  }
302
319
  }
303
- this.#assertOperationActive(operation.signal);
304
- return frozenPreferenceValues(this.values);
320
+ if(selected.length===0){
321
+ this.#assertOperationActive(operation.signal);
322
+ return completePreferenceValues(this.values);
323
+ }
324
+ const operationId=this.#nextOperationId('set-all');
325
+ const store=this;
326
+ const entries=selected;
327
+ return this.#enqueueOperation(
328
+ async function setAllPreferences(commitPreferenceOperation){
329
+ const context={operationId,signal:operation.signal};
330
+ if(typeof store.adapter.setMany==='function'){
331
+ const batch={};
332
+ for(const entry of entries){
333
+ setDataProperty(batch,entry.storageKey,entry.value);
334
+ }
335
+ await store.adapter.setMany(batch,context);
336
+ store.#assertOperationActive(operation.signal);
337
+ commitPreferenceOperation();
338
+ const next={...store.values};
339
+ for(const entry of entries) setDataProperty(next,entry.key,entry.value);
340
+ store.values=completePreferenceValues(next);
341
+ for(const entry of entries){
342
+ store.#publish(
343
+ PREFERENCE_STORE_EVENT_TYPES.change,
344
+ {key:entry.key,value:entry.value},
345
+ operationId
346
+ );
347
+ }
348
+ return completePreferenceValues(store.values);
349
+ }
350
+ for(const entry of entries){
351
+ await store.adapter.set(entry.storageKey,entry.value,context);
352
+ store.#assertOperationActive(operation.signal);
353
+ store.values=completePreferenceValues({
354
+ ...store.values,
355
+ [entry.key]:entry.value
356
+ });
357
+ store.#publish(
358
+ PREFERENCE_STORE_EVENT_TYPES.change,
359
+ {key:entry.key,value:entry.value},
360
+ operationId
361
+ );
362
+ store.#assertOperationActive(operation.signal);
363
+ }
364
+ return completePreferenceValues(store.values);
365
+ },
366
+ operation.signal
367
+ );
305
368
  }
306
369
 
307
370
  async reset(options={}){
@@ -313,13 +376,13 @@ export default class PreferenceStore extends EventTarget{
313
376
  for(const definition of store.schema){
314
377
  await store.adapter.delete(
315
378
  store.storageKey(definition.key),
316
- Object.freeze({operationId,signal:operation.signal})
379
+ {operationId,signal:operation.signal}
317
380
  );
318
381
  store.#assertOperationActive(operation.signal);
319
382
  }
320
383
  store.values=store.defaults();
321
384
  store.#publish(PREFERENCE_STORE_EVENT_TYPES.reset,{},operationId);
322
- return frozenPreferenceValues(store.values);
385
+ return completePreferenceValues(store.values);
323
386
  },
324
387
  operation.signal
325
388
  );
@@ -365,19 +428,19 @@ export default class PreferenceStore extends EventTarget{
365
428
  }
366
429
 
367
430
  #publish(type,detail,operationId){
368
- const values=frozenPreferenceValues(this.values);
369
- const compatibilityDetail=Object.freeze({
431
+ const values=completePreferenceValues(this.values);
432
+ const compatibilityDetail={
370
433
  ...detail,
371
434
  namespace:this.namespace,
372
435
  values
373
- });
374
- const publicDetail=Object.freeze({
436
+ };
437
+ const publicDetail={
375
438
  namespace:this.namespace,
376
439
  values,
377
440
  ...(typeof detail.key==='string'
378
441
  ?{preferenceId:detail.key,value:detail.value}
379
442
  :{})
380
- });
443
+ };
381
444
  this.#events.dispatch(
382
445
  type,
383
446
  compatibilityDetail,
@@ -388,6 +451,7 @@ export default class PreferenceStore extends EventTarget{
388
451
  #enqueueOperation(operation,signal){
389
452
  this.#assertOperationActive(signal);
390
453
  const store=this;
454
+ let committed=false;
391
455
  let abortHandler=null;
392
456
  let rejectResult;
393
457
  let resolveResult;
@@ -410,6 +474,14 @@ export default class PreferenceStore extends EventTarget{
410
474
  function cancelPreferenceOperation(error){
411
475
  return settlePreferenceOperation(rejectResult,error);
412
476
  }
477
+ function commitPreferenceOperation(){
478
+ store.#assertOperationActive(signal);
479
+ if(settled) return false;
480
+ committed=true;
481
+ signal?.removeEventListener('abort',abortHandler);
482
+ store.#pendingOperations.delete(cancelPreferenceOperation);
483
+ return true;
484
+ }
413
485
  abortHandler=function abortPreferenceOperation(){
414
486
  cancelPreferenceOperation(operationAbortedError(signal.reason));
415
487
  };
@@ -421,8 +493,8 @@ export default class PreferenceStore extends EventTarget{
421
493
  if(settled) return;
422
494
  try{
423
495
  store.#assertOperationActive(signal);
424
- const value=await operation();
425
- store.#assertOperationActive(signal);
496
+ const value=await operation(commitPreferenceOperation);
497
+ if(!committed) store.#assertOperationActive(signal);
426
498
  settlePreferenceOperation(resolveResult,value);
427
499
  }catch(error){
428
500
  settlePreferenceOperation(rejectResult,error);
@@ -1,16 +1,16 @@
1
- const DEFAULT_LEVELS=Object.freeze([
1
+ const DEFAULT_LEVELS=[
2
2
  {minimum:70,id:'critical'},
3
3
  {minimum:40,id:'high'},
4
4
  {minimum:15,id:'caution'},
5
5
  {minimum:0,id:'low'},
6
- ]);
6
+ ];
7
7
 
8
- function normalizeText(value,maxLength){
9
- return String(value??'').normalize('NFKC').slice(0,maxLength);
8
+ function normalizeText(value){
9
+ return String(value??'').normalize('NFKC');
10
10
  }
11
11
 
12
- export function analyzeRiskSignals(input,{signals=[],levels=DEFAULT_LEVELS,maxLength=20_000}={}){
13
- const text=normalizeText(input,maxLength);
12
+ export function analyzeRiskSignals(input,{signals=[],levels=DEFAULT_LEVELS}={}){
13
+ const text=normalizeText(input);
14
14
  const matches=[];
15
15
  let score=0;
16
16
 
@@ -18,7 +18,7 @@ export function analyzeRiskSignals(input,{signals=[],levels=DEFAULT_LEVELS,maxLe
18
18
  if(!signal||typeof signal.id!=='string'||!(signal.pattern instanceof RegExp))continue;
19
19
  signal.pattern.lastIndex=0;
20
20
  if(!signal.pattern.test(text))continue;
21
- const weight=Math.max(0,Math.min(100,Number(signal.weight)||0));
21
+ const weight=Math.max(0,Number(signal.weight)||0);
22
22
  score+=weight;
23
23
  matches.push({
24
24
  id:signal.id,
@@ -28,10 +28,9 @@ export function analyzeRiskSignals(input,{signals=[],levels=DEFAULT_LEVELS,maxLe
28
28
  });
29
29
  }
30
30
 
31
- score=Math.min(100,score);
32
31
  const ordered=[...levels].sort((a,b)=>b.minimum-a.minimum);
33
32
  const level=ordered.find(candidate=>score>=candidate.minimum)?.id||'unknown';
34
- return Object.freeze({level,matches:Object.freeze(matches),score,textLength:text.length,truncated:String(input??'').length>text.length});
33
+ return {level,matches,score,textLength:text.length};
35
34
  }
36
35
 
37
36
  export {DEFAULT_LEVELS};
@@ -3,17 +3,13 @@ import {
3
3
  openApplicationDataDirectory
4
4
  } from './AppDataScope.js';
5
5
 
6
- const DEFAULT_MAX_ENTRY_BYTES=4*1024*1024;
7
- const MAX_NAMESPACE_LENGTH=96;
8
- const MAX_KEY_LENGTH=240;
9
-
10
- function safeSegment(value,label,maximum){
6
+ function safeSegment(value,label){
11
7
  if(typeof value!=='string'){
12
8
  throw new TypeError(`${label} must be a string.`);
13
9
  }
14
10
  const normalized=value.trim();
15
- if(!normalized||normalized.length>maximum){
16
- throw new RangeError(`${label} must contain between 1 and ${maximum} characters.`);
11
+ if(!normalized){
12
+ throw new RangeError(`${label} must not be empty.`);
17
13
  }
18
14
  if(normalized==='.'||normalized==='..'||/[\\/\0]/u.test(normalized)){
19
15
  throw new TypeError(`${label} must be one filename-safe segment.`);
@@ -21,25 +17,12 @@ function safeSegment(value,label,maximum){
21
17
  return normalized;
22
18
  }
23
19
 
24
- function positiveInteger(value,label,maximum){
25
- if(!Number.isSafeInteger(value)||value<1||value>maximum){
26
- throw new RangeError(`${label} must be an integer between 1 and ${maximum}.`);
27
- }
28
- return value;
29
- }
30
-
31
20
  function unavailable(){
32
21
  const error=new Error('Origin Private File System storage is unavailable in this browser.');
33
22
  error.code='OPFS_UNAVAILABLE';
34
23
  return error;
35
24
  }
36
25
 
37
- function entryTooLarge(maximum){
38
- const error=new RangeError(`The cache entry exceeds the ${maximum}-byte limit.`);
39
- error.code='OPFS_CACHE_ENTRY_TOO_LARGE';
40
- return error;
41
- }
42
-
43
26
  /**
44
27
  * A deliberately narrow JSON cache over one application-owned OPFS directory.
45
28
  *
@@ -52,14 +35,12 @@ export default class ScopedOPFSCache{
52
35
  #arcane;
53
36
  #directoryPromise=null;
54
37
  #documentObject;
55
- #maxEntryBytes;
56
38
  #namespace;
57
39
  #storage;
58
40
 
59
41
  constructor({
60
42
  applicationId=null,
61
43
  namespace,
62
- maxEntryBytes=DEFAULT_MAX_ENTRY_BYTES,
63
44
  storage=globalThis.navigator?.storage,
64
45
  documentObject=globalThis.document,
65
46
  arcane=globalThis.Arcane
@@ -67,8 +48,7 @@ export default class ScopedOPFSCache{
67
48
  this.#applicationId=applicationId==null||applicationId===''
68
49
  ?null
69
50
  :canonicalApplicationId(applicationId);
70
- this.#namespace=safeSegment(namespace,'namespace',MAX_NAMESPACE_LENGTH);
71
- this.#maxEntryBytes=positiveInteger(maxEntryBytes,'maxEntryBytes',64*1024*1024);
51
+ this.#namespace=safeSegment(namespace,'namespace');
72
52
  if(!storage||typeof storage.getDirectory!=='function'){
73
53
  throw unavailable();
74
54
  }
@@ -89,10 +69,6 @@ export default class ScopedOPFSCache{
89
69
  return this.#applicationId;
90
70
  }
91
71
 
92
- get maxEntryBytes(){
93
- return this.#maxEntryBytes;
94
- }
95
-
96
72
  async #directory(){
97
73
  if(!this.#directoryPromise){
98
74
  this.#directoryPromise=(async()=>{
@@ -117,20 +93,12 @@ export default class ScopedOPFSCache{
117
93
  }
118
94
 
119
95
  async get(key){
120
- const normalized=safeSegment(key,'key',MAX_KEY_LENGTH);
96
+ const normalized=safeSegment(key,'key');
121
97
  try{
122
98
  const directory=await this.#directory();
123
99
  const handle=await directory.getFileHandle(normalized);
124
100
  const file=await handle.getFile();
125
- if(file.size>this.#maxEntryBytes){
126
- await this.delete(normalized).catch(()=>{});
127
- return undefined;
128
- }
129
101
  const source=await file.text();
130
- if(new TextEncoder().encode(source).byteLength>this.#maxEntryBytes){
131
- await this.delete(normalized).catch(()=>{});
132
- return undefined;
133
- }
134
102
  try{
135
103
  return JSON.parse(source);
136
104
  }catch{
@@ -146,14 +114,11 @@ export default class ScopedOPFSCache{
146
114
  }
147
115
 
148
116
  async set(key,value){
149
- const normalized=safeSegment(key,'key',MAX_KEY_LENGTH);
117
+ const normalized=safeSegment(key,'key');
150
118
  const source=JSON.stringify(value);
151
119
  if(source===undefined){
152
120
  throw new TypeError('Cache values must be JSON serializable.');
153
121
  }
154
- if(new TextEncoder().encode(source).byteLength>this.#maxEntryBytes){
155
- throw entryTooLarge(this.#maxEntryBytes);
156
- }
157
122
  const directory=await this.#directory();
158
123
  const handle=await directory.getFileHandle(normalized,{create:true});
159
124
  const writable=await handle.createWritable();
@@ -168,7 +133,7 @@ export default class ScopedOPFSCache{
168
133
  }
169
134
 
170
135
  async delete(key){
171
- const normalized=safeSegment(key,'key',MAX_KEY_LENGTH);
136
+ const normalized=safeSegment(key,'key');
172
137
  const directory=await this.#directory();
173
138
  try{
174
139
  await directory.removeEntry(normalized);