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.
- package/CHANGELOG.md +27 -0
- package/README.md +86 -117
- package/bin/arcane-test.mjs +170 -46
- package/browser-runtime/ai/browser-speech-artifacts.mjs +887 -909
- package/browser-runtime/ai/browser-speech-providers.mjs +96 -152
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +627 -819
- package/browser-runtime/ai/browser-wasm.mjs +24 -35
- package/browser-runtime/ai/browser-wllama-runtime.mjs +64 -316
- package/browser-runtime/ai/model-controller.mjs +584 -181
- package/browser-runtime/ai/speech-worker-client.mjs +8 -146
- package/browser-runtime/ai/speech-worker-runtime.mjs +643 -363
- package/browser-runtime/dom-event-instrumentation.mjs +55 -147
- package/browser-runtime/event-manager.mjs +239 -624
- package/package.json +5 -6
- package/runtime/arcane/components/app-bar.html +3 -15
- package/runtime/arcane/components/assistant-panel.html +10 -10
- package/runtime/arcane/components/calculator.html +1 -1
- package/runtime/arcane/components/chat.html +1359 -135
- package/runtime/arcane/components/conversation-view.html +2 -2
- package/runtime/arcane/components/document-inspector.html +11 -17
- package/runtime/arcane/components/file-manager.html +13 -56
- package/runtime/arcane/components/markdown-document.html +82 -281
- package/runtime/arcane/components/markdown-editor.html +7 -10
- package/runtime/arcane/components/media-embed.html +6 -6
- package/runtime/arcane/components/screen-capture.html +4 -4
- package/runtime/arcane/components/source-explanation.html +2 -2
- package/runtime/arcane/components/speech.html +112 -68
- package/runtime/arcane/components/terminal-workspace.html +4 -4
- package/runtime/arcane/components/theme-editor.html +1 -1
- package/runtime/arcane/components/unified-inbox.html +2 -2
- package/runtime/arcane/components/voice-transcription.html +31 -21
- package/runtime/arcane/entities/Calculation.js +2 -3
- package/runtime/arcane/entities/Chat.js +228 -43
- package/runtime/arcane/entities/Preference.js +3 -5
- package/runtime/arcane/entities/Weather.js +5 -5
- package/runtime/arcane/modules/AI.js +1050 -427
- package/runtime/arcane/modules/AIProviderRuntime.js +658 -363
- package/runtime/arcane/modules/AIResponseLength.js +9 -19
- package/runtime/arcane/modules/AIRuntimeState.js +109 -72
- package/runtime/arcane/modules/ArcaneNavigationPolicy.js +45 -32
- package/runtime/arcane/modules/BrowserTestSuite.js +78 -122
- package/runtime/arcane/modules/CalculatorEngine.js +9 -9
- package/runtime/arcane/modules/CommunicationAppController.js +3 -7
- package/runtime/arcane/modules/ComponentContracts.js +30 -32
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +281 -230
- package/runtime/arcane/modules/ConversationActionItems.js +26 -59
- package/runtime/arcane/modules/ConversationClosingReport.js +34 -61
- package/runtime/arcane/modules/ConversationTimebox.js +27 -15
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +152 -344
- package/runtime/arcane/modules/DocumentLexicalSearch.js +25 -91
- package/runtime/arcane/modules/HTMLImport.js +54 -1
- package/runtime/arcane/modules/IsolatedModelQuestionRunner.js +40 -203
- package/runtime/arcane/modules/LocalAIReadiness.js +40 -60
- package/runtime/arcane/modules/LocalAIReadinessController.js +15 -13
- package/runtime/arcane/modules/MD.js +1 -45
- package/runtime/arcane/modules/Mail.js +51 -103
- package/runtime/arcane/modules/MailOutbox.mjs +95 -193
- package/runtime/arcane/modules/MailTransport.mjs +36 -57
- package/runtime/arcane/modules/ModelDefinition.js +22 -106
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +39 -101
- package/runtime/arcane/modules/PersistentAIChatSession.js +281 -18
- package/runtime/arcane/modules/PreferenceStore.js +102 -30
- package/runtime/arcane/modules/RiskSignalAnalyzer.js +8 -9
- package/runtime/arcane/modules/ScopedOPFSCache.js +7 -42
- package/runtime/arcane/modules/ScreenCapture.js +175 -128
- package/runtime/arcane/modules/SpeechPlayback.js +46 -149
- package/runtime/arcane/modules/StaticDocumentCatalog.js +173 -407
- package/runtime/arcane/modules/ToolCallRouter.js +25 -12
- package/runtime/arcane/modules/YouTubeMedia.js +6 -5
- package/schemas/arcane-app-bundle.schema.json +13 -78
- package/schemas/arcane-app.schema.json +9 -25
- package/schemas/arcane-lock.schema.json +18 -151
- package/schemas/arcane-package.schema.json +2 -16
- package/schemas/native-build-plan.schema.json +119 -122
- package/src/app-descriptor.mjs +75 -132
- package/src/application-tests.mjs +200 -0
- package/src/cli/main.mjs +27 -46
- package/src/constants.mjs +3 -4
- package/src/dev-server.mjs +30 -324
- package/src/doctor.mjs +92 -154
- package/src/dom-event-instrumentation.mjs +55 -147
- package/src/errors.mjs +2 -3
- package/src/event-manager.mjs +239 -624
- package/src/event-queue.mjs +3 -3
- package/src/import-map.mjs +273 -1028
- package/src/index.mjs +14 -16
- package/src/installed-sdk-runtime.mjs +40 -62
- package/src/integrated-provider-loader.mjs +53 -382
- package/src/mail-api.mjs +0 -2
- package/src/mail-server.mjs +224 -580
- package/src/mail.mjs +4 -10
- package/src/native-plan.mjs +163 -598
- package/src/native-provider-loader.mjs +104 -1063
- package/src/packager/core.mjs +485 -3229
- package/src/process.mjs +5 -10
- package/src/release-bundle.mjs +292 -2405
- package/src/runtime.mjs +76 -396
- package/src/scaffold.mjs +30 -80
- package/src/sdk-browser-runtime.mjs +70 -626
- package/src/source-server.mjs +588 -0
- package/src/targets/index.mjs +78 -188
- package/src/templates/workspace-template.mjs +19 -135
- package/src/testing-loader.mjs +164 -0
- package/src/testing.mjs +1 -1
- package/src/toolchain.mjs +131 -544
- package/src/update-check.mjs +26 -64
- package/src/workspace-operation-lock.mjs +139 -430
- package/src/workspace-runtime.mjs +112 -779
- package/src/workspace.mjs +40 -302
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +0 -218
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +0 -203
- package/browser-runtime/ai/ARCANE_AI_BROWSER_WASM_COMPONENTS.json +0 -80
- package/browser-runtime/ai/internal/sha256.mjs +0 -166
- package/docs/architecture.md +0 -344
- package/docs/compatibility.md +0 -36
- package/docs/event-manager.md +0 -294
- package/docs/platform-targets.md +0 -108
- package/docs/publishing.md +0 -201
- package/docs/reference/README.md +0 -187
- package/docs/reference/ai/browser-speech-package-authority.json +0 -835
- package/docs/reference/ai/browser-speech.md +0 -1252
- package/docs/reference/ai/browser-wasm.md +0 -530
- package/docs/reference/arcane-ollama.md +0 -288
- package/docs/reference/availability-and-normalization.md +0 -183
- package/docs/reference/behavioral-testing.md +0 -133
- package/docs/reference/cli.md +0 -779
- package/docs/reference/core/README.md +0 -62
- package/docs/reference/core/arcane-ai-contracts.md +0 -907
- package/docs/reference/core/arcane-api.md +0 -601
- package/docs/reference/core/arcane-entities.md +0 -65
- package/docs/reference/core/arcane-events.md +0 -134
- package/docs/reference/core/ollama-module.md +0 -181
- package/docs/reference/core/reference/arcane-api/ai-and-ollama.md +0 -1909
- package/docs/reference/core/reference/arcane-api/applications-terminal-capabilities.md +0 -1057
- package/docs/reference/core/reference/arcane-api/core-and-events.md +0 -320
- package/docs/reference/core/reference/arcane-api/filesystem-storage-preferences-appearance.md +0 -610
- package/docs/reference/core/reference/arcane-api/namespaces.md +0 -1157
- package/docs/reference/core/reference/arcane-api/platform-installation-users-system.md +0 -1423
- package/docs/reference/core/reference/arcane-api/session-provisioning-diagnostics-development.md +0 -315
- package/docs/reference/event-manager.md +0 -1511
- package/docs/reference/inventory/package-api.json +0 -3284
- package/docs/reference/inventory/runtime-components.json +0 -1011
- package/docs/reference/inventory/runtime-entities.json +0 -26
- package/docs/reference/inventory/runtime-modules.json +0 -1431
- package/docs/reference/mail.md +0 -316
- package/docs/reference/protocols.md +0 -677
- package/docs/reference/runtime-components.md +0 -1366
- package/docs/reference/runtime-entities.md +0 -303
- package/docs/reference/runtime-modules.md +0 -2960
- package/docs/reference/sdk-api.md +0 -6694
- package/docs/roadmap.md +0 -79
- package/docs/work-amplification.md +0 -129
- 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=
|
|
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=
|
|
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
|
|
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
|
|
103
|
+
function completePreferenceValues(values){
|
|
104
104
|
const copy={};
|
|
105
105
|
for(const [key,value] of Object.entries(values)) setDataProperty(copy,key,value);
|
|
106
|
-
return
|
|
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(
|
|
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
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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=
|
|
266
|
+
store.values=completePreferenceValues(values);
|
|
255
267
|
store.#publish(PREFERENCE_STORE_EVENT_TYPES.load,{},operationId);
|
|
256
|
-
return
|
|
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
|
-
|
|
285
|
+
{operationId,signal:operation.signal}
|
|
274
286
|
);
|
|
275
287
|
store.#assertOperationActive(operation.signal);
|
|
276
|
-
store.values=
|
|
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
|
-
|
|
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
|
-
|
|
304
|
-
|
|
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
|
-
|
|
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
|
|
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=
|
|
369
|
-
const compatibilityDetail=
|
|
431
|
+
const values=completePreferenceValues(this.values);
|
|
432
|
+
const compatibilityDetail={
|
|
370
433
|
...detail,
|
|
371
434
|
namespace:this.namespace,
|
|
372
435
|
values
|
|
373
|
-
}
|
|
374
|
-
const publicDetail=
|
|
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=
|
|
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
|
|
9
|
-
return String(value??'').normalize('NFKC')
|
|
8
|
+
function normalizeText(value){
|
|
9
|
+
return String(value??'').normalize('NFKC');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
export function analyzeRiskSignals(input,{signals=[],levels=DEFAULT_LEVELS
|
|
13
|
-
const text=normalizeText(input
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
|
16
|
-
throw new RangeError(`${label} must
|
|
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'
|
|
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'
|
|
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'
|
|
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'
|
|
136
|
+
const normalized=safeSegment(key,'key');
|
|
172
137
|
const directory=await this.#directory();
|
|
173
138
|
try{
|
|
174
139
|
await directory.removeEntry(normalized);
|