@abloatai/humans 0.59.2 → 0.61.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/README.md +1 -1
- package/dist/Ablo.d.ts +2 -10
- package/dist/Ablo.js +0 -1
- package/dist/client.d.ts +1 -48
- package/dist/humans.d.ts +1 -1
- package/dist/local/BaseSyncedStore.d.ts +5 -5
- package/dist/local/BaseSyncedStore.js +7 -7
- package/dist/local/Database.d.ts +2 -2
- package/dist/local/LazyReferenceCollection.d.ts +1 -1
- package/dist/local/Model.js +46 -56
- package/dist/local/NetworkMonitor.js +2 -0
- package/dist/local/RuntimeContext.js +2 -0
- package/dist/local/SyncClient.d.ts +12 -36
- package/dist/local/SyncClient.js +50 -108
- package/dist/local/client/createModelOperations.d.ts +3 -27
- package/dist/local/client/createModelOperations.js +20 -21
- package/dist/local/client/options.d.ts +14 -39
- package/dist/local/client/reactiveEngine.d.ts +3 -9
- package/dist/local/client/reactiveEngine.js +6 -151
- package/dist/local/client/storeLifecycle.js +5 -1
- package/dist/local/fileUploads.d.ts +27 -0
- package/dist/local/fileUploads.js +55 -0
- package/dist/local/storeContract.d.ts +5 -5
- package/dist/local/stores/syncAction.d.ts +4 -4
- package/dist/local/sync/contextOnChange.js +1 -1
- package/dist/local/sync/createClaimStream.js +1 -1
- package/dist/local/sync/credentialLifecycle.d.ts +4 -5
- package/dist/local/sync/credentialLifecycle.js +4 -5
- package/dist/local/sync/deltaPipeline.js +12 -6
- package/dist/local/sync/schemas.d.ts +10 -10
- package/dist/local/sync/scopeGroups.d.ts +11 -0
- package/dist/local/sync/scopeGroups.js +75 -0
- package/dist/local/sync/wsFrameHandlers.d.ts +1 -1
- package/dist/local/transactions/localMutation.js +3 -3
- package/dist/local/transactions/mutations/MutationQueue.d.ts +4 -5
- package/dist/local/transactions/mutations/MutationQueue.js +25 -51
- package/dist/local/transactions/mutations/batchProcessing.js +23 -10
- package/dist/local/transactions/mutations/commitPayload.d.ts +9 -2
- package/dist/local/transactions/mutations/commitTransport.js +3 -1
- package/dist/local/transactions/mutations/executionSelection.d.ts +0 -1
- package/dist/local/transactions/mutations/executionSelection.js +9 -17
- package/dist/local/transactions/mutations/failureHandling.js +9 -0
- package/dist/local/transactions/mutations/localMutation.js +3 -3
- package/dist/local/transactions/mutations/queueCoalescing.js +8 -0
- package/dist/local/transactions/mutations/replayValidation.d.ts +15 -15
- package/dist/react/AbloProvider.d.ts +11 -86
- package/dist/react/AbloProvider.js +11 -163
- package/dist/react/ClientSideSuspense.d.ts +1 -1
- package/dist/react/DefaultFallback.d.ts +1 -1
- package/dist/react/createAbloReact.js +1 -1
- package/dist/react/useErrorListener.js +1 -1
- package/dist/react/useMutationFailureListener.js +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js +1 -1
- package/dist/surface.d.ts +3 -3
- package/dist/surface.js +1 -4
- package/package.json +3 -3
- package/src/Ablo.ts +5 -17
- package/src/client.ts +0 -51
- package/src/local/BaseSyncedStore.ts +14 -14
- package/src/local/Model.ts +45 -55
- package/src/local/NetworkMonitor.ts +2 -0
- package/src/local/RuntimeContext.ts +2 -0
- package/src/local/SyncClient.ts +73 -140
- package/src/local/client/createModelOperations.ts +30 -64
- package/src/local/client/options.ts +20 -43
- package/src/local/client/reactiveEngine.ts +7 -179
- package/src/local/client/storeLifecycle.ts +6 -1
- package/src/local/fileUploads.ts +97 -0
- package/src/local/storeContract.ts +5 -5
- package/src/local/sync/contextOnChange.ts +1 -1
- package/src/local/sync/createClaimStream.ts +1 -1
- package/src/local/sync/credentialLifecycle.ts +4 -5
- package/src/local/sync/deltaPipeline.ts +10 -6
- package/src/local/sync/scopeGroups.ts +91 -0
- package/src/local/sync/wsFrameHandlers.ts +0 -1
- package/src/local/transactions/localMutation.ts +3 -3
- package/src/local/transactions/mutations/MutationQueue.ts +24 -53
- package/src/local/transactions/mutations/batchProcessing.ts +25 -10
- package/src/local/transactions/mutations/commitPayload.ts +11 -1
- package/src/local/transactions/mutations/commitTransport.ts +2 -2
- package/src/local/transactions/mutations/executionSelection.ts +9 -15
- package/src/local/transactions/mutations/failureHandling.ts +10 -0
- package/src/local/transactions/mutations/localMutation.ts +3 -3
- package/src/local/transactions/mutations/queueCoalescing.ts +6 -0
- package/src/react/AbloProvider.tsx +17 -249
- package/src/react/useErrorListener.ts +1 -1
- package/src/react/useMutationFailureListener.ts +1 -1
- package/src/react.ts +1 -5
- package/src/surface.ts +1 -4
- package/dist/local/sync/participants.d.ts +0 -132
- package/dist/local/sync/participants.js +0 -342
- package/dist/local/transactions/mutations/pendingDrain.d.ts +0 -33
- package/dist/local/transactions/mutations/pendingDrain.js +0 -117
- package/src/local/sync/participants.ts +0 -564
- package/src/local/transactions/mutations/pendingDrain.ts +0 -169
package/README.md
CHANGED
package/dist/Ablo.d.ts
CHANGED
|
@@ -50,11 +50,11 @@ export type Ablo<S extends SchemaRecord> = AbloClient<S>;
|
|
|
50
50
|
* ```
|
|
51
51
|
*
|
|
52
52
|
* In the browser (or any client that shouldn't hold a secret key), point
|
|
53
|
-
* `
|
|
53
|
+
* `session.endpoint` at your session-mint route instead — the SDK fetches it, keeps the
|
|
54
54
|
* short-lived token fresh, and re-mints on expiry:
|
|
55
55
|
*
|
|
56
56
|
* ```ts
|
|
57
|
-
* const ablo = Ablo({ schema,
|
|
57
|
+
* const ablo = Ablo({ schema, session: { endpoint: '/api/ablo-session' } });
|
|
58
58
|
* ```
|
|
59
59
|
*
|
|
60
60
|
* Server-side agents, workers, and services use `@abloatai/transaction`.
|
|
@@ -64,7 +64,6 @@ export declare function Ablo<const S extends SchemaRecord, const P extends reado
|
|
|
64
64
|
}): Ablo<S> & MergedSurface<P>;
|
|
65
65
|
export declare function Ablo<const S extends SchemaRecord>(options: AbloOptions<S>): Ablo<S>;
|
|
66
66
|
import type * as _Streams from '@abloatai/transaction/types/streams';
|
|
67
|
-
import type * as _Participants from './local/sync/participants.js';
|
|
68
67
|
import type * as _Mutators from './local/mutators/defineMutators.js';
|
|
69
68
|
import type * as _Tx from './local/mutators/Transaction.js';
|
|
70
69
|
import type * as _Undo from './local/mutators/UndoManager.js';
|
|
@@ -110,13 +109,6 @@ export declare namespace Ablo {
|
|
|
110
109
|
namespace Auth {
|
|
111
110
|
type Actor = _Streams.ParticipantRef;
|
|
112
111
|
}
|
|
113
|
-
namespace Participant {
|
|
114
|
-
type Manager = _Participants.ParticipantManager;
|
|
115
|
-
type Joined = _Participants.JoinedParticipant;
|
|
116
|
-
type Scope = _Participants.ParticipantScope;
|
|
117
|
-
type Status = _Participants.ParticipantStatus;
|
|
118
|
-
type JoinOptions = _Participants.ParticipantJoinOptions;
|
|
119
|
-
}
|
|
120
112
|
type Schema<S extends _SchemaTypes.SchemaRecord = _SchemaTypes.SchemaRecord> = _SchemaTypes.Schema<S>;
|
|
121
113
|
/**
|
|
122
114
|
* The schema this program has registered via `interface Register { Schema }`
|
package/dist/Ablo.js
CHANGED
package/dist/client.d.ts
CHANGED
|
@@ -17,7 +17,7 @@ import type { SyncStoreContract } from './react/context.js';
|
|
|
17
17
|
import type { SyncWebSocket, CoreSyncEventMap } from './local/sync/SyncWebSocket.js';
|
|
18
18
|
import type { SyncStatus } from './local/BaseSyncedStore.js';
|
|
19
19
|
import type { ModelOperations } from './local/client/createModelOperations.js';
|
|
20
|
-
import type { ClaimResource, CommitResource
|
|
20
|
+
import type { ClaimResource, CommitResource } from '@abloatai/transaction/client/resources/httpResources';
|
|
21
21
|
import type { EffectiveAuthority } from '@abloatai/transaction/auth';
|
|
22
22
|
import type { ReadDependency } from '@abloatai/transaction/coordination';
|
|
23
23
|
import type { CapturedRow } from '@abloatai/transaction/transport/http';
|
|
@@ -102,53 +102,6 @@ export type AbloClient<S extends SchemaRecord> = {
|
|
|
102
102
|
* the watchdog.
|
|
103
103
|
*/
|
|
104
104
|
nudgeReconnect(): void;
|
|
105
|
-
/**
|
|
106
|
-
* Mint a short-lived, scoped session token for one end user. Call this on your
|
|
107
|
-
* backend, where the `sk_` secret key lives, then hand the returned `token` to
|
|
108
|
-
* that user's browser — typically through a token route the browser's `apiKey`
|
|
109
|
-
* resolver fetches. The browser presents the token as its bearer, and the
|
|
110
|
-
* server verifies it. The browser must never see the `sk_` key, only the
|
|
111
|
-
* per-user session token.
|
|
112
|
-
*
|
|
113
|
-
* Pass `{ user: { id }, can: { items: ['read', 'update'] } }` for an end-user
|
|
114
|
-
* session. It mints an `ek_` and attributes writes to a user (recorded as
|
|
115
|
-
* `actor_kind` on the delta row). Pass `{ agent: { id }, can: {
|
|
116
|
-
* items: ['update'] } }` for a scoped agent session, which mints an `rk_`.
|
|
117
|
-
* Both kinds require `can`, typed against your schema's model names. This
|
|
118
|
-
* always authenticates with the original `sk_`, never the client's exchanged
|
|
119
|
-
* sync credential.
|
|
120
|
-
*/
|
|
121
|
-
sessions: SessionResource<S>;
|
|
122
|
-
/**
|
|
123
|
-
* Mint a scoped **agent identity** and return a ready-to-use client bound to
|
|
124
|
-
* it — the `ablo.<resource>.<verb>` shape for the agent use case. One call
|
|
125
|
-
* replaces `sessions.create({ agent, can })` + constructing a second
|
|
126
|
-
* `Ablo({ apiKey: token })`:
|
|
127
|
-
*
|
|
128
|
-
* ```ts
|
|
129
|
-
* const agent = await ablo.agents.create({
|
|
130
|
-
* name: 'researcher', // readable label (optional)
|
|
131
|
-
* can: { records: ['read', 'update'] },
|
|
132
|
-
* // id omitted → a fresh uuid: a distinct, independent participant
|
|
133
|
-
* });
|
|
134
|
-
* await agent.records.update({ id, data, claim });
|
|
135
|
-
* await agent.dispose(); // when the agent is done
|
|
136
|
-
* ```
|
|
137
|
-
*
|
|
138
|
-
* Server-side only: it requires the `sk_` secret key (like `sessions.create`)
|
|
139
|
-
* and throws `AbloAuthenticationError` in the browser. The returned client
|
|
140
|
-
* holds its own auto-refreshing `rk_`, so a long run never hits token expiry,
|
|
141
|
-
* and the `sk_` never leaves this process. Each call is a distinct participant
|
|
142
|
-
* by default (omit `id` for a fresh uuid), so even two agents sharing a `name`
|
|
143
|
-
* queue behind one another on a contended row — `name` is display only and
|
|
144
|
-
* never collapses identity. Humans don't get a server-built client; ship them a
|
|
145
|
-
* token via `sessions.create({ user, can })`. If you need the raw token for
|
|
146
|
-
* revocation, or a stable re-attachable id, use `sessions.create({ agent, can })`
|
|
147
|
-
* or pass `id`.
|
|
148
|
-
*/
|
|
149
|
-
agents: {
|
|
150
|
-
create(params: CreateAgentClientParams<S>): Promise<AbloClient<S>>;
|
|
151
|
-
};
|
|
152
105
|
/**
|
|
153
106
|
* The organization this client resolved to — `null` until `ready()`
|
|
154
107
|
* completes. Use it instead of scraping CLI output or hardcoding env vars:
|
package/dist/humans.d.ts
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
import type { RecoveryClass } from '@abloatai/transaction/errorCodes';
|
|
14
14
|
import { ConnectionManager } from './sync/ConnectionManager.js';
|
|
15
15
|
import { SubscriptionManager } from './sync/SubscriptionManager.js';
|
|
16
|
-
import { type
|
|
16
|
+
import { type GroupScope } from './sync/scopeGroups.js';
|
|
17
17
|
import type { SyncClient } from './SyncClient.js';
|
|
18
18
|
import type { Database, BootstrapResult, BootstrapRequirements } from './Database.js';
|
|
19
19
|
import type { InstanceCache } from './InstanceCache.js';
|
|
@@ -254,7 +254,7 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
254
254
|
* Hydration is best-effort — a failed backfill never rejects `enterScope`,
|
|
255
255
|
* and the live delta stream keeps flowing regardless.
|
|
256
256
|
*/
|
|
257
|
-
enterScope(scope:
|
|
257
|
+
enterScope(scope: GroupScope, opts?: {
|
|
258
258
|
hydrate?: boolean;
|
|
259
259
|
}): Promise<void>;
|
|
260
260
|
/**
|
|
@@ -266,11 +266,11 @@ export declare class BaseSyncedStore<TCollaboration extends EventMap<TCollaborat
|
|
|
266
266
|
*/
|
|
267
267
|
protected hydrateGroups(syncGroups: readonly string[]): Promise<void>;
|
|
268
268
|
/** Leave a scope → its groups go warm (hysteresis), then drop on sweep. */
|
|
269
|
-
leaveScope(scope:
|
|
269
|
+
leaveScope(scope: GroupScope): Promise<void>;
|
|
270
270
|
/** Pin a scope (active claim / prominence) → never warms while pinned. */
|
|
271
|
-
pinScope(scope:
|
|
271
|
+
pinScope(scope: GroupScope): Promise<void>;
|
|
272
272
|
/** Release a pin → the group transitions to warm rather than dropping. */
|
|
273
|
-
unpinScope(scope:
|
|
273
|
+
unpinScope(scope: GroupScope): Promise<void>;
|
|
274
274
|
protected readonly queryProcessor: QueryProcessor;
|
|
275
275
|
/**
|
|
276
276
|
* Runtime behavior flags only — the schema/config arrays
|
|
@@ -15,7 +15,7 @@ import { AbloConnectionError, AbloValidationError, toAbloError } from '@abloatai
|
|
|
15
15
|
import { ConnectionManager } from './sync/ConnectionManager.js';
|
|
16
16
|
import { contextLogger, contextSocketObservability } from './sync/contextPorts.js';
|
|
17
17
|
import { SubscriptionManager } from './sync/SubscriptionManager.js';
|
|
18
|
-
import {
|
|
18
|
+
import { resolveScopeGroups, } from './sync/scopeGroups.js';
|
|
19
19
|
import { ModelRegistry } from './ModelRegistry.js';
|
|
20
20
|
import { PropertyType } from '@abloatai/transaction/types';
|
|
21
21
|
import { SyncWebSocket, } from './sync/SyncWebSocket.js';
|
|
@@ -180,7 +180,7 @@ export class BaseSyncedStore {
|
|
|
180
180
|
// {@link SubscriptionManager.reconcile}); the on-connect `resync` pushes
|
|
181
181
|
// whatever interest accumulated.
|
|
182
182
|
scopeToGroups(scope) {
|
|
183
|
-
return
|
|
183
|
+
return resolveScopeGroups(scope, this.schema);
|
|
184
184
|
}
|
|
185
185
|
/**
|
|
186
186
|
* Bring a scope into view and subscribe to its sync groups. With
|
|
@@ -1334,10 +1334,10 @@ export class BaseSyncedStore {
|
|
|
1334
1334
|
const isCreate = !this.objectPool.get(model.id);
|
|
1335
1335
|
if (isCreate) {
|
|
1336
1336
|
model.updatedAt = new Date();
|
|
1337
|
-
this.syncClient.add(model);
|
|
1337
|
+
await this.syncClient.add(model);
|
|
1338
1338
|
}
|
|
1339
1339
|
else {
|
|
1340
|
-
this.syncClient.update(model);
|
|
1340
|
+
await this.syncClient.update(model);
|
|
1341
1341
|
}
|
|
1342
1342
|
}
|
|
1343
1343
|
/** Save with an atomic server mutation (e.g., createSectionWithBlocks) */
|
|
@@ -1350,19 +1350,19 @@ export class BaseSyncedStore {
|
|
|
1350
1350
|
const model = rowAsModel(entity);
|
|
1351
1351
|
this.pendingDeletes.add(model.id);
|
|
1352
1352
|
// SyncClient.delete handles: pool remove, transaction queue
|
|
1353
|
-
this.syncClient.delete(model);
|
|
1353
|
+
await this.syncClient.delete(model);
|
|
1354
1354
|
}
|
|
1355
1355
|
/** Archive a model. Accepts schema-inferred entity shapes (see `save`). */
|
|
1356
1356
|
async archive(entity) {
|
|
1357
1357
|
const model = rowAsModel(entity);
|
|
1358
1358
|
model.archivedAt = new Date();
|
|
1359
|
-
this.syncClient.archive(model);
|
|
1359
|
+
await this.syncClient.archive(model);
|
|
1360
1360
|
}
|
|
1361
1361
|
/** Unarchive a model. Accepts schema-inferred entity shapes (see `save`). */
|
|
1362
1362
|
async unarchive(entity) {
|
|
1363
1363
|
const model = rowAsModel(entity);
|
|
1364
1364
|
model.archivedAt = null;
|
|
1365
|
-
this.syncClient.update(model);
|
|
1365
|
+
await this.syncClient.update(model);
|
|
1366
1366
|
}
|
|
1367
1367
|
// ── Query API ────────────────────────────────────────────────────────────
|
|
1368
1368
|
// `ablo.<model>.local.get` / `.local.list` is the read surface for
|
package/dist/local/Database.d.ts
CHANGED
|
@@ -101,7 +101,7 @@ export declare class Database {
|
|
|
101
101
|
* where a missing store points to silent data loss. Callers that
|
|
102
102
|
* already expect optional behavior (e.g. lazy lookups) can omit it.
|
|
103
103
|
*/
|
|
104
|
-
getStore(modelName: string, context?: string):
|
|
104
|
+
getStore(modelName: string, context?: string): import("./stores/ObjectStore.js").ObjectStore | InMemoryObjectStore | undefined;
|
|
105
105
|
/** Get store or throw if not found (for operations that require the store). */
|
|
106
106
|
private getRequiredStore;
|
|
107
107
|
/** Log preserved fields during partial UPDATE merge (debug helper) */
|
|
@@ -274,7 +274,7 @@ export declare class Database {
|
|
|
274
274
|
* `getStore(modelName, context?)` is defined near the top of this
|
|
275
275
|
* class — single accessor for both inMemory and IDB modes.
|
|
276
276
|
*/
|
|
277
|
-
getAllStores(): Map<string,
|
|
277
|
+
getAllStores(): Map<string, import("./stores/ObjectStore.js").ObjectStore> | Map<string, InMemoryObjectStore>;
|
|
278
278
|
/**
|
|
279
279
|
* Model persistence tracking
|
|
280
280
|
*/
|
|
@@ -65,7 +65,7 @@ export declare class LazyReferenceCollection<T extends Model> {
|
|
|
65
65
|
private get database();
|
|
66
66
|
/** Get objectPool from static dependencies */
|
|
67
67
|
private get objectPool();
|
|
68
|
-
constructor(modelName: string, parent: Model, foreignKey: string, customQuery?: any, options?: LazyCollectionOptions);
|
|
68
|
+
constructor(modelName: string, parent: Model, foreignKey: string, customQuery?: any | undefined, options?: LazyCollectionOptions);
|
|
69
69
|
/**
|
|
70
70
|
* Set up MobX observation lifecycle hooks
|
|
71
71
|
* When React components observe this collection, we prevent GC of the parent model
|
package/dist/local/Model.js
CHANGED
|
@@ -311,7 +311,7 @@ export class Model {
|
|
|
311
311
|
for (const key of keys) {
|
|
312
312
|
if (key === 'id')
|
|
313
313
|
continue;
|
|
314
|
-
const mod = modified
|
|
314
|
+
const mod = modified.get(key);
|
|
315
315
|
if (mod) {
|
|
316
316
|
out[key] = mod.old;
|
|
317
317
|
}
|
|
@@ -358,25 +358,23 @@ export class Model {
|
|
|
358
358
|
const errors = [];
|
|
359
359
|
const modelName = this.getModelName();
|
|
360
360
|
const properties = getActiveRegistry().getProperties(modelName);
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
errors.push(`${propName} is required`);
|
|
369
|
-
}
|
|
361
|
+
const json = this.toJSON();
|
|
362
|
+
for (const [propName, metadata] of properties) {
|
|
363
|
+
// Check required fields
|
|
364
|
+
if (!metadata.nullable && !metadata.optional) {
|
|
365
|
+
const value = json[propName];
|
|
366
|
+
if (value == null || value === '') {
|
|
367
|
+
errors.push(`${propName} is required`);
|
|
370
368
|
}
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
369
|
+
}
|
|
370
|
+
// Run custom validation rules
|
|
371
|
+
const rules = this.validationRules[propName];
|
|
372
|
+
if (rules) {
|
|
373
|
+
const value = json[propName];
|
|
374
|
+
for (const rule of rules) {
|
|
375
|
+
const error = rule(value);
|
|
376
|
+
if (error)
|
|
377
|
+
errors.push(error);
|
|
380
378
|
}
|
|
381
379
|
}
|
|
382
380
|
}
|
|
@@ -679,20 +677,18 @@ export class Model {
|
|
|
679
677
|
if (this.archivedAt !== undefined) {
|
|
680
678
|
result.archivedAt = this.archivedAt?.toISOString() ?? null;
|
|
681
679
|
}
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
result[propName] = value;
|
|
695
|
-
}
|
|
680
|
+
const self = this;
|
|
681
|
+
for (const [propName, metadata] of properties) {
|
|
682
|
+
// Skip certain types
|
|
683
|
+
if (metadata.type === 'ephemeralProperty')
|
|
684
|
+
continue;
|
|
685
|
+
if (metadata.type === 'referenceModel')
|
|
686
|
+
continue;
|
|
687
|
+
if (metadata.type === 'referenceCollection')
|
|
688
|
+
continue;
|
|
689
|
+
const value = self[propName];
|
|
690
|
+
if (value !== undefined) {
|
|
691
|
+
result[propName] = value;
|
|
696
692
|
}
|
|
697
693
|
}
|
|
698
694
|
return result;
|
|
@@ -793,14 +789,12 @@ export class Model {
|
|
|
793
789
|
if (hasActiveRegistry()) {
|
|
794
790
|
const modelName = this.getModelName();
|
|
795
791
|
const properties = getActiveRegistry().getProperties(modelName);
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
collection.dispose();
|
|
803
|
-
}
|
|
792
|
+
const self = this;
|
|
793
|
+
for (const [propName, metadata] of properties) {
|
|
794
|
+
if (metadata.type === 'referenceCollection') {
|
|
795
|
+
const collection = self[propName];
|
|
796
|
+
if (collection) {
|
|
797
|
+
collection.dispose();
|
|
804
798
|
}
|
|
805
799
|
}
|
|
806
800
|
}
|
|
@@ -825,11 +819,9 @@ export class Model {
|
|
|
825
819
|
const snapshot = {};
|
|
826
820
|
const modelName = this.getModelName();
|
|
827
821
|
const properties = getActiveRegistry().getProperties(modelName);
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
snapshot[propName] = json[propName];
|
|
832
|
-
}
|
|
822
|
+
const json = this.toJSON();
|
|
823
|
+
for (const [propName] of properties) {
|
|
824
|
+
snapshot[propName] = json[propName];
|
|
833
825
|
}
|
|
834
826
|
return snapshot;
|
|
835
827
|
}
|
|
@@ -874,17 +866,15 @@ export class Model {
|
|
|
874
866
|
snapshot.archivedAt = this.archivedAt;
|
|
875
867
|
const properties = getActiveRegistry().getProperties(this.getModelName());
|
|
876
868
|
const self = this;
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
continue;
|
|
883
|
-
}
|
|
884
|
-
// Reading through the observable getter is the point: it subscribes the
|
|
885
|
-
// enclosing MobX reaction to this field.
|
|
886
|
-
snapshot[propName] = self[propName];
|
|
869
|
+
for (const [propName, metadata] of properties) {
|
|
870
|
+
if (metadata.type === 'ephemeralProperty' ||
|
|
871
|
+
metadata.type === 'referenceModel' ||
|
|
872
|
+
metadata.type === 'referenceCollection') {
|
|
873
|
+
continue;
|
|
887
874
|
}
|
|
875
|
+
// Reading through the observable getter is the point: it subscribes the
|
|
876
|
+
// enclosing MobX reaction to this field.
|
|
877
|
+
snapshot[propName] = self[propName];
|
|
888
878
|
}
|
|
889
879
|
for (const name of this.getDerivedGetterNames()) {
|
|
890
880
|
Object.defineProperty(snapshot, name, {
|
|
@@ -12,6 +12,8 @@ export class NetworkMonitor extends EventEmitter {
|
|
|
12
12
|
// Only `navigator.onLine === false` means offline. Node 18+ exposes a global
|
|
13
13
|
// `navigator` with `onLine === undefined`, so the naive `navigator.onLine`
|
|
14
14
|
// would seed `false` (offline) on every server client — start optimistic.
|
|
15
|
+
// DOM types say `onLine` is boolean, but Node exposes it as undefined.
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare
|
|
15
17
|
isOnline = !(typeof navigator !== 'undefined' && navigator.onLine === false);
|
|
16
18
|
lastOnlineCheck = new Date();
|
|
17
19
|
constructor(runtime = globalRuntime) {
|
|
@@ -47,6 +47,8 @@ export const browserOnlineStatus = {
|
|
|
47
47
|
// signal. Don't use `!navigator.onLine`: Node 18+ exposes a global
|
|
48
48
|
// `navigator` whose `onLine` is `undefined`, which `!` would read as offline —
|
|
49
49
|
// wedging every Node/server client (agents, worker, MCP) into a false offline.
|
|
50
|
+
// DOM types say `onLine` is boolean, but Node exposes it as undefined.
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-unnecessary-boolean-literal-compare
|
|
50
52
|
return !(typeof navigator !== 'undefined' && navigator.onLine === false);
|
|
51
53
|
},
|
|
52
54
|
};
|
|
@@ -21,6 +21,7 @@ import type { WriteOptions } from './interfaces/index.js';
|
|
|
21
21
|
import { LogPosition } from './logPosition.js';
|
|
22
22
|
import { type RehydrationStats, type SyncObserver, type SyncState } from './syncClientTypes.js';
|
|
23
23
|
import type { BootstrapSnapshot } from './syncClientTypes.js';
|
|
24
|
+
import { type BatchFileUploadOptions, type FileUploadOptions } from './fileUploads.js';
|
|
24
25
|
export type { BootstrapSnapshot, RehydrationStats } from './syncClientTypes.js';
|
|
25
26
|
export declare class SyncClient extends EventEmitter {
|
|
26
27
|
private readonly runtime;
|
|
@@ -186,9 +187,9 @@ export declare class SyncClient extends EventEmitter {
|
|
|
186
187
|
*/
|
|
187
188
|
private captureModelChanges;
|
|
188
189
|
/** Add new model (CREATE) - works offline */
|
|
189
|
-
add(model: Model, options?: WriteOptions): void;
|
|
190
|
+
add(model: Model, options?: WriteOptions): Promise<void> | undefined;
|
|
190
191
|
/** Update existing model (UPDATE) - works offline */
|
|
191
|
-
update(model: Model, options?: WriteOptions): void;
|
|
192
|
+
update(model: Model, options?: WriteOptions, capturedChanges?: Record<string, unknown>): Promise<void> | undefined;
|
|
192
193
|
/**
|
|
193
194
|
* Update existing model with pre-computed changes.
|
|
194
195
|
* Used by saveManyOptimized when incoming models have empty change-tracking
|
|
@@ -200,37 +201,12 @@ export declare class SyncClient extends EventEmitter {
|
|
|
200
201
|
* but still need optimistic pool updates at the sync layer. */
|
|
201
202
|
get gql(): import("./interfaces/index.js").MutationExecutor;
|
|
202
203
|
/** Delete model (DELETE) - works offline */
|
|
203
|
-
delete(model: Model, options?: WriteOptions): void;
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
* response and added to the pool.
|
|
208
|
-
*/
|
|
209
|
-
uploadFile(file: File, options: {
|
|
210
|
-
id: string;
|
|
211
|
-
attachableType: string;
|
|
212
|
-
attachableId: string;
|
|
213
|
-
metadata?: Record<string, unknown>;
|
|
214
|
-
}): Promise<Model | null>;
|
|
215
|
-
/**
|
|
216
|
-
* Batch upload files — single GraphQL call + parallel S3 PUTs.
|
|
217
|
-
*
|
|
218
|
-
* Returns the raw `Model[]` built by the object pool (typename is
|
|
219
|
-
* determined by the payload the server returns — currently always
|
|
220
|
-
* `Attachment`). The SDK has no knowledge of app-specific model classes,
|
|
221
|
-
* so it cannot honestly claim a narrower return type; consumers that
|
|
222
|
-
* need an `Attachment[]` project through their own typed accessor
|
|
223
|
-
* (e.g. `store.query.attachments.findMany({ where: { id: IN ids } })`)
|
|
224
|
-
* after the upload resolves.
|
|
225
|
-
*/
|
|
226
|
-
batchUploadFiles(files: File[], options: {
|
|
227
|
-
ids: string[];
|
|
228
|
-
attachableType: string;
|
|
229
|
-
attachableId: string;
|
|
230
|
-
metadata?: Record<string, unknown>;
|
|
231
|
-
}): Promise<Model[]>;
|
|
204
|
+
delete(model: Model, options?: WriteOptions): Promise<void> | undefined;
|
|
205
|
+
private fileUploadContext;
|
|
206
|
+
uploadFile(file: File, options: FileUploadOptions): Promise<Model | null>;
|
|
207
|
+
batchUploadFiles(files: File[], options: BatchFileUploadOptions): Promise<Model[]>;
|
|
232
208
|
/** Archive model (ARCHIVE) - works offline */
|
|
233
|
-
archive(model: Model): void;
|
|
209
|
+
archive(model: Model): Promise<void> | undefined;
|
|
234
210
|
/**
|
|
235
211
|
* Append a mutation to the pending queue and schedule its sync work.
|
|
236
212
|
*
|
|
@@ -409,14 +385,14 @@ export declare class SyncClient extends EventEmitter {
|
|
|
409
385
|
* Get detailed debug info for the sync debug page
|
|
410
386
|
*/
|
|
411
387
|
getDebugInfo(): {
|
|
412
|
-
connectionState: "connected" | "
|
|
388
|
+
connectionState: "connected" | "disconnected" | "connecting";
|
|
413
389
|
pendingMutationsCount: number;
|
|
414
390
|
mutationQueue: {
|
|
415
391
|
lastSeenSyncId: number;
|
|
416
392
|
awaitingDeltaCount: number;
|
|
417
393
|
awaitingDeltaTransactions: {
|
|
418
394
|
id: string;
|
|
419
|
-
type: "
|
|
395
|
+
type: "update" | "create" | "delete" | "archive" | "unarchive";
|
|
420
396
|
modelName: string;
|
|
421
397
|
modelId: string;
|
|
422
398
|
syncIdNeeded: number | undefined;
|
|
@@ -425,13 +401,13 @@ export declare class SyncClient extends EventEmitter {
|
|
|
425
401
|
}[];
|
|
426
402
|
pendingTransactions: {
|
|
427
403
|
id: string;
|
|
428
|
-
type: "
|
|
404
|
+
type: "update" | "create" | "delete" | "archive" | "unarchive";
|
|
429
405
|
modelName: string;
|
|
430
406
|
modelId: string;
|
|
431
407
|
}[];
|
|
432
408
|
executingTransactions: {
|
|
433
409
|
id: string;
|
|
434
|
-
type: "
|
|
410
|
+
type: "update" | "create" | "delete" | "archive" | "unarchive";
|
|
435
411
|
modelName: string;
|
|
436
412
|
modelId: string;
|
|
437
413
|
}[];
|