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,8 +1,111 @@
|
|
|
1
|
+
import {createArcaneEventSource} from 'arcane-os/event-manager';
|
|
1
2
|
import {resolveApplicationLocalStorageKey} from './AppDataScope.js';
|
|
2
3
|
|
|
4
|
+
export const RECORD_REVIEW_STORE_ERROR_CODES=Object.freeze({
|
|
5
|
+
adapterInvalid:'ARCANE_RECORD_REVIEW_STORE_ADAPTER_INVALID',
|
|
6
|
+
disposed:'ARCANE_RECORD_REVIEW_STORE_DISPOSED',
|
|
7
|
+
operationAborted:'ARCANE_RECORD_REVIEW_STORE_OPERATION_ABORTED',
|
|
8
|
+
operationOptionsInvalid:'ARCANE_RECORD_REVIEW_STORE_OPERATION_OPTIONS_INVALID',
|
|
9
|
+
recordIdInvalid:'ARCANE_RECORD_REVIEW_ID_INVALID',
|
|
10
|
+
storedRecordsInvalid:'ARCANE_RECORD_REVIEW_STORED_RECORDS_INVALID'
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const RECORD_REVIEW_STORE_EVENT_TYPES=Object.freeze({
|
|
14
|
+
change:'record-review-change'
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
function recordReviewStoreError(code,reason,message,ErrorType=Error,cause){
|
|
18
|
+
const error=new ErrorType(message);
|
|
19
|
+
error.code=code;
|
|
20
|
+
error.reason=reason;
|
|
21
|
+
if(cause!==undefined) error.cause=cause;
|
|
22
|
+
return error;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function disposedError(){
|
|
26
|
+
return recordReviewStoreError(
|
|
27
|
+
RECORD_REVIEW_STORE_ERROR_CODES.disposed,
|
|
28
|
+
'record-review-store-disposed',
|
|
29
|
+
'The record review store has been disposed.'
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function operationAbortedError(reason){
|
|
34
|
+
const error=recordReviewStoreError(
|
|
35
|
+
RECORD_REVIEW_STORE_ERROR_CODES.operationAborted,
|
|
36
|
+
'record-review-operation-aborted',
|
|
37
|
+
'The record review operation was aborted.',
|
|
38
|
+
Error,
|
|
39
|
+
reason
|
|
40
|
+
);
|
|
41
|
+
error.name='AbortError';
|
|
42
|
+
return error;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isPlainRecord(value){
|
|
46
|
+
if(!value||typeof value!=='object'||Array.isArray(value)) return false;
|
|
47
|
+
const prototype=Object.getPrototypeOf(value);
|
|
48
|
+
return prototype===Object.prototype||prototype===null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isAbortSignal(value){
|
|
52
|
+
return Boolean(value)
|
|
53
|
+
&&typeof value==='object'
|
|
54
|
+
&&typeof value.aborted==='boolean'
|
|
55
|
+
&&typeof value.addEventListener==='function'
|
|
56
|
+
&&typeof value.removeEventListener==='function';
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function normalizeOperationOptions(value={}){
|
|
60
|
+
if(!isPlainRecord(value)){
|
|
61
|
+
throw recordReviewStoreError(
|
|
62
|
+
RECORD_REVIEW_STORE_ERROR_CODES.operationOptionsInvalid,
|
|
63
|
+
'record-review-operation-options-invalid',
|
|
64
|
+
'Record review operation options must be a plain object.',
|
|
65
|
+
TypeError
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
const keys=Reflect.ownKeys(value);
|
|
69
|
+
if(keys.some(function hasUnsupportedRecordReviewOperationOption(key){
|
|
70
|
+
return key!=='signal';
|
|
71
|
+
})){
|
|
72
|
+
throw recordReviewStoreError(
|
|
73
|
+
RECORD_REVIEW_STORE_ERROR_CODES.operationOptionsInvalid,
|
|
74
|
+
'record-review-operation-options-invalid',
|
|
75
|
+
'Record review operation options support only signal.',
|
|
76
|
+
TypeError
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
const signal=value.signal??null;
|
|
80
|
+
if(signal!==null&&!isAbortSignal(signal)){
|
|
81
|
+
throw recordReviewStoreError(
|
|
82
|
+
RECORD_REVIEW_STORE_ERROR_CODES.operationOptionsInvalid,
|
|
83
|
+
'record-review-operation-signal-invalid',
|
|
84
|
+
'Record review operation signal must be an AbortSignal.',
|
|
85
|
+
TypeError
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return Object.freeze({signal});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function setDataProperty(target,key,value){
|
|
92
|
+
Object.defineProperty(
|
|
93
|
+
target,
|
|
94
|
+
key,
|
|
95
|
+
{configurable:true,enumerable:true,value,writable:true}
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
3
99
|
function normalizeRecordId(value=''){
|
|
4
100
|
const id=String(value).trim();
|
|
5
|
-
if(!id||id.length>160||/[\x00-\x1f]/.test(id))
|
|
101
|
+
if(!id||id.length>160||/[\x00-\x1f]/.test(id)){
|
|
102
|
+
throw recordReviewStoreError(
|
|
103
|
+
RECORD_REVIEW_STORE_ERROR_CODES.recordIdInvalid,
|
|
104
|
+
'record-review-id-invalid',
|
|
105
|
+
'A valid record id is required.',
|
|
106
|
+
TypeError
|
|
107
|
+
);
|
|
108
|
+
}
|
|
6
109
|
return id;
|
|
7
110
|
}
|
|
8
111
|
|
|
@@ -13,18 +116,73 @@ function normalizeReview(value={}){
|
|
|
13
116
|
for(const [key,value] of Object.entries(source.attributes).slice(0,40)){
|
|
14
117
|
const normalizedKey=String(key).replace(/[^a-z0-9._-]/gi,'-').slice(0,80);
|
|
15
118
|
if(!normalizedKey) continue;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
119
|
+
setDataProperty(
|
|
120
|
+
attributes,
|
|
121
|
+
normalizedKey,
|
|
122
|
+
Array.isArray(value)
|
|
123
|
+
?Object.freeze(value.slice(0,100).map(function normalizeReviewAttributeItem(item){
|
|
124
|
+
return String(item).slice(0,500);
|
|
125
|
+
}))
|
|
126
|
+
:String(value??'').slice(0,10000)
|
|
127
|
+
);
|
|
19
128
|
}
|
|
20
129
|
}
|
|
21
|
-
return {
|
|
130
|
+
return Object.freeze({
|
|
22
131
|
status:String(source.status||'not-reviewed').trim().slice(0,80)||'not-reviewed',
|
|
23
132
|
classification:String(source.classification||'unassigned').trim().slice(0,80)||'unassigned',
|
|
24
|
-
attributes,
|
|
133
|
+
attributes:Object.freeze(attributes),
|
|
25
134
|
notes:String(source.notes||'').slice(0,10000),
|
|
26
135
|
updatedAt:source.updatedAt?String(source.updatedAt):null
|
|
27
|
-
};
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function frozenRecordMap(records){
|
|
140
|
+
const copy={};
|
|
141
|
+
for(const [id,review] of Object.entries(records)){
|
|
142
|
+
setDataProperty(copy,id,normalizeReview(review));
|
|
143
|
+
}
|
|
144
|
+
return Object.freeze(copy);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function normalizedStoredRecords(value){
|
|
148
|
+
if(!isPlainRecord(value)) return Object.freeze({});
|
|
149
|
+
const records={};
|
|
150
|
+
for(const [recordId,review] of Object.entries(value)){
|
|
151
|
+
let id;
|
|
152
|
+
try{
|
|
153
|
+
id=normalizeRecordId(recordId);
|
|
154
|
+
}catch(error){
|
|
155
|
+
throw recordReviewStoreError(
|
|
156
|
+
RECORD_REVIEW_STORE_ERROR_CODES.storedRecordsInvalid,
|
|
157
|
+
'record-review-stored-id-invalid',
|
|
158
|
+
'Stored record reviews contain an invalid record id.',
|
|
159
|
+
TypeError,
|
|
160
|
+
error
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
if(Object.prototype.hasOwnProperty.call(records,id)){
|
|
164
|
+
throw recordReviewStoreError(
|
|
165
|
+
RECORD_REVIEW_STORE_ERROR_CODES.storedRecordsInvalid,
|
|
166
|
+
'record-review-stored-id-collision',
|
|
167
|
+
'Stored record reviews contain colliding record ids.',
|
|
168
|
+
TypeError
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
setDataProperty(records,id,normalizeReview(review));
|
|
172
|
+
}
|
|
173
|
+
return Object.freeze(records);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function validateAdapter(adapter){
|
|
177
|
+
if(!adapter||typeof adapter.get!=='function'||typeof adapter.set!=='function'){
|
|
178
|
+
throw recordReviewStoreError(
|
|
179
|
+
RECORD_REVIEW_STORE_ERROR_CODES.adapterInvalid,
|
|
180
|
+
'record-review-storage-adapter-invalid',
|
|
181
|
+
'The record review storage adapter must provide get and set methods.',
|
|
182
|
+
TypeError
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
return adapter;
|
|
28
186
|
}
|
|
29
187
|
|
|
30
188
|
function localAdapter(namespace){
|
|
@@ -33,7 +191,11 @@ function localAdapter(namespace){
|
|
|
33
191
|
async get(){
|
|
34
192
|
const raw=globalThis.localStorage?.getItem(key);
|
|
35
193
|
if(!raw) return {};
|
|
36
|
-
try{
|
|
194
|
+
try{
|
|
195
|
+
return JSON.parse(raw);
|
|
196
|
+
}catch{
|
|
197
|
+
return {};
|
|
198
|
+
}
|
|
37
199
|
},
|
|
38
200
|
async set(value){
|
|
39
201
|
globalThis.localStorage?.setItem(key,JSON.stringify(value));
|
|
@@ -51,24 +213,56 @@ function nativeAdapter(namespace){
|
|
|
51
213
|
const result=await storage.get(key);
|
|
52
214
|
return result?.value??result??{};
|
|
53
215
|
},
|
|
54
|
-
async set(value){
|
|
216
|
+
async set(value){
|
|
217
|
+
await storage.set(key,value);
|
|
218
|
+
return value;
|
|
219
|
+
}
|
|
55
220
|
};
|
|
56
221
|
}
|
|
57
222
|
|
|
58
223
|
class RecordReviewStore extends EventTarget{
|
|
224
|
+
#disposed=false;
|
|
225
|
+
#events;
|
|
226
|
+
#operationSequence=0;
|
|
227
|
+
#operationTail=Promise.resolve();
|
|
228
|
+
#pendingOperations=new Set();
|
|
229
|
+
|
|
59
230
|
constructor({namespace='records',adapter=null}={}){
|
|
60
231
|
super();
|
|
61
232
|
this.namespace=String(namespace||'records').replace(/[^a-z0-9._-]/gi,'-').slice(0,120);
|
|
62
|
-
this.adapter=adapter||nativeAdapter(this.namespace)||localAdapter(this.namespace);
|
|
63
|
-
this.records={};
|
|
233
|
+
this.adapter=validateAdapter(adapter||nativeAdapter(this.namespace)||localAdapter(this.namespace));
|
|
234
|
+
this.records=Object.freeze({});
|
|
64
235
|
this.loaded=false;
|
|
236
|
+
this.#events=createArcaneEventSource(
|
|
237
|
+
this,
|
|
238
|
+
{
|
|
239
|
+
source:'record-review-store',
|
|
240
|
+
eventTypes:Object.freeze(Object.values(RECORD_REVIEW_STORE_EVENT_TYPES))
|
|
241
|
+
}
|
|
242
|
+
);
|
|
65
243
|
}
|
|
66
244
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
245
|
+
addEventListener(type,listener,options){return this.#events.addEventListener(type,listener,options);}
|
|
246
|
+
removeEventListener(type,listener,options){return this.#events.removeEventListener(type,listener,options);}
|
|
247
|
+
on(type,listener,options){return this.#events.on(type,listener,options);}
|
|
248
|
+
dispatchEvent(value){return this.#events.dispatchEvent(value);}
|
|
249
|
+
|
|
250
|
+
async load(options={}){
|
|
251
|
+
const operation=normalizeOperationOptions(options);
|
|
252
|
+
const operationId=this.#nextOperationId('load');
|
|
253
|
+
const store=this;
|
|
254
|
+
return this.#enqueueOperation(
|
|
255
|
+
async function loadRecordReviews(){
|
|
256
|
+
const stored=await store.adapter.get(
|
|
257
|
+
Object.freeze({operationId,signal:operation.signal})
|
|
258
|
+
);
|
|
259
|
+
store.#assertOperationActive(operation.signal);
|
|
260
|
+
store.records=normalizedStoredRecords(stored);
|
|
261
|
+
store.loaded=true;
|
|
262
|
+
return store.snapshot();
|
|
263
|
+
},
|
|
264
|
+
operation.signal
|
|
265
|
+
);
|
|
72
266
|
}
|
|
73
267
|
|
|
74
268
|
get(recordId){
|
|
@@ -76,17 +270,122 @@ class RecordReviewStore extends EventTarget{
|
|
|
76
270
|
return normalizeReview(this.records[id]);
|
|
77
271
|
}
|
|
78
272
|
|
|
79
|
-
async set(recordId,value={}){
|
|
273
|
+
async set(recordId,value={},options={}){
|
|
274
|
+
const operation=normalizeOperationOptions(options);
|
|
80
275
|
const id=normalizeRecordId(recordId);
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
276
|
+
const operationId=this.#nextOperationId('set');
|
|
277
|
+
const store=this;
|
|
278
|
+
return this.#enqueueOperation(
|
|
279
|
+
async function setRecordReview(){
|
|
280
|
+
const review=normalizeReview({
|
|
281
|
+
...store.get(id),
|
|
282
|
+
...value,
|
|
283
|
+
updatedAt:new Date().toISOString()
|
|
284
|
+
});
|
|
285
|
+
const records=frozenRecordMap({...store.records,[id]:review});
|
|
286
|
+
await store.adapter.set(
|
|
287
|
+
records,
|
|
288
|
+
Object.freeze({operationId,signal:operation.signal})
|
|
289
|
+
);
|
|
290
|
+
store.#assertOperationActive(operation.signal);
|
|
291
|
+
store.records=records;
|
|
292
|
+
const detail=Object.freeze({
|
|
293
|
+
namespace:store.namespace,
|
|
294
|
+
recordId:id,
|
|
295
|
+
review:normalizeReview(review)
|
|
296
|
+
});
|
|
297
|
+
store.#events.dispatch(
|
|
298
|
+
RECORD_REVIEW_STORE_EVENT_TYPES.change,
|
|
299
|
+
detail,
|
|
300
|
+
{operationId,publicDetail:detail}
|
|
301
|
+
);
|
|
302
|
+
return normalizeReview(review);
|
|
303
|
+
},
|
|
304
|
+
operation.signal
|
|
305
|
+
);
|
|
86
306
|
}
|
|
87
307
|
|
|
88
308
|
snapshot(){
|
|
89
|
-
return
|
|
309
|
+
return frozenRecordMap(this.records);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
dispose(){
|
|
313
|
+
if(this.#disposed) return false;
|
|
314
|
+
this.#disposed=true;
|
|
315
|
+
const error=disposedError();
|
|
316
|
+
for(const cancel of [...this.#pendingOperations]) cancel(error);
|
|
317
|
+
return this.#events.dispose();
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
destroy(){return this.dispose();}
|
|
321
|
+
|
|
322
|
+
#assertOpen(){
|
|
323
|
+
if(this.#disposed) throw disposedError();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
#assertOperationActive(signal){
|
|
327
|
+
this.#assertOpen();
|
|
328
|
+
if(signal?.aborted) throw operationAbortedError(signal.reason);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
#nextOperationId(action){
|
|
332
|
+
this.#assertOpen();
|
|
333
|
+
this.#operationSequence+=1;
|
|
334
|
+
return `${this.#events.instanceId}:${action}:${this.#operationSequence.toString(36)}`;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
#enqueueOperation(operation,signal){
|
|
338
|
+
this.#assertOperationActive(signal);
|
|
339
|
+
const store=this;
|
|
340
|
+
let abortHandler=null;
|
|
341
|
+
let rejectResult;
|
|
342
|
+
let resolveResult;
|
|
343
|
+
let settled=false;
|
|
344
|
+
const result=new Promise(function captureRecordReviewOperationSettlement(resolve,reject){
|
|
345
|
+
resolveResult=resolve;
|
|
346
|
+
rejectResult=reject;
|
|
347
|
+
});
|
|
348
|
+
function cleanupRecordReviewOperation(){
|
|
349
|
+
signal?.removeEventListener('abort',abortHandler);
|
|
350
|
+
store.#pendingOperations.delete(cancelRecordReviewOperation);
|
|
351
|
+
}
|
|
352
|
+
function settleRecordReviewOperation(handler,value){
|
|
353
|
+
if(settled) return false;
|
|
354
|
+
settled=true;
|
|
355
|
+
cleanupRecordReviewOperation();
|
|
356
|
+
handler(value);
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
function cancelRecordReviewOperation(error){
|
|
360
|
+
return settleRecordReviewOperation(rejectResult,error);
|
|
361
|
+
}
|
|
362
|
+
abortHandler=function abortRecordReviewOperation(){
|
|
363
|
+
cancelRecordReviewOperation(operationAbortedError(signal.reason));
|
|
364
|
+
};
|
|
365
|
+
store.#pendingOperations.add(cancelRecordReviewOperation);
|
|
366
|
+
signal?.addEventListener('abort',abortHandler,{once:true});
|
|
367
|
+
if(signal?.aborted) abortHandler();
|
|
368
|
+
|
|
369
|
+
async function runRecordReviewOperation(){
|
|
370
|
+
if(settled) return;
|
|
371
|
+
try{
|
|
372
|
+
store.#assertOperationActive(signal);
|
|
373
|
+
const value=await operation();
|
|
374
|
+
store.#assertOperationActive(signal);
|
|
375
|
+
settleRecordReviewOperation(resolveResult,value);
|
|
376
|
+
}catch(error){
|
|
377
|
+
settleRecordReviewOperation(rejectResult,error);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const queued=this.#operationTail.then(
|
|
381
|
+
runRecordReviewOperation,
|
|
382
|
+
runRecordReviewOperation
|
|
383
|
+
);
|
|
384
|
+
this.#operationTail=queued.then(
|
|
385
|
+
function completeRecordReviewOperationLane(){},
|
|
386
|
+
function recoverRecordReviewOperationLane(){}
|
|
387
|
+
);
|
|
388
|
+
return result;
|
|
90
389
|
}
|
|
91
390
|
}
|
|
92
391
|
|