@abloatai/ablo 0.10.1 → 0.11.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 +10 -0
- package/README.md +2 -1
- package/dist/BaseSyncedStore.d.ts +75 -0
- package/dist/BaseSyncedStore.js +193 -8
- package/dist/Database.d.ts +10 -2
- package/dist/Database.js +15 -1
- package/dist/SyncClient.d.ts +12 -1
- package/dist/SyncClient.js +110 -26
- package/dist/agent/Agent.d.ts +9 -9
- package/dist/agent/Agent.js +16 -16
- package/dist/agent/index.d.ts +1 -1
- package/dist/agent/index.js +2 -2
- package/dist/agent/types.d.ts +1 -1
- package/dist/agent/types.js +1 -1
- package/dist/ai-sdk/{intent-broadcast.d.ts → claim-broadcast.d.ts} +10 -10
- package/dist/ai-sdk/{intent-broadcast.js → claim-broadcast.js} +6 -6
- package/dist/ai-sdk/coordination-context.d.ts +9 -9
- package/dist/ai-sdk/coordination-context.js +8 -8
- package/dist/ai-sdk/index.d.ts +1 -1
- package/dist/ai-sdk/index.js +1 -1
- package/dist/ai-sdk/wrap.d.ts +4 -4
- package/dist/ai-sdk/wrap.js +4 -4
- package/dist/api/index.d.ts +2 -2
- package/dist/cli.cjs +254 -48
- package/dist/client/Ablo.d.ts +30 -63
- package/dist/client/Ablo.js +108 -102
- package/dist/client/ApiClient.d.ts +6 -5
- package/dist/client/ApiClient.js +83 -62
- package/dist/client/createModelProxy.d.ts +16 -54
- package/dist/client/createModelProxy.js +44 -16
- package/dist/client/httpClient.d.ts +2 -0
- package/dist/client/httpClient.js +1 -1
- package/dist/client/index.d.ts +3 -3
- package/dist/client/writeOptionsSchema.d.ts +4 -4
- package/dist/client/writeOptionsSchema.js +4 -4
- package/dist/coordination/schema.d.ts +249 -38
- package/dist/coordination/schema.js +172 -39
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.js +4 -4
- package/dist/errorCodes.d.ts +9 -9
- package/dist/errorCodes.js +15 -15
- package/dist/errors.d.ts +51 -2
- package/dist/errors.js +94 -5
- package/dist/interfaces/index.d.ts +8 -4
- package/dist/policy/index.d.ts +1 -1
- package/dist/policy/types.d.ts +13 -13
- package/dist/policy/types.js +8 -8
- package/dist/react/AbloProvider.d.ts +51 -4
- package/dist/react/AbloProvider.js +95 -11
- package/dist/react/context.d.ts +26 -9
- package/dist/react/context.js +2 -2
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.js +4 -4
- package/dist/react/useAblo.js +5 -5
- package/dist/react/{useIntent.d.ts → useClaim.d.ts} +9 -9
- package/dist/react/useClaim.js +42 -0
- package/dist/schema/index.js +1 -1
- package/dist/schema/sugar.d.ts +3 -3
- package/dist/schema/sugar.js +3 -3
- package/dist/schema/sync-delta-wire.d.ts +8 -8
- package/dist/server/commit.d.ts +2 -2
- package/dist/sync/AreaOfInterestManager.d.ts +162 -0
- package/dist/sync/AreaOfInterestManager.js +233 -0
- package/dist/sync/BootstrapHelper.d.ts +9 -1
- package/dist/sync/BootstrapHelper.js +15 -5
- package/dist/sync/NetworkProbe.d.ts +1 -1
- package/dist/sync/NetworkProbe.js +1 -1
- package/dist/sync/SyncWebSocket.d.ts +59 -25
- package/dist/sync/SyncWebSocket.js +123 -26
- package/dist/sync/awaitClaimGrant.d.ts +40 -0
- package/dist/sync/awaitClaimGrant.js +86 -0
- package/dist/sync/createClaimStream.d.ts +34 -0
- package/dist/sync/{createIntentStream.js → createClaimStream.js} +92 -81
- package/dist/sync/createPresenceStream.js +3 -2
- package/dist/sync/participants.d.ts +10 -10
- package/dist/sync/participants.js +17 -10
- package/dist/sync/schemas.d.ts +8 -8
- package/dist/transactions/TransactionQueue.d.ts +12 -0
- package/dist/transactions/TransactionQueue.js +126 -8
- package/dist/types/global.d.ts +10 -10
- package/dist/types/global.js +3 -3
- package/dist/types/index.d.ts +9 -7
- package/dist/types/index.js +2 -2
- package/dist/types/streams.d.ts +114 -98
- package/dist/types/streams.js +1 -1
- package/dist/utils/asyncIterator.d.ts +1 -1
- package/dist/utils/asyncIterator.js +1 -1
- package/dist/wire/frames.d.ts +2 -2
- package/package.json +3 -2
- package/dist/react/useIntent.js +0 -42
- package/dist/sync/awaitIntentGrant.d.ts +0 -40
- package/dist/sync/awaitIntentGrant.js +0 -62
- package/dist/sync/createIntentStream.d.ts +0 -34
|
@@ -225,6 +225,7 @@ export class TransactionQueue extends EventEmitter {
|
|
|
225
225
|
// Per-model in-flight tracking and merge buffer
|
|
226
226
|
inFlightByModel = new Set();
|
|
227
227
|
pendingMergeByModel = new Map();
|
|
228
|
+
deferredDeletesByCreate = new Map();
|
|
228
229
|
// Commit lane: pre-built atomic multi-op envelopes from `ablo.commits.create()`.
|
|
229
230
|
// Drained serially (one envelope at a time) since each is atomic; no
|
|
230
231
|
// coalescing with model-proxy transactions.
|
|
@@ -242,6 +243,102 @@ export class TransactionQueue extends EventEmitter {
|
|
|
242
243
|
transaction.priorityScore = this.computePriorityScore(transaction.type, transaction.modelName);
|
|
243
244
|
}
|
|
244
245
|
}
|
|
246
|
+
entityKey(modelName, modelId) {
|
|
247
|
+
return `${modelName}:${modelId}`;
|
|
248
|
+
}
|
|
249
|
+
isTransactionForModel(transaction, modelName, modelId) {
|
|
250
|
+
return transaction.modelName === modelName && transaction.modelId === modelId;
|
|
251
|
+
}
|
|
252
|
+
resolveConfirmation(transaction) {
|
|
253
|
+
const resolver = this.confirmationResolvers.get(transaction.id);
|
|
254
|
+
if (!resolver)
|
|
255
|
+
return;
|
|
256
|
+
this.confirmationResolvers.delete(transaction.id);
|
|
257
|
+
resolver.resolve();
|
|
258
|
+
}
|
|
259
|
+
takeUnsentCreateForModel(modelName, modelId) {
|
|
260
|
+
const isUnsentCreate = (tx) => tx.type === 'create' &&
|
|
261
|
+
tx.status === 'pending' &&
|
|
262
|
+
tx.attempts === 0 &&
|
|
263
|
+
this.isTransactionForModel(tx, modelName, modelId);
|
|
264
|
+
const stagedIndex = this.createdTransactions.findIndex(isUnsentCreate);
|
|
265
|
+
if (stagedIndex >= 0) {
|
|
266
|
+
return this.createdTransactions.splice(stagedIndex, 1)[0];
|
|
267
|
+
}
|
|
268
|
+
const queuedIndex = this.executionQueue.findIndex(isUnsentCreate);
|
|
269
|
+
if (queuedIndex >= 0) {
|
|
270
|
+
return this.executionQueue.splice(queuedIndex, 1)[0];
|
|
271
|
+
}
|
|
272
|
+
return this.store.getByStatus('pending').find(isUnsentCreate);
|
|
273
|
+
}
|
|
274
|
+
async cancelUnsentCreateForDelete(transaction) {
|
|
275
|
+
this.store.updateStatus(transaction.id, 'rolled_back');
|
|
276
|
+
if (this.config.enableOptimistic) {
|
|
277
|
+
await this.rollbackOptimistic(transaction, 'model_cancelled');
|
|
278
|
+
}
|
|
279
|
+
this.resolveConfirmation(transaction);
|
|
280
|
+
}
|
|
281
|
+
findCreateBarrierForDelete(modelName, modelId) {
|
|
282
|
+
const liveCreates = [
|
|
283
|
+
...this.store.getByStatus('pending'),
|
|
284
|
+
...this.store.getByStatus('executing'),
|
|
285
|
+
...this.store.getByStatus('awaiting_delta'),
|
|
286
|
+
].filter((tx) => tx.type === 'create' &&
|
|
287
|
+
this.isTransactionForModel(tx, modelName, modelId) &&
|
|
288
|
+
// A never-attempted pending create can be cancelled instead. Once the
|
|
289
|
+
// create has been sent, even a retry-pending state is a causal barrier:
|
|
290
|
+
// the server may already have applied it and only the response was lost.
|
|
291
|
+
(tx.status !== 'pending' || tx.attempts > 0));
|
|
292
|
+
return liveCreates.sort((a, b) => b.createdAt - a.createdAt)[0];
|
|
293
|
+
}
|
|
294
|
+
completeLocalDelete(model, context, writeOptions) {
|
|
295
|
+
const actualModelName = model.getModelName();
|
|
296
|
+
const modelKey = normalizeModelKey(actualModelName);
|
|
297
|
+
const transaction = {
|
|
298
|
+
id: this.generateId(),
|
|
299
|
+
type: 'delete',
|
|
300
|
+
modelName: actualModelName,
|
|
301
|
+
modelId: model.id,
|
|
302
|
+
modelKey,
|
|
303
|
+
priorityScore: this.computePriorityScore('delete', actualModelName),
|
|
304
|
+
previousData: model.toJSON ? model.toJSON() : { ...model },
|
|
305
|
+
context,
|
|
306
|
+
status: 'completed',
|
|
307
|
+
createdAt: Date.now(),
|
|
308
|
+
attempts: 0,
|
|
309
|
+
priority: 'high',
|
|
310
|
+
writeOptions,
|
|
311
|
+
localOnly: true,
|
|
312
|
+
};
|
|
313
|
+
this.attachConfirmation(transaction);
|
|
314
|
+
this.store.add(transaction);
|
|
315
|
+
if (this.config.enableOptimistic) {
|
|
316
|
+
this.applyOptimisticDelete(model, transaction);
|
|
317
|
+
}
|
|
318
|
+
this.emit('transaction:created', transaction);
|
|
319
|
+
this.emit('transaction:completed', transaction);
|
|
320
|
+
this.emit(`transaction:completed:${transaction.id}`, transaction);
|
|
321
|
+
this.optimisticUpdates.delete(transaction.id);
|
|
322
|
+
return transaction;
|
|
323
|
+
}
|
|
324
|
+
deferDeleteUntilCreateSettles(createTransaction, deleteTransaction) {
|
|
325
|
+
const key = this.entityKey(createTransaction.modelName, createTransaction.modelId);
|
|
326
|
+
const deferred = this.deferredDeletesByCreate.get(key) ?? [];
|
|
327
|
+
deferred.push(deleteTransaction);
|
|
328
|
+
this.deferredDeletesByCreate.set(key, deferred);
|
|
329
|
+
}
|
|
330
|
+
releaseDeferredDeletesForCreate(createTransaction) {
|
|
331
|
+
const key = this.entityKey(createTransaction.modelName, createTransaction.modelId);
|
|
332
|
+
const deferred = this.deferredDeletesByCreate.get(key);
|
|
333
|
+
if (!deferred || deferred.length === 0)
|
|
334
|
+
return;
|
|
335
|
+
this.deferredDeletesByCreate.delete(key);
|
|
336
|
+
for (const deleteTransaction of deferred) {
|
|
337
|
+
if (this.store.get(deleteTransaction.id)?.status !== 'pending')
|
|
338
|
+
continue;
|
|
339
|
+
this.enqueue(deleteTransaction);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
245
342
|
// Merge two GraphQL update payloads with special handling for metadata fields
|
|
246
343
|
mergeUpdateData(left, right, _modelName) {
|
|
247
344
|
const out = { ...(left || {}) };
|
|
@@ -374,6 +471,9 @@ export class TransactionQueue extends EventEmitter {
|
|
|
374
471
|
this.confirmationResolvers.delete(tx.id);
|
|
375
472
|
r.resolve();
|
|
376
473
|
}
|
|
474
|
+
if (tx.type === 'create') {
|
|
475
|
+
this.releaseDeferredDeletesForCreate(tx);
|
|
476
|
+
}
|
|
377
477
|
});
|
|
378
478
|
this.on('transaction:failed', ({ transaction, error }) => {
|
|
379
479
|
const r = this.confirmationResolvers.get(transaction.id);
|
|
@@ -381,6 +481,9 @@ export class TransactionQueue extends EventEmitter {
|
|
|
381
481
|
this.confirmationResolvers.delete(transaction.id);
|
|
382
482
|
r.reject(error);
|
|
383
483
|
}
|
|
484
|
+
if (transaction.type === 'create') {
|
|
485
|
+
this.releaseDeferredDeletesForCreate(transaction);
|
|
486
|
+
}
|
|
384
487
|
});
|
|
385
488
|
}
|
|
386
489
|
/**
|
|
@@ -726,6 +829,7 @@ export class TransactionQueue extends EventEmitter {
|
|
|
726
829
|
attempts: 0,
|
|
727
830
|
priority: 'high',
|
|
728
831
|
writeOptions,
|
|
832
|
+
localOnly: true,
|
|
729
833
|
// Activity deletes complete synchronously (audit-record skip path).
|
|
730
834
|
// Pre-resolved so consumers can still `await tx.confirmation` uniformly.
|
|
731
835
|
confirmation: Promise.resolve(),
|
|
@@ -740,6 +844,11 @@ export class TransactionQueue extends EventEmitter {
|
|
|
740
844
|
}
|
|
741
845
|
const modelKey = normalizeModelKey(actualModelName);
|
|
742
846
|
const priorityScore = this.computePriorityScore('delete', actualModelName);
|
|
847
|
+
const unsentCreate = this.takeUnsentCreateForModel(actualModelName, model.id);
|
|
848
|
+
if (unsentCreate) {
|
|
849
|
+
await this.cancelUnsentCreateForDelete(unsentCreate);
|
|
850
|
+
return this.completeLocalDelete(model, context, writeOptions);
|
|
851
|
+
}
|
|
743
852
|
const transaction = {
|
|
744
853
|
id: this.generateId(),
|
|
745
854
|
type: 'delete',
|
|
@@ -760,15 +869,22 @@ export class TransactionQueue extends EventEmitter {
|
|
|
760
869
|
// Cancel any pending/in-flight updates for this model to prevent "no rows" errors
|
|
761
870
|
// when the delete executes before the update (race condition fix)
|
|
762
871
|
this.cancelTransactionsForModel(model.id, 'update');
|
|
763
|
-
this.
|
|
764
|
-
this.
|
|
872
|
+
const entityKey = this.entityKey(actualModelName, model.id);
|
|
873
|
+
this.pendingMergeByModel.delete(entityKey);
|
|
874
|
+
this.inFlightByModel.delete(entityKey);
|
|
765
875
|
// Apply optimistic delete
|
|
766
876
|
if (this.config.enableOptimistic) {
|
|
767
877
|
this.applyOptimisticDelete(model, transaction);
|
|
768
878
|
}
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
879
|
+
const createBarrier = this.findCreateBarrierForDelete(actualModelName, model.id);
|
|
880
|
+
if (createBarrier) {
|
|
881
|
+
this.deferDeleteUntilCreateSettles(createBarrier, transaction);
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
// LINEAR PATTERN: Stage transaction for microtask commit
|
|
885
|
+
// All deletes in same event loop will be batched together
|
|
886
|
+
this.stageTransaction(transaction);
|
|
887
|
+
}
|
|
772
888
|
this.emit('transaction:created', transaction);
|
|
773
889
|
return transaction;
|
|
774
890
|
}
|
|
@@ -1015,7 +1131,7 @@ export class TransactionQueue extends EventEmitter {
|
|
|
1015
1131
|
tx.syncIdNeededForCompletion = lastSyncId;
|
|
1016
1132
|
// Safety net: when lastSyncId is 0, DELETE transactions should be confirmed
|
|
1017
1133
|
// immediately. DELETEs are idempotent — if no delta was emitted, the entity
|
|
1018
|
-
// is already gone and the
|
|
1134
|
+
// is already gone and the claim was achieved. Parking DELETEs in awaiting_delta
|
|
1019
1135
|
// with threshold 0 causes 30s reconciliation delays.
|
|
1020
1136
|
if (lastSyncId === 0 && tx.type === 'delete') {
|
|
1021
1137
|
this.store.updateStatus(tx.id, 'completed');
|
|
@@ -1110,14 +1226,14 @@ export class TransactionQueue extends EventEmitter {
|
|
|
1110
1226
|
});
|
|
1111
1227
|
// LINEAR PATTERN: Handle "no rows in result set" gracefully
|
|
1112
1228
|
// This error means the entity was already deleted - for UPDATE/DELETE ops, this is success
|
|
1113
|
-
// The
|
|
1229
|
+
// The claim was achieved (the data doesn't exist), so treat as completed
|
|
1114
1230
|
if (errorMessage.includes('no rows in result set')) {
|
|
1115
1231
|
getContext().logger.info('[TransactionQueue] Graceful handling: entity already deleted', {
|
|
1116
1232
|
batchSize: batchOps.length,
|
|
1117
1233
|
});
|
|
1118
1234
|
for (const { tx, op } of batchOps) {
|
|
1119
1235
|
if (op.type === 'UPDATE' || op.type === 'DELETE') {
|
|
1120
|
-
// Entity gone =
|
|
1236
|
+
// Entity gone = claim achieved, mark as completed
|
|
1121
1237
|
this.store.updateStatus(tx.id, 'completed');
|
|
1122
1238
|
this.emit('transaction:completed', tx);
|
|
1123
1239
|
getContext().logger.debug('[TransactionQueue] Orphaned transaction treated as success', {
|
|
@@ -1973,6 +2089,8 @@ export class TransactionQueue extends EventEmitter {
|
|
|
1973
2089
|
this.store.clear();
|
|
1974
2090
|
this.optimisticUpdates.clear();
|
|
1975
2091
|
this.executionQueue = [];
|
|
2092
|
+
this.createdTransactions = [];
|
|
2093
|
+
this.deferredDeletesByCreate.clear();
|
|
1976
2094
|
// Clear event listeners
|
|
1977
2095
|
this.removeAllListeners();
|
|
1978
2096
|
// Reset state
|
package/dist/types/global.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type registration point for SDK consumers.
|
|
3
3
|
*
|
|
4
|
-
* A consumer registers their Schema, Presence,
|
|
4
|
+
* A consumer registers their Schema, Presence, Claims, and UserMeta ONCE by
|
|
5
5
|
* augmenting the {@link Register} interface, and every SDK hook — `useAblo`,
|
|
6
|
-
* `useQuery`, `useOne`, `usePresence`, `
|
|
6
|
+
* `useQuery`, `useOne`, `usePresence`, `useClaim` — reads its types from the
|
|
7
7
|
* resolved registration. No generics at call sites, no `schema` arg per call.
|
|
8
8
|
*
|
|
9
9
|
* Registration is done via **module augmentation** of `@abloatai/ablo` —
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* interface Register {
|
|
23
23
|
* Schema: typeof schema;
|
|
24
24
|
* Presence: { cursor: { x: number; y: number } | null };
|
|
25
|
-
*
|
|
25
|
+
* Claims: { editLayer: { layerId: string } };
|
|
26
26
|
* UserMeta: { id: string; email: string };
|
|
27
27
|
* }
|
|
28
28
|
* }
|
|
@@ -44,7 +44,7 @@ export interface DefaultSyncShape {
|
|
|
44
44
|
readonly models: Record<string, unknown>;
|
|
45
45
|
};
|
|
46
46
|
readonly Presence: Record<string, unknown>;
|
|
47
|
-
readonly
|
|
47
|
+
readonly Claims: Record<string, unknown>;
|
|
48
48
|
readonly UserMeta: {
|
|
49
49
|
readonly id: string;
|
|
50
50
|
};
|
|
@@ -75,13 +75,13 @@ export type ResolvePresence = Register extends {
|
|
|
75
75
|
Presence: infer P;
|
|
76
76
|
} ? P : DefaultSyncShape['Presence'];
|
|
77
77
|
/**
|
|
78
|
-
* The consumer's
|
|
79
|
-
*
|
|
80
|
-
* `
|
|
78
|
+
* The consumer's claim vocabulary, or the default if unregistered. Keys are
|
|
79
|
+
* claim names; values are the claim payload for each claim. Used by
|
|
80
|
+
* `useClaim(claimName)`.
|
|
81
81
|
*/
|
|
82
|
-
export type
|
|
83
|
-
|
|
84
|
-
} ? I : DefaultSyncShape['
|
|
82
|
+
export type ResolveClaims = Register extends {
|
|
83
|
+
Claims: infer I;
|
|
84
|
+
} ? I : DefaultSyncShape['Claims'];
|
|
85
85
|
/**
|
|
86
86
|
* The consumer's user-metadata shape, or the default if unregistered. Carries
|
|
87
87
|
* identity info the consumer trusts from their auth layer — not SDK-validated.
|
package/dist/types/global.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Type registration point for SDK consumers.
|
|
3
3
|
*
|
|
4
|
-
* A consumer registers their Schema, Presence,
|
|
4
|
+
* A consumer registers their Schema, Presence, Claims, and UserMeta ONCE by
|
|
5
5
|
* augmenting the {@link Register} interface, and every SDK hook — `useAblo`,
|
|
6
|
-
* `useQuery`, `useOne`, `usePresence`, `
|
|
6
|
+
* `useQuery`, `useOne`, `usePresence`, `useClaim` — reads its types from the
|
|
7
7
|
* resolved registration. No generics at call sites, no `schema` arg per call.
|
|
8
8
|
*
|
|
9
9
|
* Registration is done via **module augmentation** of `@abloatai/ablo` —
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
* interface Register {
|
|
23
23
|
* Schema: typeof schema;
|
|
24
24
|
* Presence: { cursor: { x: number; y: number } | null };
|
|
25
|
-
*
|
|
25
|
+
* Claims: { editLayer: { layerId: string } };
|
|
26
26
|
* UserMeta: { id: string; email: string };
|
|
27
27
|
* }
|
|
28
28
|
* }
|
package/dist/types/index.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* Foundational type definitions for the model-driven sync architecture.
|
|
5
5
|
* These types define how properties are tracked, loaded, and synchronized.
|
|
6
6
|
*/
|
|
7
|
+
import type { FieldMeta } from '../schema/field.js';
|
|
7
8
|
/**
|
|
8
9
|
* Model Scope - lifecycle filter for queries.
|
|
9
10
|
* Controls whether live, archived, or all entities are returned.
|
|
@@ -108,14 +109,15 @@ export interface ModelMetadata {
|
|
|
108
109
|
* JSON-typed values) inside the transaction queue.
|
|
109
110
|
*
|
|
110
111
|
* Populated by `registerModelsFromSchema`. Each entry carries the
|
|
111
|
-
* sync-engine type tag (
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
112
|
+
* sync-engine type tag (the canonical {@link FieldMeta.type} union),
|
|
113
|
+
* which tells the wire serializer how to handle the value. Missing →
|
|
114
|
+
* projection becomes identity pass-through (back-compat for models
|
|
115
|
+
* registered outside the schema path).
|
|
116
|
+
*
|
|
117
|
+
* Narrowed to the canonical union via `Pick` rather than re-declared —
|
|
118
|
+
* a hand-rolled copy silently drifts when a new field type lands.
|
|
115
119
|
*/
|
|
116
|
-
fields?: Readonly<Record<string,
|
|
117
|
-
type: 'string' | 'number' | 'boolean' | 'date' | 'enum' | 'json';
|
|
118
|
-
}>>;
|
|
120
|
+
fields?: Readonly<Record<string, Pick<FieldMeta, 'type'>>>;
|
|
119
121
|
/**
|
|
120
122
|
* Fields to back-fill from the sync client identity when missing
|
|
121
123
|
* during IndexedDB self-healing. Populated from
|
package/dist/types/index.js
CHANGED
|
@@ -65,6 +65,6 @@ export var MutationOperationType;
|
|
|
65
65
|
})(MutationOperationType || (MutationOperationType = {}));
|
|
66
66
|
// Re-export stream + snapshot + principal types for the engine surface
|
|
67
67
|
// (PresenceStream,
|
|
68
|
-
//
|
|
69
|
-
// `.
|
|
68
|
+
// ClaimStream, Snapshot, etc.) consumed by `Ablo({...}).presence`,
|
|
69
|
+
// `.claims`, `.snapshot()`.
|
|
70
70
|
export * from "./streams.js";
|