arcane-os 0.2.2 → 0.3.0
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 +29 -0
- package/README.md +8 -8
- package/browser-runtime/ARCANE_SDK_BROWSER_RELEASE.json +29 -22
- package/browser-runtime/ai/ARCANE_AI_BROWSER_SPEECH_COMPONENTS.json +203 -0
- package/browser-runtime/ai/browser-kokoro-worker.mjs +11 -2
- package/browser-runtime/ai/browser-speech-artifacts.mjs +3230 -397
- package/browser-runtime/ai/browser-speech-providers.mjs +1141 -157
- package/browser-runtime/ai/browser-speech.mjs +2 -0
- package/browser-runtime/ai/browser-whisper-worker.mjs +11 -2
- package/browser-runtime/ai/model-controller.mjs +285 -95
- package/browser-runtime/ai/speech-worker-client.mjs +247 -32
- package/browser-runtime/ai/speech-worker-runtime.mjs +2310 -167
- package/browser-runtime/event-manager.mjs +1097 -1
- package/docs/architecture.md +2 -2
- package/docs/event-manager.md +155 -27
- package/docs/reference/README.md +27 -27
- package/docs/reference/ai/browser-speech-package-authority.json +835 -0
- package/docs/reference/ai/browser-speech.md +1162 -246
- package/docs/reference/ai/browser-wasm.md +18 -7
- package/docs/reference/availability-and-normalization.md +6 -3
- package/docs/reference/behavioral-testing.md +29 -6
- package/docs/reference/cli.md +117 -9
- package/docs/reference/core/arcane-ai-contracts.md +1 -1
- package/docs/reference/event-manager.md +577 -32
- package/docs/reference/inventory/package-api.json +478 -2
- package/docs/reference/inventory/runtime-components.json +108 -44
- package/docs/reference/inventory/runtime-modules.json +131 -53
- package/docs/reference/mail.md +316 -0
- package/docs/reference/protocols.md +157 -43
- package/docs/reference/runtime-components.md +258 -83
- package/docs/reference/runtime-modules.md +613 -77
- package/docs/reference/sdk-api.md +1014 -25
- package/package.json +5 -4
- package/runtime/ARCANE_RUNTIME_RELEASE.json +145 -140
- package/runtime/arcane/components/app-bar.html +34 -13
- package/runtime/arcane/components/assistant-panel.html +110 -57
- package/runtime/arcane/components/calculator.html +7 -4
- package/runtime/arcane/components/chart.html +58 -17
- package/runtime/arcane/components/chat.html +606 -136
- package/runtime/arcane/components/conversation-view.html +13 -6
- package/runtime/arcane/components/dashboard-config.html +96 -59
- package/runtime/arcane/components/data-maintenance.html +69 -14
- package/runtime/arcane/components/data-view.html +53 -7
- package/runtime/arcane/components/directory-picker.html +118 -32
- package/runtime/arcane/components/document-inspector.html +47 -10
- package/runtime/arcane/components/file-drop.html +81 -35
- package/runtime/arcane/components/file-inspector.html +72 -22
- package/runtime/arcane/components/file-manager.html +374 -79
- package/runtime/arcane/components/integration-settings.html +12 -5
- package/runtime/arcane/components/local-ai-status.html +48 -19
- package/runtime/arcane/components/markdown-document.html +161 -68
- package/runtime/arcane/components/markdown-editor.html +110 -33
- package/runtime/arcane/components/media-embed.html +8 -5
- package/runtime/arcane/components/modal.html +15 -5
- package/runtime/arcane/components/output-panel.html +28 -23
- package/runtime/arcane/components/preferences-form.html +22 -4
- package/runtime/arcane/components/record-timeline.html +18 -2
- package/runtime/arcane/components/relationship-board.html +23 -3
- package/runtime/arcane/components/screen-capture.html +10 -4
- package/runtime/arcane/components/source-code-viewer.html +76 -9
- package/runtime/arcane/components/source-explanation.html +23 -3
- package/runtime/arcane/components/speech.html +462 -384
- package/runtime/arcane/components/summary-strip.html +22 -11
- package/runtime/arcane/components/table.html +39 -21
- package/runtime/arcane/components/task-progress.html +79 -21
- package/runtime/arcane/components/terminal-workspace.html +7 -4
- package/runtime/arcane/components/theme-editor.html +7 -3
- package/runtime/arcane/components/unified-inbox.html +9 -4
- package/runtime/arcane/components/voice-transcription.html +639 -98
- package/runtime/arcane/components/weather-widget.html +5 -3
- package/runtime/arcane/components/web-navigator.html +48 -8
- package/runtime/arcane/entities/Chat.js +1 -1
- package/runtime/arcane/entities/User.js +110 -23
- package/runtime/arcane/modules/AI.js +2109 -130
- package/runtime/arcane/modules/AIProviderRuntime.js +720 -13
- package/runtime/arcane/modules/AIRuntimeState.js +109 -52
- package/runtime/arcane/modules/ApiModelDatabase.js +390 -17
- package/runtime/arcane/modules/BrowserTestSuite.js +205 -28
- package/runtime/arcane/modules/CalculatorEngine.js +63 -3
- package/runtime/arcane/modules/CommunicationAppController.js +588 -28
- package/runtime/arcane/modules/CommunicationHub.js +590 -10
- package/runtime/arcane/modules/ComponentContracts.js +470 -0
- package/runtime/arcane/modules/ConversationTimebox.js +152 -33
- package/runtime/arcane/modules/DBLS.js +40 -7
- package/runtime/arcane/modules/DBOPFS.js +35 -11
- package/runtime/arcane/modules/DataMaintenance.js +12 -2
- package/runtime/arcane/modules/Errors.js +65 -7
- package/runtime/arcane/modules/HTMLImport.js +198 -14
- package/runtime/arcane/modules/LocalAIReadinessController.js +208 -29
- package/runtime/arcane/modules/Mail.js +738 -115
- package/runtime/arcane/modules/MailOutbox.mjs +1395 -0
- package/runtime/arcane/modules/MailTransport.mjs +197 -39
- package/runtime/arcane/modules/Ollama.js +36 -1
- package/runtime/arcane/modules/OpenMeteoWeatherProvider.js +583 -7
- package/runtime/arcane/modules/PreferenceStore.js +367 -33
- package/runtime/arcane/modules/RecordReviewStore.js +322 -23
- package/runtime/arcane/modules/ScreenCapture.js +1397 -15
- package/runtime/arcane/modules/SpeechPlayback.js +438 -41
- package/runtime/arcane/modules/TerminalClient.js +277 -12
- package/runtime/arcane/modules/ThemeBootstrap.js +80 -6
- package/runtime/arcane/modules/ThemeManager.js +39 -7
- package/runtime/arcane/modules/TimeGuard.js +131 -20
- package/runtime/arcane/modules/WaitForComponent.js +386 -33
- package/schemas/arcane-lock.schema.json +2 -2
- package/src/cli/main.mjs +435 -9
- package/src/event-manager.mjs +1097 -1
- package/src/import-map.mjs +21 -3
- package/src/index.mjs +13 -0
- package/src/installed-sdk-runtime.mjs +112 -0
- package/src/mail-api.mjs +22 -0
- package/src/mail-credentials.mjs +667 -0
- package/src/mail-server.mjs +1769 -0
- package/src/mail.mjs +261 -0
- package/src/sdk-browser-runtime.mjs +85 -41
- package/src/testing-loader.mjs +7 -0
- package/src/toolchain.mjs +3 -0
- package/src/workspace.mjs +1 -1
|
@@ -1,6 +1,126 @@
|
|
|
1
|
+
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
1
2
|
import Preference,{preferenceSchema} from '../entities/Preference.js';
|
|
2
3
|
import {resolveApplicationLocalStorageKey} from './AppDataScope.js';
|
|
3
4
|
|
|
5
|
+
export const PREFERENCE_STORE_ERROR_CODES=Object.freeze({
|
|
6
|
+
adapterInvalid:'ARCANE_PREFERENCE_STORE_ADAPTER_INVALID',
|
|
7
|
+
disposed:'ARCANE_PREFERENCE_STORE_DISPOSED',
|
|
8
|
+
eventDetailInvalid:'ARCANE_PREFERENCE_EVENT_DETAIL_INVALID',
|
|
9
|
+
keyUnknown:'ARCANE_PREFERENCE_KEY_UNKNOWN',
|
|
10
|
+
operationAborted:'ARCANE_PREFERENCE_STORE_OPERATION_ABORTED',
|
|
11
|
+
operationOptionsInvalid:'ARCANE_PREFERENCE_STORE_OPERATION_OPTIONS_INVALID',
|
|
12
|
+
valuesInvalid:'ARCANE_PREFERENCE_VALUES_INVALID'
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
export const PREFERENCE_STORE_EVENT_TYPES=Object.freeze({
|
|
16
|
+
change:'preference-change',
|
|
17
|
+
load:'preference-load',
|
|
18
|
+
reset:'preference-reset'
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
function preferenceStoreError(code,reason,message,ErrorType=Error,cause){
|
|
22
|
+
const error=new ErrorType(message);
|
|
23
|
+
error.code=code;
|
|
24
|
+
error.reason=reason;
|
|
25
|
+
if(cause!==undefined) error.cause=cause;
|
|
26
|
+
return error;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function disposedError(){
|
|
30
|
+
return preferenceStoreError(
|
|
31
|
+
PREFERENCE_STORE_ERROR_CODES.disposed,
|
|
32
|
+
'preference-store-disposed',
|
|
33
|
+
'The preference store has been disposed.'
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function operationAbortedError(reason){
|
|
38
|
+
const error=preferenceStoreError(
|
|
39
|
+
PREFERENCE_STORE_ERROR_CODES.operationAborted,
|
|
40
|
+
'preference-operation-aborted',
|
|
41
|
+
'The preference operation was aborted.',
|
|
42
|
+
Error,
|
|
43
|
+
reason
|
|
44
|
+
);
|
|
45
|
+
error.name='AbortError';
|
|
46
|
+
return error;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isPlainRecord(value){
|
|
50
|
+
if(!value||typeof value!=='object'||Array.isArray(value)) return false;
|
|
51
|
+
const prototype=Object.getPrototypeOf(value);
|
|
52
|
+
return prototype===Object.prototype||prototype===null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function isAbortSignal(value){
|
|
56
|
+
return Boolean(value)
|
|
57
|
+
&&typeof value==='object'
|
|
58
|
+
&&typeof value.aborted==='boolean'
|
|
59
|
+
&&typeof value.addEventListener==='function'
|
|
60
|
+
&&typeof value.removeEventListener==='function';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function normalizeOperationOptions(value={}){
|
|
64
|
+
if(!isPlainRecord(value)){
|
|
65
|
+
throw preferenceStoreError(
|
|
66
|
+
PREFERENCE_STORE_ERROR_CODES.operationOptionsInvalid,
|
|
67
|
+
'preference-operation-options-invalid',
|
|
68
|
+
'Preference operation options must be a plain object.',
|
|
69
|
+
TypeError
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
const keys=Reflect.ownKeys(value);
|
|
73
|
+
if(keys.some(function hasUnsupportedPreferenceOperationOption(key){
|
|
74
|
+
return key!=='signal';
|
|
75
|
+
})){
|
|
76
|
+
throw preferenceStoreError(
|
|
77
|
+
PREFERENCE_STORE_ERROR_CODES.operationOptionsInvalid,
|
|
78
|
+
'preference-operation-options-invalid',
|
|
79
|
+
'Preference operation options support only signal.',
|
|
80
|
+
TypeError
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
const signal=value.signal??null;
|
|
84
|
+
if(signal!==null&&!isAbortSignal(signal)){
|
|
85
|
+
throw preferenceStoreError(
|
|
86
|
+
PREFERENCE_STORE_ERROR_CODES.operationOptionsInvalid,
|
|
87
|
+
'preference-operation-signal-invalid',
|
|
88
|
+
'Preference operation signal must be an AbortSignal.',
|
|
89
|
+
TypeError
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
return Object.freeze({signal});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function setDataProperty(target,key,value){
|
|
96
|
+
Object.defineProperty(
|
|
97
|
+
target,
|
|
98
|
+
key,
|
|
99
|
+
{configurable:true,enumerable:true,value,writable:true}
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function frozenPreferenceValues(values){
|
|
104
|
+
const copy={};
|
|
105
|
+
for(const [key,value] of Object.entries(values)) setDataProperty(copy,key,value);
|
|
106
|
+
return Object.freeze(copy);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function validateAdapter(adapter){
|
|
110
|
+
if(!adapter
|
|
111
|
+
||typeof adapter.get!=='function'
|
|
112
|
+
||typeof adapter.set!=='function'
|
|
113
|
+
||typeof adapter.delete!=='function'){
|
|
114
|
+
throw preferenceStoreError(
|
|
115
|
+
PREFERENCE_STORE_ERROR_CODES.adapterInvalid,
|
|
116
|
+
'preference-storage-adapter-invalid',
|
|
117
|
+
'The preference storage adapter must provide get, set, and delete methods.',
|
|
118
|
+
TypeError
|
|
119
|
+
);
|
|
120
|
+
}
|
|
121
|
+
return adapter;
|
|
122
|
+
}
|
|
123
|
+
|
|
4
124
|
function localAdapter(prefix){
|
|
5
125
|
const storage=globalThis.localStorage;
|
|
6
126
|
const scopedPrefix=resolveApplicationLocalStorageKey(prefix);
|
|
@@ -9,8 +129,14 @@ function localAdapter(prefix){
|
|
|
9
129
|
const raw=storage?.getItem(`${scopedPrefix}:${key}`);
|
|
10
130
|
return {found:raw!==null&&raw!==undefined,value:raw==null?null:JSON.parse(raw)};
|
|
11
131
|
},
|
|
12
|
-
async set(key,value){
|
|
13
|
-
|
|
132
|
+
async set(key,value){
|
|
133
|
+
storage?.setItem(`${scopedPrefix}:${key}`,JSON.stringify(value));
|
|
134
|
+
return {key,value};
|
|
135
|
+
},
|
|
136
|
+
async delete(key){
|
|
137
|
+
storage?.removeItem(`${scopedPrefix}:${key}`);
|
|
138
|
+
return {key,deleted:true};
|
|
139
|
+
}
|
|
14
140
|
};
|
|
15
141
|
}
|
|
16
142
|
|
|
@@ -40,69 +166,277 @@ function preferenceAdapter(){
|
|
|
40
166
|
}
|
|
41
167
|
}
|
|
42
168
|
return {
|
|
43
|
-
get
|
|
44
|
-
|
|
45
|
-
|
|
169
|
+
async get(key){
|
|
170
|
+
return call('get',[key]);
|
|
171
|
+
},
|
|
172
|
+
async set(key,value){
|
|
173
|
+
return call('set',[key,value]);
|
|
174
|
+
},
|
|
175
|
+
async delete(key){
|
|
176
|
+
return call('delete',[key]);
|
|
177
|
+
}
|
|
46
178
|
};
|
|
47
179
|
}
|
|
48
180
|
|
|
49
181
|
export default class PreferenceStore extends EventTarget{
|
|
182
|
+
#disposed=false;
|
|
183
|
+
#events;
|
|
184
|
+
#operationSequence=0;
|
|
185
|
+
#operationTail=Promise.resolve();
|
|
186
|
+
#pendingOperations=new Set();
|
|
187
|
+
|
|
50
188
|
constructor({namespace='arcane',schema=[],adapter=null}={}){
|
|
51
189
|
super();
|
|
52
190
|
this.namespace=String(namespace||'arcane');
|
|
53
191
|
this.schema=preferenceSchema(schema);
|
|
54
|
-
this.adapter=adapter||preferenceAdapter();
|
|
192
|
+
this.adapter=validateAdapter(adapter||preferenceAdapter());
|
|
55
193
|
this.values=this.defaults();
|
|
194
|
+
this.#events=createArcaneEventSource(
|
|
195
|
+
this,
|
|
196
|
+
{
|
|
197
|
+
source:'preference-store',
|
|
198
|
+
eventTypes:Object.freeze(Object.values(PREFERENCE_STORE_EVENT_TYPES))
|
|
199
|
+
}
|
|
200
|
+
);
|
|
56
201
|
}
|
|
57
202
|
|
|
203
|
+
addEventListener(type,listener,options){return this.#events.addEventListener(type,listener,options);}
|
|
204
|
+
removeEventListener(type,listener,options){return this.#events.removeEventListener(type,listener,options);}
|
|
205
|
+
on(type,listener,options){return this.#events.on(type,listener,options);}
|
|
206
|
+
dispatchEvent(value){return this.#events.dispatchEvent(value);}
|
|
207
|
+
|
|
58
208
|
defaults(){
|
|
59
|
-
|
|
209
|
+
const values={};
|
|
210
|
+
for(const definition of this.schema){
|
|
211
|
+
setDataProperty(values,definition.key,definition.defaultValue);
|
|
212
|
+
}
|
|
213
|
+
return frozenPreferenceValues(values);
|
|
60
214
|
}
|
|
61
215
|
|
|
62
|
-
storageKey(key){
|
|
216
|
+
storageKey(key){return `${this.namespace}.${key}`;}
|
|
63
217
|
|
|
64
218
|
definition(key){
|
|
65
|
-
const definition=this.schema.find(item
|
|
66
|
-
|
|
219
|
+
const definition=this.schema.find(function findPreferenceDefinition(item){
|
|
220
|
+
return item.key===key;
|
|
221
|
+
});
|
|
222
|
+
if(!definition){
|
|
223
|
+
throw preferenceStoreError(
|
|
224
|
+
PREFERENCE_STORE_ERROR_CODES.keyUnknown,
|
|
225
|
+
'preference-key-unknown',
|
|
226
|
+
`Unknown preference: ${key}`,
|
|
227
|
+
RangeError
|
|
228
|
+
);
|
|
229
|
+
}
|
|
67
230
|
return definition;
|
|
68
231
|
}
|
|
69
232
|
|
|
70
|
-
async load(){
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
233
|
+
async load(options={}){
|
|
234
|
+
const operation=normalizeOperationOptions(options);
|
|
235
|
+
const operationId=this.#nextOperationId('load');
|
|
236
|
+
const store=this;
|
|
237
|
+
return this.#enqueueOperation(
|
|
238
|
+
async function loadPreferences(){
|
|
239
|
+
const values={};
|
|
240
|
+
for(const definition of store.schema){
|
|
241
|
+
const result=await store.adapter.get(
|
|
242
|
+
store.storageKey(definition.key),
|
|
243
|
+
Object.freeze({operationId,signal:operation.signal})
|
|
244
|
+
);
|
|
245
|
+
store.#assertOperationActive(operation.signal);
|
|
246
|
+
setDataProperty(
|
|
247
|
+
values,
|
|
248
|
+
definition.key,
|
|
249
|
+
result?.found
|
|
250
|
+
?definition.value(result.value)
|
|
251
|
+
:definition.defaultValue
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
store.values=frozenPreferenceValues(values);
|
|
255
|
+
store.#publish(PREFERENCE_STORE_EVENT_TYPES.load,{},operationId);
|
|
256
|
+
return frozenPreferenceValues(store.values);
|
|
257
|
+
},
|
|
258
|
+
operation.signal
|
|
259
|
+
);
|
|
79
260
|
}
|
|
80
261
|
|
|
81
|
-
async set(key,value){
|
|
262
|
+
async set(key,value,options={}){
|
|
263
|
+
const operation=normalizeOperationOptions(options);
|
|
82
264
|
const definition=this.definition(key);
|
|
83
265
|
const normalized=definition.value(value);
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
this
|
|
87
|
-
|
|
266
|
+
const operationId=this.#nextOperationId('set');
|
|
267
|
+
const store=this;
|
|
268
|
+
return this.#enqueueOperation(
|
|
269
|
+
async function setPreference(){
|
|
270
|
+
await store.adapter.set(
|
|
271
|
+
store.storageKey(key),
|
|
272
|
+
normalized,
|
|
273
|
+
Object.freeze({operationId,signal:operation.signal})
|
|
274
|
+
);
|
|
275
|
+
store.#assertOperationActive(operation.signal);
|
|
276
|
+
store.values=frozenPreferenceValues({...store.values,[key]:normalized});
|
|
277
|
+
store.#publish(
|
|
278
|
+
PREFERENCE_STORE_EVENT_TYPES.change,
|
|
279
|
+
{key,value:normalized},
|
|
280
|
+
operationId
|
|
281
|
+
);
|
|
282
|
+
return normalized;
|
|
283
|
+
},
|
|
284
|
+
operation.signal
|
|
285
|
+
);
|
|
88
286
|
}
|
|
89
287
|
|
|
90
|
-
async setAll(values={}){
|
|
288
|
+
async setAll(values={},options={}){
|
|
289
|
+
const operation=normalizeOperationOptions(options);
|
|
290
|
+
if(!isPlainRecord(values)){
|
|
291
|
+
throw preferenceStoreError(
|
|
292
|
+
PREFERENCE_STORE_ERROR_CODES.valuesInvalid,
|
|
293
|
+
'preference-values-invalid',
|
|
294
|
+
'Preference values must be a plain object.',
|
|
295
|
+
TypeError
|
|
296
|
+
);
|
|
297
|
+
}
|
|
91
298
|
for(const definition of this.schema){
|
|
92
|
-
if(Object.prototype.hasOwnProperty.call(values,definition.key))
|
|
299
|
+
if(Object.prototype.hasOwnProperty.call(values,definition.key)){
|
|
300
|
+
await this.set(definition.key,values[definition.key],operation);
|
|
301
|
+
}
|
|
93
302
|
}
|
|
94
|
-
|
|
303
|
+
this.#assertOperationActive(operation.signal);
|
|
304
|
+
return frozenPreferenceValues(this.values);
|
|
95
305
|
}
|
|
96
306
|
|
|
97
|
-
async reset(){
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
this
|
|
101
|
-
return
|
|
307
|
+
async reset(options={}){
|
|
308
|
+
const operation=normalizeOperationOptions(options);
|
|
309
|
+
const operationId=this.#nextOperationId('reset');
|
|
310
|
+
const store=this;
|
|
311
|
+
return this.#enqueueOperation(
|
|
312
|
+
async function resetPreferences(){
|
|
313
|
+
for(const definition of store.schema){
|
|
314
|
+
await store.adapter.delete(
|
|
315
|
+
store.storageKey(definition.key),
|
|
316
|
+
Object.freeze({operationId,signal:operation.signal})
|
|
317
|
+
);
|
|
318
|
+
store.#assertOperationActive(operation.signal);
|
|
319
|
+
}
|
|
320
|
+
store.values=store.defaults();
|
|
321
|
+
store.#publish(PREFERENCE_STORE_EVENT_TYPES.reset,{},operationId);
|
|
322
|
+
return frozenPreferenceValues(store.values);
|
|
323
|
+
},
|
|
324
|
+
operation.signal
|
|
325
|
+
);
|
|
102
326
|
}
|
|
103
327
|
|
|
104
328
|
emit(type,detail={}){
|
|
105
|
-
this
|
|
329
|
+
this.#assertOpen();
|
|
330
|
+
if(!isPlainRecord(detail)){
|
|
331
|
+
throw preferenceStoreError(
|
|
332
|
+
PREFERENCE_STORE_ERROR_CODES.eventDetailInvalid,
|
|
333
|
+
'preference-event-detail-invalid',
|
|
334
|
+
'Preference event detail must be a plain object.',
|
|
335
|
+
TypeError
|
|
336
|
+
);
|
|
337
|
+
}
|
|
338
|
+
const eventType=PREFERENCE_STORE_EVENT_TYPES[type]??type;
|
|
339
|
+
this.#publish(eventType,detail,this.#nextOperationId('emit'));
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
dispose(){
|
|
343
|
+
if(this.#disposed) return false;
|
|
344
|
+
this.#disposed=true;
|
|
345
|
+
const error=disposedError();
|
|
346
|
+
for(const cancel of [...this.#pendingOperations]) cancel(error);
|
|
347
|
+
return this.#events.dispose();
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
destroy(){return this.dispose();}
|
|
351
|
+
|
|
352
|
+
#assertOpen(){
|
|
353
|
+
if(this.#disposed) throw disposedError();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
#assertOperationActive(signal){
|
|
357
|
+
this.#assertOpen();
|
|
358
|
+
if(signal?.aborted) throw operationAbortedError(signal.reason);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
#nextOperationId(action){
|
|
362
|
+
this.#assertOpen();
|
|
363
|
+
this.#operationSequence+=1;
|
|
364
|
+
return `${this.#events.instanceId}:${action}:${this.#operationSequence.toString(36)}`;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
#publish(type,detail,operationId){
|
|
368
|
+
const values=frozenPreferenceValues(this.values);
|
|
369
|
+
const compatibilityDetail=Object.freeze({
|
|
370
|
+
...detail,
|
|
371
|
+
namespace:this.namespace,
|
|
372
|
+
values
|
|
373
|
+
});
|
|
374
|
+
const publicDetail=Object.freeze({
|
|
375
|
+
namespace:this.namespace,
|
|
376
|
+
values,
|
|
377
|
+
...(typeof detail.key==='string'
|
|
378
|
+
?{preferenceId:detail.key,value:detail.value}
|
|
379
|
+
:{})
|
|
380
|
+
});
|
|
381
|
+
this.#events.dispatch(
|
|
382
|
+
type,
|
|
383
|
+
compatibilityDetail,
|
|
384
|
+
{operationId,publicDetail}
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
#enqueueOperation(operation,signal){
|
|
389
|
+
this.#assertOperationActive(signal);
|
|
390
|
+
const store=this;
|
|
391
|
+
let abortHandler=null;
|
|
392
|
+
let rejectResult;
|
|
393
|
+
let resolveResult;
|
|
394
|
+
let settled=false;
|
|
395
|
+
const result=new Promise(function capturePreferenceOperationSettlement(resolve,reject){
|
|
396
|
+
resolveResult=resolve;
|
|
397
|
+
rejectResult=reject;
|
|
398
|
+
});
|
|
399
|
+
function cleanupPreferenceOperation(){
|
|
400
|
+
signal?.removeEventListener('abort',abortHandler);
|
|
401
|
+
store.#pendingOperations.delete(cancelPreferenceOperation);
|
|
402
|
+
}
|
|
403
|
+
function settlePreferenceOperation(handler,value){
|
|
404
|
+
if(settled) return false;
|
|
405
|
+
settled=true;
|
|
406
|
+
cleanupPreferenceOperation();
|
|
407
|
+
handler(value);
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
function cancelPreferenceOperation(error){
|
|
411
|
+
return settlePreferenceOperation(rejectResult,error);
|
|
412
|
+
}
|
|
413
|
+
abortHandler=function abortPreferenceOperation(){
|
|
414
|
+
cancelPreferenceOperation(operationAbortedError(signal.reason));
|
|
415
|
+
};
|
|
416
|
+
store.#pendingOperations.add(cancelPreferenceOperation);
|
|
417
|
+
signal?.addEventListener('abort',abortHandler,{once:true});
|
|
418
|
+
if(signal?.aborted) abortHandler();
|
|
419
|
+
|
|
420
|
+
async function runPreferenceOperation(){
|
|
421
|
+
if(settled) return;
|
|
422
|
+
try{
|
|
423
|
+
store.#assertOperationActive(signal);
|
|
424
|
+
const value=await operation();
|
|
425
|
+
store.#assertOperationActive(signal);
|
|
426
|
+
settlePreferenceOperation(resolveResult,value);
|
|
427
|
+
}catch(error){
|
|
428
|
+
settlePreferenceOperation(rejectResult,error);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
const queued=this.#operationTail.then(
|
|
432
|
+
runPreferenceOperation,
|
|
433
|
+
runPreferenceOperation
|
|
434
|
+
);
|
|
435
|
+
this.#operationTail=queued.then(
|
|
436
|
+
function completePreferenceOperationLane(){},
|
|
437
|
+
function recoverPreferenceOperationLane(){}
|
|
438
|
+
);
|
|
439
|
+
return result;
|
|
106
440
|
}
|
|
107
441
|
}
|
|
108
442
|
|