@absolutejs/absolute 0.20.0-beta.15 → 0.20.0-beta.17
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/dist/angular/components/core/streamingSlotRegistrar.js +1 -1
- package/dist/angular/components/core/streamingSlotRegistry.js +2 -2
- package/dist/build.js +42 -3
- package/dist/build.js.map +4 -4
- package/dist/cli/index.js +49 -5
- package/dist/dev/client/hmrClient.ts +9 -3
- package/dist/dev/client/syncDevtools.ts +237 -0
- package/dist/index.js +42 -3
- package/dist/index.js.map +4 -4
- package/dist/mobile/browser.js +110 -1
- package/dist/mobile/browser.js.map +6 -4
- package/dist/mobile/index.js +153 -5
- package/dist/mobile/index.js.map +8 -6
- package/dist/mobile/shellSync.js +42 -0
- package/dist/src/mobile/browser.d.ts +1 -0
- package/dist/src/mobile/index.d.ts +1 -0
- package/dist/src/mobile/syncRemediation.d.ts +10 -0
- package/package.json +11 -11
package/dist/cli/index.js
CHANGED
|
@@ -6304,7 +6304,7 @@ var init_materializedBundle = __esm(() => {
|
|
|
6304
6304
|
});
|
|
6305
6305
|
|
|
6306
6306
|
// node_modules/@absolutejs/sync/dist/client/index.js
|
|
6307
|
-
var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations")), registry, SyncLocalDataPolicyError, SyncLocalStoreSchemaError, positiveVersion = (value, label) => {
|
|
6307
|
+
var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations")) && Array.isArray(Reflect.get(value, "clients")), registry, SyncLocalDataPolicyError, SyncLocalStoreSchemaError, positiveVersion = (value, label) => {
|
|
6308
6308
|
if (!Number.isSafeInteger(value) || value < 1)
|
|
6309
6309
|
throw new SyncLocalStoreSchemaError("INVALID_PLAN", `${label} must be a positive safe integer`);
|
|
6310
6310
|
return value;
|
|
@@ -6325,6 +6325,14 @@ var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" &
|
|
|
6325
6325
|
}
|
|
6326
6326
|
for (const [index, rule] of (policy.mutations ?? []).entries()) {
|
|
6327
6327
|
validatePolicyMatch(rule.match, `${label}.mutations[${index}]`);
|
|
6328
|
+
if (rule.conflict !== undefined && rule.conflict.strategy !== "client-wins" && rule.conflict.strategy !== "manual" && rule.conflict.strategy !== "server-wins")
|
|
6329
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.strategy is invalid.`);
|
|
6330
|
+
if (rule.conflict?.maxAttempts !== undefined && (!Number.isSafeInteger(rule.conflict.maxAttempts) || rule.conflict.maxAttempts < 1))
|
|
6331
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts must be a positive safe integer.`);
|
|
6332
|
+
if (rule.conflict?.maxAttempts !== undefined && rule.conflict.strategy !== "client-wins")
|
|
6333
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts is only valid for client-wins.`);
|
|
6334
|
+
if (rule.persistence === "memory-only" && rule.protection === "required")
|
|
6335
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] cannot require at-rest protection when it is memory-only.`);
|
|
6328
6336
|
if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
|
|
6329
6337
|
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] declares ${rule.sensitivity} arguments without required protection.`);
|
|
6330
6338
|
}
|
|
@@ -6388,7 +6396,11 @@ var init_client2 = __esm(() => {
|
|
|
6388
6396
|
const existing = host[RUNTIME_TRANSPORT];
|
|
6389
6397
|
if (isRegistry(existing))
|
|
6390
6398
|
return existing;
|
|
6391
|
-
|
|
6399
|
+
if (typeof existing === "object" && existing !== null && Array.isArray(Reflect.get(existing, "installations"))) {
|
|
6400
|
+
Reflect.set(existing, "clients", []);
|
|
6401
|
+
return existing;
|
|
6402
|
+
}
|
|
6403
|
+
const created = { clients: [], installations: [] };
|
|
6392
6404
|
Object.defineProperty(host, RUNTIME_TRANSPORT, {
|
|
6393
6405
|
configurable: false,
|
|
6394
6406
|
enumerable: false,
|
|
@@ -6576,7 +6588,9 @@ var object = (value) => typeof value === "object" && value !== null && !Array.is
|
|
|
6576
6588
|
const mutations = Array.isArray(mutationRules) ? mutationRules.map((entry, index) => {
|
|
6577
6589
|
const rule = requireObject(entry, id, `localData.mutations[${index}] must be an object.`);
|
|
6578
6590
|
const allowedRuleKeys = new Set([
|
|
6591
|
+
"conflict",
|
|
6579
6592
|
"match",
|
|
6593
|
+
"onProtectionUnavailable",
|
|
6580
6594
|
"persistence",
|
|
6581
6595
|
"protection",
|
|
6582
6596
|
"sensitivity"
|
|
@@ -6587,15 +6601,40 @@ var object = (value) => typeof value === "object" && value !== null && !Array.is
|
|
|
6587
6601
|
const protection = unknownField(rule, "protection");
|
|
6588
6602
|
const sensitivity = unknownField(rule, "sensitivity");
|
|
6589
6603
|
const persistence = unknownField(rule, "persistence");
|
|
6604
|
+
const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
|
|
6605
|
+
const declaredConflict = unknownField(rule, "conflict");
|
|
6606
|
+
let conflict;
|
|
6607
|
+
if (declaredConflict !== undefined) {
|
|
6608
|
+
const conflictRecord = requireObject(declaredConflict, id, `localData.mutations[${index}].conflict must be an object.`);
|
|
6609
|
+
const unsupportedConflictKey = Object.keys(conflictRecord).find((key) => key !== "maxAttempts" && key !== "strategy");
|
|
6610
|
+
if (unsupportedConflictKey)
|
|
6611
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.${unsupportedConflictKey} is not supported.`);
|
|
6612
|
+
const strategy = unknownField(conflictRecord, "strategy");
|
|
6613
|
+
if (strategy !== "client-wins" && strategy !== "manual" && strategy !== "server-wins")
|
|
6614
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.strategy is invalid.`);
|
|
6615
|
+
const maxAttempts = unknownField(conflictRecord, "maxAttempts");
|
|
6616
|
+
if (maxAttempts !== undefined && strategy !== "client-wins")
|
|
6617
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.maxAttempts requires client-wins.`);
|
|
6618
|
+
conflict = {
|
|
6619
|
+
strategy,
|
|
6620
|
+
...maxAttempts === undefined ? {} : {
|
|
6621
|
+
maxAttempts: positiveVersion2(maxAttempts, id, `localData.mutations[${index}].conflict.maxAttempts`)
|
|
6622
|
+
}
|
|
6623
|
+
};
|
|
6624
|
+
}
|
|
6590
6625
|
if (protection !== undefined && protection !== "none" && protection !== "required")
|
|
6591
6626
|
throw metadataError(id, `localData.mutations[${index}].protection is invalid.`);
|
|
6592
6627
|
if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
|
|
6593
6628
|
throw metadataError(id, `localData.mutations[${index}].sensitivity is invalid.`);
|
|
6629
|
+
if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
|
|
6630
|
+
throw metadataError(id, `localData.mutations[${index}].onProtectionUnavailable is invalid.`);
|
|
6594
6631
|
if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
|
|
6595
6632
|
throw metadataError(id, `localData.mutations[${index}].persistence is invalid.`);
|
|
6596
6633
|
return {
|
|
6597
6634
|
match: nonEmpty(Reflect.get(rule, "match"), id, `localData.mutations[${index}].match`),
|
|
6635
|
+
...conflict ? { conflict } : {},
|
|
6598
6636
|
...sensitivity ? { sensitivity } : {},
|
|
6637
|
+
...onProtectionUnavailable ? { onProtectionUnavailable } : {},
|
|
6599
6638
|
...persistence ? {
|
|
6600
6639
|
persistence
|
|
6601
6640
|
} : {},
|
|
@@ -16970,9 +17009,14 @@ var HMR_ASSET_PATTERN, RELEASE_ASSET_EXTENSIONS, pathExists5 = async (path) => {
|
|
|
16970
17009
|
const collectionRules = schema.components.flatMap((component2) => component2.localData?.collections ?? []);
|
|
16971
17010
|
const mutationRules = schema.components.flatMap((component2) => component2.localData?.mutations ?? []);
|
|
16972
17011
|
const protectedCount = [...collectionRules, ...mutationRules].filter((rule) => rule.protection === "required").length;
|
|
16973
|
-
const memoryOnlyCount = collectionRules.filter((rule) => rule.persistence === "memory-only" || rule.onProtectionUnavailable === "memory-only").length;
|
|
17012
|
+
const memoryOnlyCount = [...collectionRules, ...mutationRules].filter((rule) => rule.persistence === "memory-only" || rule.onProtectionUnavailable === "memory-only").length;
|
|
17013
|
+
const conflictCounts = {
|
|
17014
|
+
clientWins: mutationRules.filter((rule) => rule.conflict?.strategy === "client-wins").length,
|
|
17015
|
+
manual: mutationRules.filter((rule) => rule.conflict?.strategy === "manual").length,
|
|
17016
|
+
serverWins: mutationRules.filter((rule) => rule.conflict?.strategy === "server-wins").length
|
|
17017
|
+
};
|
|
16974
17018
|
const quotas = schema.components.map((component2) => component2.localData?.maxBytesPerNamespace).filter((value) => value !== undefined);
|
|
16975
|
-
const policy = `${collectionRules.length} collection rule(s), ${mutationRules.length} mutation rule(s), ${protectedCount} encryption-required, ${memoryOnlyCount} memory-only fallback(s)${quotas.length > 0 ? `, ${Math.min(...quotas)}-byte effective quota` : ", no logical quota"}`;
|
|
17019
|
+
const policy = `${collectionRules.length} collection rule(s), ${mutationRules.length} mutation rule(s), ${protectedCount} encryption-required, ${memoryOnlyCount} memory-only fallback(s), conflicts ${conflictCounts.clientWins} client-wins/${conflictCounts.serverWins} server-wins/${conflictCounts.manual} manual${quotas.length > 0 ? `, ${Math.min(...quotas)}-byte effective quota` : ", no logical quota"}`;
|
|
16976
17020
|
return pass("sync.storage-schema", `Generated offline schema is compatible: ${versions}; ${policy}.`, manifestPath);
|
|
16977
17021
|
} catch (error) {
|
|
16978
17022
|
return fail5("sync.storage-schema", error instanceof Error ? error.message : "Generated offline schema metadata is invalid.", manifestPath, "Fix absolutejs.sync.localSchema metadata in the named app or package before releasing.");
|
|
@@ -18505,7 +18549,7 @@ var init_mobile = __esm(() => {
|
|
|
18505
18549
|
"@absolutejs/devices-capacitor@0.1.3"
|
|
18506
18550
|
];
|
|
18507
18551
|
CAPACITOR_SYNC_PACKAGE_SPECS = [
|
|
18508
|
-
"@absolutejs/sync-capacitor@0.
|
|
18552
|
+
"@absolutejs/sync-capacitor@0.8.0",
|
|
18509
18553
|
"@capacitor-community/sqlite@8.1.1"
|
|
18510
18554
|
];
|
|
18511
18555
|
});
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
sendAbsoluteHmrTiming
|
|
19
19
|
} from './hmrTiming';
|
|
20
20
|
import { hideErrorOverlay, showErrorOverlay } from './errorOverlay';
|
|
21
|
+
import { installAbsoluteNativeSyncDevtools } from './syncDevtools';
|
|
21
22
|
import {
|
|
22
23
|
dispatchAngularComponentRemount,
|
|
23
24
|
dispatchAngularComponentUpdate
|
|
@@ -45,6 +46,10 @@ const isStringRecord = (value: unknown): value is Record<string, string> =>
|
|
|
45
46
|
Object.values(value).every((entry) => typeof entry === 'string');
|
|
46
47
|
|
|
47
48
|
restoreAbsoluteHmrApply();
|
|
49
|
+
const removeNativeSyncDevtools =
|
|
50
|
+
absoluteHmrClientTarget() === 'web'
|
|
51
|
+
? () => undefined
|
|
52
|
+
: installAbsoluteNativeSyncDevtools();
|
|
48
53
|
|
|
49
54
|
/* Lightweight "server disconnected" banner. When the dev server is
|
|
50
55
|
* genuinely down (process restarting or crashed) the browser would
|
|
@@ -165,15 +170,15 @@ type HMRMessage = {
|
|
|
165
170
|
|
|
166
171
|
const handleStylesheetUpdate = (message: HMRMessage) => {
|
|
167
172
|
const clientStart = performance.now();
|
|
168
|
-
void reloadCSSStylesheets(message.data.manifest ?? {}).then((applied) =>
|
|
173
|
+
void reloadCSSStylesheets(message.data.manifest ?? {}).then((applied) =>
|
|
169
174
|
sendAbsoluteHmrTiming({
|
|
170
175
|
clientStart,
|
|
171
176
|
kind: 'css',
|
|
172
177
|
outcome: applied ? 'applied' : 'failed',
|
|
173
178
|
serverMs: message.data.serverDuration,
|
|
174
179
|
updateId: message.timestamp
|
|
175
|
-
})
|
|
176
|
-
|
|
180
|
+
})
|
|
181
|
+
);
|
|
177
182
|
};
|
|
178
183
|
|
|
179
184
|
const handleHMRMessage = (message: HMRMessage) => {
|
|
@@ -413,6 +418,7 @@ if (!(window.__HMR_WS__ && window.__HMR_WS__.readyState === WebSocket.OPEN)) {
|
|
|
413
418
|
};
|
|
414
419
|
|
|
415
420
|
window.addEventListener('beforeunload', () => {
|
|
421
|
+
removeNativeSyncDevtools();
|
|
416
422
|
if (hmrState.isHMRUpdating) {
|
|
417
423
|
if (hmrState.pingInterval) clearInterval(hmrState.pingInterval);
|
|
418
424
|
if (hmrState.reconnectTimeout)
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import type {} from '../../types/globals';
|
|
2
|
+
import {
|
|
3
|
+
discardSyncRuntimeDeadLetter,
|
|
4
|
+
inspectSyncRuntime,
|
|
5
|
+
rebaseSyncRuntimeDeadLetter,
|
|
6
|
+
retrySyncRuntimeDeadLetter,
|
|
7
|
+
type SyncRuntimeInspection
|
|
8
|
+
} from '@absolutejs/sync/client/runtime';
|
|
9
|
+
|
|
10
|
+
export type SyncDevtoolsBridge = {
|
|
11
|
+
discard: (operationId: string) => Promise<void>;
|
|
12
|
+
inspect: () => Promise<SyncRuntimeInspection>;
|
|
13
|
+
rebase: (operationId: string, args: unknown) => Promise<string>;
|
|
14
|
+
retry: (operationId: string) => Promise<void>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const DEVTOOLS_ID = 'absolutejs-sync-devtools';
|
|
18
|
+
const REFRESH_INTERVAL_MS = 1_500;
|
|
19
|
+
const bridge: SyncDevtoolsBridge = {
|
|
20
|
+
discard: discardSyncRuntimeDeadLetter,
|
|
21
|
+
inspect: inspectSyncRuntime,
|
|
22
|
+
rebase: rebaseSyncRuntimeDeadLetter,
|
|
23
|
+
retry: retrySyncRuntimeDeadLetter
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const time = (value: number | undefined) =>
|
|
27
|
+
value === undefined ? '—' : new Date(value).toLocaleTimeString();
|
|
28
|
+
|
|
29
|
+
const styles = `
|
|
30
|
+
:host { all: initial; color-scheme: light dark; }
|
|
31
|
+
button { font: inherit; }
|
|
32
|
+
.trigger { position:fixed;right:max(12px,env(safe-area-inset-right));bottom:max(12px,env(safe-area-inset-bottom));z-index:2147483645;border:0;border-radius:999px;padding:9px 13px;background:#111827;color:#fff;box-shadow:0 4px 18px #0006;font:600 12px/1.2 ui-monospace,SFMono-Regular,Menlo,monospace; }
|
|
33
|
+
.trigger[data-alert=true] { background:#b91c1c; }
|
|
34
|
+
.panel { position:fixed;inset:max(12px,env(safe-area-inset-top)) max(12px,env(safe-area-inset-right)) max(56px,env(safe-area-inset-bottom)) auto;z-index:2147483645;width:min(420px,calc(100vw - 24px));max-height:calc(100vh - 80px);overflow:auto;border:1px solid #64748b66;border-radius:14px;background:#fffffff2;color:#111827;box-shadow:0 16px 48px #0008;font:12px/1.45 ui-monospace,SFMono-Regular,Menlo,monospace;backdrop-filter:blur(12px); }
|
|
35
|
+
[hidden] { display:none!important; }
|
|
36
|
+
header { position:sticky;top:0;display:flex;align-items:center;justify-content:space-between;padding:12px 14px;border-bottom:1px solid #64748b44;background:inherit; }
|
|
37
|
+
h2 { margin:0;font-size:14px; }
|
|
38
|
+
.close,.action { border:1px solid #64748b66;border-radius:7px;background:transparent;color:inherit;padding:5px 8px; }
|
|
39
|
+
.body { padding:12px 14px; }
|
|
40
|
+
.metrics { display:grid;grid-template-columns:repeat(2,1fr);gap:7px;margin-bottom:12px; }
|
|
41
|
+
.metric { padding:8px;border-radius:8px;background:#64748b18; }
|
|
42
|
+
.metric strong { display:block;font-size:16px; }
|
|
43
|
+
.empty { color:#64748b; }
|
|
44
|
+
.letter { margin-top:9px;padding:10px;border:1px solid #ef444466;border-radius:9px;overflow-wrap:anywhere; }
|
|
45
|
+
.letter strong { display:block; }
|
|
46
|
+
.meta { color:#64748b;margin:4px 0 8px; }
|
|
47
|
+
.actions { display:flex;flex-wrap:wrap;gap:6px; }
|
|
48
|
+
.danger { color:#b91c1c; }
|
|
49
|
+
.notice { margin-bottom:9px;padding:8px;border-radius:7px;background:#f59e0b22; }
|
|
50
|
+
@media (prefers-color-scheme:dark) { .panel { background:#111827f2;color:#f8fafc; } .empty,.meta { color:#94a3b8; } .danger { color:#fca5a5; } }
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
const renderInspection = (
|
|
54
|
+
body: HTMLElement,
|
|
55
|
+
inspection: SyncRuntimeInspection,
|
|
56
|
+
notice?: string
|
|
57
|
+
) => {
|
|
58
|
+
body.replaceChildren();
|
|
59
|
+
if (notice) {
|
|
60
|
+
const message = document.createElement('div');
|
|
61
|
+
message.className = 'notice';
|
|
62
|
+
message.textContent = notice;
|
|
63
|
+
body.appendChild(message);
|
|
64
|
+
}
|
|
65
|
+
const metrics = document.createElement('div');
|
|
66
|
+
metrics.className = 'metrics';
|
|
67
|
+
for (const [label, value] of [
|
|
68
|
+
['Pending', inspection.pending],
|
|
69
|
+
['Dead letters', inspection.deadLetters.length],
|
|
70
|
+
['Conflicts', inspection.conflicts],
|
|
71
|
+
['Auto-resolved', inspection.automaticResolutions]
|
|
72
|
+
] as const) {
|
|
73
|
+
const metric = document.createElement('div');
|
|
74
|
+
metric.className = 'metric';
|
|
75
|
+
const strong = document.createElement('strong');
|
|
76
|
+
strong.textContent = String(value);
|
|
77
|
+
metric.append(strong, label);
|
|
78
|
+
metrics.appendChild(metric);
|
|
79
|
+
}
|
|
80
|
+
body.appendChild(metrics);
|
|
81
|
+
const activity = document.createElement('div');
|
|
82
|
+
activity.className = 'meta';
|
|
83
|
+
activity.textContent = `Clients ${inspection.clients} · last push ${time(inspection.lastSuccessfulPushAt)} · last pull ${time(inspection.lastSuccessfulPullAt)}`;
|
|
84
|
+
body.appendChild(activity);
|
|
85
|
+
if (inspection.deadLetters.length === 0) {
|
|
86
|
+
const empty = document.createElement('div');
|
|
87
|
+
empty.className = 'empty';
|
|
88
|
+
empty.textContent = 'No mutations need manual remediation.';
|
|
89
|
+
body.appendChild(empty);
|
|
90
|
+
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
for (const deadLetter of inspection.deadLetters) {
|
|
94
|
+
const item = document.createElement('section');
|
|
95
|
+
item.className = 'letter';
|
|
96
|
+
const title = document.createElement('strong');
|
|
97
|
+
title.textContent = deadLetter.name;
|
|
98
|
+
const metadata = document.createElement('div');
|
|
99
|
+
metadata.className = 'meta';
|
|
100
|
+
metadata.textContent = `${deadLetter.kind ?? 'rejected'}${deadLetter.code ? ` · ${deadLetter.code}` : ''} · attempts ${deadLetter.attempts} · ${time(deadLetter.deadLetteredAt)}`;
|
|
101
|
+
const detail = document.createElement('div');
|
|
102
|
+
detail.textContent =
|
|
103
|
+
deadLetter.message ?? 'The server rejected this mutation.';
|
|
104
|
+
const actions = document.createElement('div');
|
|
105
|
+
actions.className = 'actions';
|
|
106
|
+
for (const [action, label] of [
|
|
107
|
+
['retry', 'Retry unchanged'],
|
|
108
|
+
['rebase', 'Rebase with new args'],
|
|
109
|
+
['discard', 'Discard']
|
|
110
|
+
] as const) {
|
|
111
|
+
const button = document.createElement('button');
|
|
112
|
+
button.className = `action${action === 'discard' ? ' danger' : ''}`;
|
|
113
|
+
button.dataset.action = action;
|
|
114
|
+
button.dataset.operationId = deadLetter.operationId;
|
|
115
|
+
button.textContent = label;
|
|
116
|
+
actions.appendChild(button);
|
|
117
|
+
}
|
|
118
|
+
item.append(title, metadata, detail, actions);
|
|
119
|
+
body.appendChild(item);
|
|
120
|
+
}
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
/** Install the development-only, framework-neutral native Sync panel. */
|
|
124
|
+
export const installAbsoluteNativeSyncDevtools = (
|
|
125
|
+
devtoolsBridge: SyncDevtoolsBridge = bridge
|
|
126
|
+
) => {
|
|
127
|
+
if (typeof document === 'undefined' || !document.body)
|
|
128
|
+
return () => undefined;
|
|
129
|
+
if (document.getElementById(DEVTOOLS_ID)) return () => undefined;
|
|
130
|
+
const host = document.createElement('aside');
|
|
131
|
+
host.id = DEVTOOLS_ID;
|
|
132
|
+
host.dataset.hmrOverlay = 'true';
|
|
133
|
+
const root = host.attachShadow({ mode: 'open' });
|
|
134
|
+
const style = document.createElement('style');
|
|
135
|
+
style.textContent = styles;
|
|
136
|
+
const trigger = document.createElement('button');
|
|
137
|
+
trigger.className = 'trigger';
|
|
138
|
+
trigger.textContent = 'Sync';
|
|
139
|
+
trigger.type = 'button';
|
|
140
|
+
const panel = document.createElement('section');
|
|
141
|
+
panel.className = 'panel';
|
|
142
|
+
panel.hidden = true;
|
|
143
|
+
const header = document.createElement('header');
|
|
144
|
+
const title = document.createElement('h2');
|
|
145
|
+
title.textContent = 'AbsoluteJS Sync';
|
|
146
|
+
const close = document.createElement('button');
|
|
147
|
+
close.className = 'close';
|
|
148
|
+
close.textContent = 'Close';
|
|
149
|
+
close.type = 'button';
|
|
150
|
+
header.append(title, close);
|
|
151
|
+
const body = document.createElement('div');
|
|
152
|
+
body.className = 'body';
|
|
153
|
+
panel.append(header, body);
|
|
154
|
+
root.append(style, trigger, panel);
|
|
155
|
+
document.body.appendChild(host);
|
|
156
|
+
let active = true;
|
|
157
|
+
let notice: string | undefined;
|
|
158
|
+
const refresh = async () => {
|
|
159
|
+
try {
|
|
160
|
+
const inspection = await devtoolsBridge.inspect();
|
|
161
|
+
if (!active) return;
|
|
162
|
+
trigger.dataset.alert = String(inspection.deadLetters.length > 0);
|
|
163
|
+
trigger.textContent = inspection.deadLetters.length
|
|
164
|
+
? `Sync · ${inspection.deadLetters.length}`
|
|
165
|
+
: 'Sync';
|
|
166
|
+
if (!panel.hidden) renderInspection(body, inspection, notice);
|
|
167
|
+
notice = undefined;
|
|
168
|
+
} catch {
|
|
169
|
+
if (!active || panel.hidden) return;
|
|
170
|
+
notice = 'Sync diagnostics are temporarily unavailable.';
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
trigger.addEventListener('click', () => {
|
|
174
|
+
panel.hidden = !panel.hidden;
|
|
175
|
+
if (!panel.hidden) void refresh();
|
|
176
|
+
});
|
|
177
|
+
close.addEventListener('click', () => {
|
|
178
|
+
panel.hidden = true;
|
|
179
|
+
});
|
|
180
|
+
const remediate = async (
|
|
181
|
+
action: string | undefined,
|
|
182
|
+
operationId: string
|
|
183
|
+
) => {
|
|
184
|
+
try {
|
|
185
|
+
if (action === 'retry') await devtoolsBridge.retry(operationId);
|
|
186
|
+
else if (action === 'discard') {
|
|
187
|
+
if (
|
|
188
|
+
!globalThis.confirm(
|
|
189
|
+
'Discard this local mutation permanently?'
|
|
190
|
+
)
|
|
191
|
+
)
|
|
192
|
+
return;
|
|
193
|
+
await devtoolsBridge.discard(operationId);
|
|
194
|
+
} else if (action === 'rebase') {
|
|
195
|
+
const serialized = globalThis.prompt(
|
|
196
|
+
'New mutation arguments as JSON. This creates a new operation intent:'
|
|
197
|
+
);
|
|
198
|
+
if (serialized === null) return;
|
|
199
|
+
let args: unknown;
|
|
200
|
+
try {
|
|
201
|
+
args = JSON.parse(serialized);
|
|
202
|
+
} catch {
|
|
203
|
+
notice = 'Rebase cancelled: arguments were not valid JSON.';
|
|
204
|
+
await refresh();
|
|
205
|
+
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
if (
|
|
209
|
+
!globalThis.confirm(
|
|
210
|
+
'Create a new mutation with these arguments?'
|
|
211
|
+
)
|
|
212
|
+
)
|
|
213
|
+
return;
|
|
214
|
+
await devtoolsBridge.rebase(operationId, args);
|
|
215
|
+
}
|
|
216
|
+
notice = 'Sync remediation applied.';
|
|
217
|
+
} catch {
|
|
218
|
+
notice =
|
|
219
|
+
'Sync remediation failed. The local mutation was retained.';
|
|
220
|
+
}
|
|
221
|
+
await refresh();
|
|
222
|
+
};
|
|
223
|
+
body.addEventListener('click', (event) => {
|
|
224
|
+
if (!(event.target instanceof HTMLButtonElement)) return;
|
|
225
|
+
const { action, operationId } = event.target.dataset;
|
|
226
|
+
if (!operationId) return;
|
|
227
|
+
void remediate(action, operationId);
|
|
228
|
+
});
|
|
229
|
+
const interval = setInterval(() => void refresh(), REFRESH_INTERVAL_MS);
|
|
230
|
+
void refresh();
|
|
231
|
+
|
|
232
|
+
return () => {
|
|
233
|
+
active = false;
|
|
234
|
+
clearInterval(interval);
|
|
235
|
+
host.remove();
|
|
236
|
+
};
|
|
237
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -13199,7 +13199,7 @@ var isTestSourcePath = (file2) => {
|
|
|
13199
13199
|
};
|
|
13200
13200
|
|
|
13201
13201
|
// node_modules/@absolutejs/sync/dist/client/index.js
|
|
13202
|
-
var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations")), registry, SyncLocalDataPolicyError, SyncLocalStoreSchemaError, positiveVersion = (value, label) => {
|
|
13202
|
+
var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" && value !== null && Array.isArray(Reflect.get(value, "installations")) && Array.isArray(Reflect.get(value, "clients")), registry, SyncLocalDataPolicyError, SyncLocalStoreSchemaError, positiveVersion = (value, label) => {
|
|
13203
13203
|
if (!Number.isSafeInteger(value) || value < 1)
|
|
13204
13204
|
throw new SyncLocalStoreSchemaError("INVALID_PLAN", `${label} must be a positive safe integer`);
|
|
13205
13205
|
return value;
|
|
@@ -13220,6 +13220,14 @@ var RUNTIME_TRANSPORT, host, isRegistry = (value) => typeof value === "object" &
|
|
|
13220
13220
|
}
|
|
13221
13221
|
for (const [index, rule] of (policy.mutations ?? []).entries()) {
|
|
13222
13222
|
validatePolicyMatch(rule.match, `${label}.mutations[${index}]`);
|
|
13223
|
+
if (rule.conflict !== undefined && rule.conflict.strategy !== "client-wins" && rule.conflict.strategy !== "manual" && rule.conflict.strategy !== "server-wins")
|
|
13224
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.strategy is invalid.`);
|
|
13225
|
+
if (rule.conflict?.maxAttempts !== undefined && (!Number.isSafeInteger(rule.conflict.maxAttempts) || rule.conflict.maxAttempts < 1))
|
|
13226
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts must be a positive safe integer.`);
|
|
13227
|
+
if (rule.conflict?.maxAttempts !== undefined && rule.conflict.strategy !== "client-wins")
|
|
13228
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}].conflict.maxAttempts is only valid for client-wins.`);
|
|
13229
|
+
if (rule.persistence === "memory-only" && rule.protection === "required")
|
|
13230
|
+
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] cannot require at-rest protection when it is memory-only.`);
|
|
13223
13231
|
if (rule.sensitivity !== undefined && rule.sensitivity !== "public" && rule.protection !== "required" && rule.persistence !== "memory-only")
|
|
13224
13232
|
throw new SyncLocalDataPolicyError("INVALID_POLICY", `${label}.mutations[${index}] declares ${rule.sensitivity} arguments without required protection.`);
|
|
13225
13233
|
}
|
|
@@ -13283,7 +13291,11 @@ var init_client = __esm(() => {
|
|
|
13283
13291
|
const existing = host[RUNTIME_TRANSPORT];
|
|
13284
13292
|
if (isRegistry(existing))
|
|
13285
13293
|
return existing;
|
|
13286
|
-
|
|
13294
|
+
if (typeof existing === "object" && existing !== null && Array.isArray(Reflect.get(existing, "installations"))) {
|
|
13295
|
+
Reflect.set(existing, "clients", []);
|
|
13296
|
+
return existing;
|
|
13297
|
+
}
|
|
13298
|
+
const created = { clients: [], installations: [] };
|
|
13287
13299
|
Object.defineProperty(host, RUNTIME_TRANSPORT, {
|
|
13288
13300
|
configurable: false,
|
|
13289
13301
|
enumerable: false,
|
|
@@ -13471,7 +13483,9 @@ var object = (value) => typeof value === "object" && value !== null && !Array.is
|
|
|
13471
13483
|
const mutations = Array.isArray(mutationRules) ? mutationRules.map((entry, index) => {
|
|
13472
13484
|
const rule = requireObject(entry, id, `localData.mutations[${index}] must be an object.`);
|
|
13473
13485
|
const allowedRuleKeys = new Set([
|
|
13486
|
+
"conflict",
|
|
13474
13487
|
"match",
|
|
13488
|
+
"onProtectionUnavailable",
|
|
13475
13489
|
"persistence",
|
|
13476
13490
|
"protection",
|
|
13477
13491
|
"sensitivity"
|
|
@@ -13482,15 +13496,40 @@ var object = (value) => typeof value === "object" && value !== null && !Array.is
|
|
|
13482
13496
|
const protection = unknownField(rule, "protection");
|
|
13483
13497
|
const sensitivity = unknownField(rule, "sensitivity");
|
|
13484
13498
|
const persistence = unknownField(rule, "persistence");
|
|
13499
|
+
const onProtectionUnavailable = unknownField(rule, "onProtectionUnavailable");
|
|
13500
|
+
const declaredConflict = unknownField(rule, "conflict");
|
|
13501
|
+
let conflict;
|
|
13502
|
+
if (declaredConflict !== undefined) {
|
|
13503
|
+
const conflictRecord = requireObject(declaredConflict, id, `localData.mutations[${index}].conflict must be an object.`);
|
|
13504
|
+
const unsupportedConflictKey = Object.keys(conflictRecord).find((key) => key !== "maxAttempts" && key !== "strategy");
|
|
13505
|
+
if (unsupportedConflictKey)
|
|
13506
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.${unsupportedConflictKey} is not supported.`);
|
|
13507
|
+
const strategy = unknownField(conflictRecord, "strategy");
|
|
13508
|
+
if (strategy !== "client-wins" && strategy !== "manual" && strategy !== "server-wins")
|
|
13509
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.strategy is invalid.`);
|
|
13510
|
+
const maxAttempts = unknownField(conflictRecord, "maxAttempts");
|
|
13511
|
+
if (maxAttempts !== undefined && strategy !== "client-wins")
|
|
13512
|
+
throw metadataError(id, `localData.mutations[${index}].conflict.maxAttempts requires client-wins.`);
|
|
13513
|
+
conflict = {
|
|
13514
|
+
strategy,
|
|
13515
|
+
...maxAttempts === undefined ? {} : {
|
|
13516
|
+
maxAttempts: positiveVersion2(maxAttempts, id, `localData.mutations[${index}].conflict.maxAttempts`)
|
|
13517
|
+
}
|
|
13518
|
+
};
|
|
13519
|
+
}
|
|
13485
13520
|
if (protection !== undefined && protection !== "none" && protection !== "required")
|
|
13486
13521
|
throw metadataError(id, `localData.mutations[${index}].protection is invalid.`);
|
|
13487
13522
|
if (sensitivity !== undefined && sensitivity !== "public" && sensitivity !== "private" && sensitivity !== "secret")
|
|
13488
13523
|
throw metadataError(id, `localData.mutations[${index}].sensitivity is invalid.`);
|
|
13524
|
+
if (onProtectionUnavailable !== undefined && onProtectionUnavailable !== "error" && onProtectionUnavailable !== "memory-only")
|
|
13525
|
+
throw metadataError(id, `localData.mutations[${index}].onProtectionUnavailable is invalid.`);
|
|
13489
13526
|
if (persistence !== undefined && persistence !== "durable" && persistence !== "memory-only")
|
|
13490
13527
|
throw metadataError(id, `localData.mutations[${index}].persistence is invalid.`);
|
|
13491
13528
|
return {
|
|
13492
13529
|
match: nonEmpty(Reflect.get(rule, "match"), id, `localData.mutations[${index}].match`),
|
|
13530
|
+
...conflict ? { conflict } : {},
|
|
13493
13531
|
...sensitivity ? { sensitivity } : {},
|
|
13532
|
+
...onProtectionUnavailable ? { onProtectionUnavailable } : {},
|
|
13494
13533
|
...persistence ? {
|
|
13495
13534
|
persistence
|
|
13496
13535
|
} : {},
|
|
@@ -40380,5 +40419,5 @@ export {
|
|
|
40380
40419
|
ANGULAR_INIT_TIMEOUT_MS
|
|
40381
40420
|
};
|
|
40382
40421
|
|
|
40383
|
-
//# debugId=
|
|
40422
|
+
//# debugId=C0E84CA8E87CA8B764756E2164756E21
|
|
40384
40423
|
//# sourceMappingURL=index.js.map
|