@abloatai/transaction 0.39.0 → 0.40.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.
Files changed (95) hide show
  1. package/dist/coordination/events.d.ts +9 -0
  2. package/dist/coordination/events.d.ts.map +1 -1
  3. package/dist/coordination/index.d.ts +2 -2
  4. package/dist/coordination/index.d.ts.map +1 -1
  5. package/dist/coordination/index.js +1 -1
  6. package/dist/coordination/index.js.map +1 -1
  7. package/dist/coordination/schema.d.ts +226 -34
  8. package/dist/coordination/schema.d.ts.map +1 -1
  9. package/dist/coordination/schema.js +180 -48
  10. package/dist/coordination/schema.js.map +1 -1
  11. package/dist/coordination/trace.d.ts.map +1 -1
  12. package/dist/coordination/trace.js +9 -1
  13. package/dist/coordination/trace.js.map +1 -1
  14. package/dist/errorCodes.d.ts +5 -0
  15. package/dist/errorCodes.d.ts.map +1 -1
  16. package/dist/errorCodes.js +5 -0
  17. package/dist/errorCodes.js.map +1 -1
  18. package/dist/errors.d.ts.map +1 -1
  19. package/dist/errors.js +9 -2
  20. package/dist/errors.js.map +1 -1
  21. package/dist/index.d.ts +1 -0
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +3 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/policy/types.d.ts +10 -2
  26. package/dist/policy/types.d.ts.map +1 -1
  27. package/dist/policy/types.js.map +1 -1
  28. package/dist/pricing.d.ts +194 -0
  29. package/dist/pricing.d.ts.map +1 -0
  30. package/dist/pricing.js +330 -0
  31. package/dist/pricing.js.map +1 -0
  32. package/dist/resources/functionalUpdate.d.ts.map +1 -1
  33. package/dist/resources/functionalUpdate.js +8 -8
  34. package/dist/resources/functionalUpdate.js.map +1 -1
  35. package/dist/schema/index.d.ts +1 -1
  36. package/dist/schema/index.d.ts.map +1 -1
  37. package/dist/schema/index.js +1 -1
  38. package/dist/schema/index.js.map +1 -1
  39. package/dist/schema/roles.d.ts +19 -0
  40. package/dist/schema/roles.d.ts.map +1 -1
  41. package/dist/schema/roles.js +23 -3
  42. package/dist/schema/roles.js.map +1 -1
  43. package/dist/schema/schema.d.ts +1 -1
  44. package/dist/schema/schema.d.ts.map +1 -1
  45. package/dist/schema/schema.js +1 -1
  46. package/dist/schema/schema.js.map +1 -1
  47. package/dist/schema/tenancy.d.ts +1 -1
  48. package/dist/transactions/settlement/commitEnvelope.d.ts +10 -2
  49. package/dist/transactions/settlement/commitEnvelope.d.ts.map +1 -1
  50. package/dist/transactions/settlement/pendingWrite.d.ts +10 -2
  51. package/dist/transactions/settlement/pendingWrite.d.ts.map +1 -1
  52. package/dist/transport/wsFrameHandlers.d.ts.map +1 -1
  53. package/dist/transport/wsFrameHandlers.js +9 -3
  54. package/dist/transport/wsFrameHandlers.js.map +1 -1
  55. package/dist/wire/accountResponses.d.ts +100 -0
  56. package/dist/wire/accountResponses.d.ts.map +1 -1
  57. package/dist/wire/accountResponses.js +70 -0
  58. package/dist/wire/accountResponses.js.map +1 -1
  59. package/dist/wire/commit.d.ts +708 -100
  60. package/dist/wire/commit.d.ts.map +1 -1
  61. package/dist/wire/dataSourceResponses.d.ts +171 -0
  62. package/dist/wire/dataSourceResponses.d.ts.map +1 -0
  63. package/dist/wire/dataSourceResponses.js +168 -0
  64. package/dist/wire/dataSourceResponses.js.map +1 -0
  65. package/dist/wire/frames.d.ts +20 -4
  66. package/dist/wire/frames.d.ts.map +1 -1
  67. package/dist/wire/inboundFrames.d.ts +102 -14
  68. package/dist/wire/inboundFrames.d.ts.map +1 -1
  69. package/dist/wire/index.d.ts +4 -2
  70. package/dist/wire/index.d.ts.map +1 -1
  71. package/dist/wire/index.js +5 -1
  72. package/dist/wire/index.js.map +1 -1
  73. package/dist/wire/pricing.d.ts +43 -0
  74. package/dist/wire/pricing.d.ts.map +1 -0
  75. package/dist/wire/pricing.js +36 -0
  76. package/dist/wire/pricing.js.map +1 -0
  77. package/package.json +6 -1
  78. package/src/coordination/events.ts +9 -0
  79. package/src/coordination/index.ts +8 -0
  80. package/src/coordination/schema.ts +200 -51
  81. package/src/coordination/trace.ts +9 -1
  82. package/src/errorCodes.ts +20 -0
  83. package/src/errors.ts +9 -2
  84. package/src/index.ts +4 -0
  85. package/src/policy/types.ts +10 -2
  86. package/src/pricing.ts +350 -0
  87. package/src/resources/functionalUpdate.ts +11 -11
  88. package/src/schema/index.ts +2 -0
  89. package/src/schema/roles.ts +26 -3
  90. package/src/schema/schema.ts +2 -0
  91. package/src/transport/wsFrameHandlers.ts +9 -3
  92. package/src/wire/accountResponses.ts +76 -0
  93. package/src/wire/dataSourceResponses.ts +188 -0
  94. package/src/wire/index.ts +33 -0
  95. package/src/wire/pricing.ts +41 -0
@@ -74,7 +74,11 @@ export { fieldTypeSchema, fieldMetaSchema, relationTypeSchema, relationMetaSchem
74
74
  export { modelMutationRequestSchema } from './modelMutations.js';
75
75
  // The account routes' responses — projects, the deployed schema, the commit
76
76
  // log, usage. What the server, the CLI, and the MCP server agree on.
77
- export { projectResponseSchema, projectListResponseSchema, provisionedKeySchema, provisionKeyResponseSchema, conflictAxisWireSchema, schemaModelResponseSchema, schemaReadResponseSchema, logOpSchema, LOG_OP_BY_ACTION, logEventSchema, usageBucketSchema, usageReportResponseSchema, controlKeySchema, controlKeyListResponseSchema, keyMintedResponseSchema, keyRevokedResponseSchema, } from './accountResponses.js';
77
+ export { projectResponseSchema, projectListResponseSchema, provisionedKeySchema, provisionKeyResponseSchema, conflictAxisWireSchema, schemaModelResponseSchema, schemaReadResponseSchema, logOpSchema, LOG_OP_BY_ACTION, logEventSchema, usageBucketSchema, usageReportResponseSchema, meterUsageSchema, billingSummarySchema, usageSummaryResponseSchema, controlKeySchema, controlKeyListResponseSchema, keyMintedResponseSchema, keyRevokedResponseSchema, } from './accountResponses.js';
78
+ // The datasource routes' responses — the `ablo connect` surface: register,
79
+ // validate, locate, list, deregister. What the server, the CLI, and the
80
+ // dashboard agree on.
81
+ export { READINESS_ITEMS, READINESS_ADVISORY_ITEMS, isReadinessItem, readinessFailureSchema, readinessAdvisorySchema, datasourceSummarySchema, datasourceListResponseSchema, datasourceValidationResponseSchema, datasourceLocationResponseSchema, datasourceDisconnectedResponseSchema, } from './dataSourceResponses.js';
78
82
  // The inbound socket surface: every frame the server can send, and how each
79
83
  // one's payload is validated.
80
84
  export { WS_INBOUND_FRAMES, wsInboundEnvelopeSchema, isKnownInboundFrame, isSchemaValidatedFrame, } from './inboundFrames.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wire/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/wire/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * The pricing vocabulary that crosses the wire: the tier names, the meter
3
+ * names, and the shape of one rate-card step. Account responses carry these,
4
+ * so they live in the dependency-free wire layer; the pricing contract
5
+ * (`../pricing.ts`) derives the rate card, the plans, and the bill arithmetic
6
+ * from them and re-exports them for its consumers.
7
+ */
8
+ import { z } from 'zod';
9
+ export declare const planTierSchema: z.ZodEnum<{
10
+ free: "free";
11
+ scale: "scale";
12
+ enterprise: "enterprise";
13
+ }>;
14
+ export type PlanTier = z.infer<typeof planTierSchema>;
15
+ /**
16
+ * Every meter the engine records, over any transport. This is the closed set
17
+ * behind the loose `eventName` strings that `UsageRecorder` and the quota gate
18
+ * pass around.
19
+ *
20
+ * Recording and pricing are separate questions. A meter exists so usage can be
21
+ * gated and attributed; whether it reaches an invoice is `METER_EVENT_AXIS`.
22
+ * Bootstraps are recorded and quota-gated but deliberately unpriced: a bootstrap
23
+ * is what a client does once to become useful, and charging for it would price
24
+ * the act of connecting.
25
+ */
26
+ export declare const meterEventSchema: z.ZodEnum<{
27
+ "api.commit_ops": "api.commit_ops";
28
+ "api.model_reads": "api.model_reads";
29
+ "api.claim_creates": "api.claim_creates";
30
+ "api.bootstraps": "api.bootstraps";
31
+ }>;
32
+ export type MeterEvent = z.infer<typeof meterEventSchema>;
33
+ /**
34
+ * One step of the rate card. `throughOps` is the cumulative ceiling the rate
35
+ * applies up to, and `null` marks the final, unbounded step. Rates are marginal:
36
+ * crossing a ceiling reprices the operations above it, never the ones below.
37
+ */
38
+ export declare const rateBracketSchema: z.ZodObject<{
39
+ throughOps: z.ZodNullable<z.ZodNumber>;
40
+ usdPerMillionOps: z.ZodNumber;
41
+ }, z.core.$strip>;
42
+ export type RateBracket = z.infer<typeof rateBracketSchema>;
43
+ //# sourceMappingURL=pricing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.d.ts","sourceRoot":"","sources":["../../src/wire/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,eAAO,MAAM,cAAc;;;;EAA0C,CAAC;AACtE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC;AAEtD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB;;;;;EAK3B,CAAC;AACH,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAE1D;;;;GAIG;AACH,eAAO,MAAM,iBAAiB;;;iBAG5B,CAAC;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * The pricing vocabulary that crosses the wire: the tier names, the meter
3
+ * names, and the shape of one rate-card step. Account responses carry these,
4
+ * so they live in the dependency-free wire layer; the pricing contract
5
+ * (`../pricing.ts`) derives the rate card, the plans, and the bill arithmetic
6
+ * from them and re-exports them for its consumers.
7
+ */
8
+ import { z } from 'zod';
9
+ export const planTierSchema = z.enum(['free', 'scale', 'enterprise']);
10
+ /**
11
+ * Every meter the engine records, over any transport. This is the closed set
12
+ * behind the loose `eventName` strings that `UsageRecorder` and the quota gate
13
+ * pass around.
14
+ *
15
+ * Recording and pricing are separate questions. A meter exists so usage can be
16
+ * gated and attributed; whether it reaches an invoice is `METER_EVENT_AXIS`.
17
+ * Bootstraps are recorded and quota-gated but deliberately unpriced: a bootstrap
18
+ * is what a client does once to become useful, and charging for it would price
19
+ * the act of connecting.
20
+ */
21
+ export const meterEventSchema = z.enum([
22
+ 'api.commit_ops',
23
+ 'api.model_reads',
24
+ 'api.claim_creates',
25
+ 'api.bootstraps',
26
+ ]);
27
+ /**
28
+ * One step of the rate card. `throughOps` is the cumulative ceiling the rate
29
+ * applies up to, and `null` marks the final, unbounded step. Rates are marginal:
30
+ * crossing a ceiling reprices the operations above it, never the ones below.
31
+ */
32
+ export const rateBracketSchema = z.object({
33
+ throughOps: z.number().int().positive().nullable(),
34
+ usdPerMillionOps: z.number().nonnegative(),
35
+ });
36
+ //# sourceMappingURL=pricing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pricing.js","sourceRoot":"","sources":["../../src/wire/pricing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC,CAAC,CAAC;AAGtE;;;;;;;;;;GAUG;AACH,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC,IAAI,CAAC;IACrC,gBAAgB;IAChB,iBAAiB;IACjB,mBAAmB;IACnB,gBAAgB;CACjB,CAAC,CAAC;AAGH;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,MAAM,CAAC;IACxC,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE;IAClD,gBAAgB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,WAAW,EAAE;CAC3C,CAAC,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/transaction",
3
- "version": "0.39.0",
3
+ "version": "0.40.0",
4
4
  "description": "The headless Ablo transaction client and canonical contracts for reads, commits, settlement, claims, and durable observation.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -17,6 +17,11 @@
17
17
  "import": "./dist/errorCodes.js",
18
18
  "default": "./dist/errorCodes.js"
19
19
  },
20
+ "./pricing": {
21
+ "types": "./dist/pricing.d.ts",
22
+ "import": "./dist/pricing.js",
23
+ "default": "./dist/pricing.js"
24
+ },
20
25
  "./environment": {
21
26
  "types": "./dist/environment.d.ts",
22
27
  "import": "./dist/environment.js",
@@ -76,6 +76,15 @@ export interface ConflictEvent {
76
76
  id: string;
77
77
  fields: readonly string[];
78
78
  writtenBy?: ParticipantKind;
79
+ /**
80
+ * The group premise this row breached, when the conflict was found at group
81
+ * grain. `model`/`id` stay the row that actually moved, so a log line can
82
+ * say which row moved AND which premise it broke — the two used to be the
83
+ * same field, and a group conflict logged its group key as the row.
84
+ */
85
+ group?: string;
86
+ /** How the row reached `group` — `self`, `parent`, or `transitive`. */
87
+ via?: string;
79
88
  }[];
80
89
  }
81
90
 
@@ -24,6 +24,10 @@ export {
24
24
  onStaleModeSchema,
25
25
  writeGuardSchema,
26
26
  staleNotificationSchema,
27
+ rowStaleNotificationSchema,
28
+ groupStaleNotificationSchema,
29
+ stalePropagationSchema,
30
+
27
31
  readDependencySchema,
28
32
  trackDependencySchema,
29
33
  // Layer 2 — pessimistic claim / claim-lease
@@ -101,6 +105,9 @@ export type {
101
105
  OnStaleMode,
102
106
  WriteGuard,
103
107
  StaleNotification,
108
+ RowStaleNotification,
109
+ GroupStaleNotification,
110
+ StalePropagation,
104
111
  ReadDependency,
105
112
  TrackDependency,
106
113
  // Layer 2 — pessimistic claim / claim-lease
@@ -179,3 +186,4 @@ export {
179
186
  modelTarget,
180
187
  streamTarget,
181
188
  } from './locator.js';
189
+
@@ -1,5 +1,9 @@
1
1
  import { z } from 'zod';
2
- import { syncGroupInputSchema } from '../schema/roles.js';
2
+ import {
3
+ syncGroupInputSchema,
4
+ syncGroupRefSchema,
5
+ syncGroupSchema,
6
+ } from '../schema/roles.js';
3
7
  export { syncGroupInputSchema } from '../schema/roles.js';
4
8
  import { isFieldRef, type FieldRef } from '../schema/fieldRef.js';
5
9
  import type { ParticipantKind } from '../types/participant.js';
@@ -213,6 +217,28 @@ export const targetRefSchema = z.object({
213
217
  });
214
218
  export type TargetRef = z.infer<typeof targetRefSchema>;
215
219
 
220
+ /**
221
+ * What a {@link ModelClaim} points at — the target locator as SDK callers see
222
+ * it, keyed by `model` and `id` rather than the wire schema's `entityType` and
223
+ * `entityId`. This is the public `ModelTarget` shape.
224
+ *
225
+ * Declared beside {@link targetRefSchema} because the two are the same shape in
226
+ * two spellings, and a member added to one belongs in the other. Both are read
227
+ * by {@link staleNotificationSchema} and the claim family below, so this has to
228
+ * precede them.
229
+ */
230
+ export const modelTargetSchema = z
231
+ .object({
232
+ model: z.string(),
233
+ id: z.string(),
234
+ field: z.string().optional(),
235
+ /** Several named parts at once — see {@link targetRefSchema}. */
236
+ fields: z.array(z.string()).readonly().optional(),
237
+ meta: z.record(z.string(), z.unknown()).optional(),
238
+ })
239
+ .readonly();
240
+ export type ModelTarget = z.infer<typeof modelTargetSchema>;
241
+
216
242
  /**
217
243
  * The same locator in the spelling the wait line and the claim handle use —
218
244
  * `{ type, id }` for the entity, the sub-entity half unchanged. It is a
@@ -270,45 +296,153 @@ export type WriteGuard = z.infer<typeof writeGuardSchema>;
270
296
  * re-commits. `reject` throws instead, and `overwrite` proceeds silently —
271
297
  * neither notifies.
272
298
  */
273
- export const staleNotificationSchema = z.object({
299
+ /**
300
+ * A log position, as the server reports one.
301
+ *
302
+ * Server-PRODUCED watermarks are constrained here; the caller-supplied
303
+ * {@link writeGuardSchema} `readAt` deliberately is not. The asymmetry is the
304
+ * trust direction, not an oversight: the server controls what it stamps, so
305
+ * stating the domain costs nothing, while tightening an inbound field would
306
+ * reject payloads the wire accepts today.
307
+ */
308
+ const syncIdSchema = z.number().int().nonnegative();
309
+
310
+ /**
311
+ * How a change reached the premise that fired — the three ways a record joins a
312
+ * sync group, which is the vocabulary `RecordGroupSpec` already routes by
313
+ * (`selfKind` / `parents` / `transitive`) rather than a fourth name for it.
314
+ *
315
+ * Without this a group notification can only say "something in the group you
316
+ * read moved", and the cheapest correct response to that is to re-read the
317
+ * whole group. `via` plus the notification's `target` narrows it to the row
318
+ * that actually moved and how it got there, which is usually a one-row re-read.
319
+ *
320
+ * • `self` — the row that moved IS the group's scope root.
321
+ * • `parent` — it sits one declared containment edge below the root.
322
+ * • `transitive` — the root is ≥2 hops up (`comment → task → project`).
323
+ */
324
+ export const stalePropagationSchema = z
325
+ .object({
326
+ via: z.enum(['self', 'parent', 'transitive']),
327
+ /**
328
+ * The intermediate models walked from the moved row up to the scope root,
329
+ * nearest hop first — the part of the route a reader cannot see from the
330
+ * two endpoints. Empty on `self` and `parent`, which have no intermediate.
331
+ *
332
+ * Model names, not rows: naming the intermediate ROWS would cost a join per
333
+ * notification, and the route is what explains the reach. `target` already
334
+ * identifies the row that moved and `group` the premise it broke.
335
+ */
336
+ through: z.array(z.string()).readonly(),
337
+ })
338
+ .readonly();
339
+ export type StalePropagation = z.infer<typeof stalePropagationSchema>;
340
+
341
+ /**
342
+ * What both notification scopes carry. The arms below derive from it rather
343
+ * than restate it, the same way {@link claimStateSchema} projects the claim
344
+ * record.
345
+ */
346
+ const staleNotificationBaseSchema = z.object({
274
347
  /** Names this object's type; every returned object carries such a tag. */
275
- object: z.literal('stale_notification').optional(),
276
- /** Model name of the conflicting row. */
277
- model: z.string(),
278
- /** Row id. */
279
- id: z.string(),
280
- /** The watermark the committer reasoned against (its `readAt`). */
281
- readAt: z.number(),
282
- /**
283
- * Newest delta id on the row — the committer's new watermark. Re-capture
284
- * context at/after this id to reconcile.
285
- */
286
- observedSyncId: z.number(),
348
+ object: z.literal('stale_notification'),
287
349
  /**
288
- * Fields whose concurrent change collided with this write (intersection of
289
- * the committer's written columns and a newer delta's `changed_fields`).
290
- * Empty ⇒ a whole-entity change (CREATE/DELETE/legacy delta).
350
+ * The row that moved, and which part of it — `fields` holds the columns whose
351
+ * concurrent change collided (empty a whole-entity CREATE/DELETE).
352
+ *
353
+ * The same shape a claim names its subject with, so one reader handles "who
354
+ * holds this row" and "this row moved under you" without learning two
355
+ * vocabularies. In BOTH scopes this is the row that actually changed: a group
356
+ * notification names the moved row here and the fired premise in `group`.
291
357
  */
292
- conflictingFields: z.array(z.string()),
358
+ target: modelTargetSchema,
359
+ /** The watermark the committer reasoned against (its `readAt`). */
360
+ readAt: syncIdSchema,
293
361
  /**
294
- * The live values of `conflictingFields` after the conflict — the piece a
295
- * plain stale error omits. It lets the actor reconcile without a follow-up read.
362
+ * Newest delta id on the premise — the committer's new watermark. Re-capture
363
+ * context at/after this id to reconcile.
296
364
  */
297
- currentValues: z.record(z.string(), z.unknown()),
365
+ observedSyncId: syncIdSchema,
298
366
  /** Who wrote the conflicting delta. */
299
367
  writtenBy: z.object({
300
368
  kind: participantKindSchema,
301
369
  id: z.string(),
302
370
  }),
371
+ });
372
+
373
+ /** A premise on one row: that row moved. */
374
+ export const rowStaleNotificationSchema = staleNotificationBaseSchema.extend({
375
+ scope: z.literal('row'),
376
+ /**
377
+ * The live values of `target.fields` after the conflict — the piece a plain
378
+ * stale error omits. It lets the actor reconcile without a follow-up read.
379
+ *
380
+ * Row scope only. Reading live values is cheap for one known row and is what
381
+ * a group breach cannot promise, so the group arm omits the field rather than
382
+ * carrying an empty one that reads as "nothing changed".
383
+ */
384
+ currentValues: z.record(z.string(), z.unknown()),
385
+ });
386
+ export type RowStaleNotification = z.infer<typeof rowStaleNotificationSchema>;
387
+
388
+ /** A premise on a whole sync group: something in it moved. */
389
+ export const groupStaleNotificationSchema = staleNotificationBaseSchema.extend({
390
+ scope: z.literal('group'),
391
+ /**
392
+ * The group premise that fired (`report:abc`, `section:s1`).
393
+ *
394
+ * Same format schema the premise declares, so the value a caller writes into
395
+ * `reads[].group` and the value it gets back name a group identically. The
396
+ * BRANDED view is for values Ablo mints; a notification only echoes the
397
+ * premise it was handed, and re-parsing it to acquire a brand would be a
398
+ * second validation of data the commit boundary already checked.
399
+ */
400
+ group: syncGroupRefSchema,
401
+ /**
402
+ * How `target` reached `group`. Absent when the schema's record-group spec is
403
+ * unavailable to the commit path, which is the only case where the server
404
+ * knows a group moved without knowing the route.
405
+ */
406
+ propagation: stalePropagationSchema.optional(),
303
407
  /**
304
- * Set when this notification is for a GROUP premise (e.g. `report:abc`,
305
- * `section:s1`) rather than a single row "something in the group you read
306
- * changed." For a group notification `conflictingFields`/`currentValues` are
307
- * empty (the change could span many rows); re-read the group at
308
- * `observedSyncId` to reconcile. Absent a row-scoped notification.
408
+ * How much of the group moved the decision "re-read these rows" versus
409
+ * "give up and take the whole group" needs a size, and a premise that only
410
+ * says *something* changed forces the expensive branch every time.
411
+ *
412
+ * Counts DISTINCT ROWS, not deltas: two writes to one row is one row's worth
413
+ * of re-reading. `sample` names the most recently changed of them and is
414
+ * capped, so `truncated` says whether it tells the whole story.
415
+ *
416
+ * Absent when the commit path did not measure it (a durable `track` fires per
417
+ * commit and does not pay for the extra statement).
309
418
  */
310
- group: z.string().optional(),
419
+ changed: z
420
+ .object({
421
+ count: z.number().int().nonnegative(),
422
+ sample: z.array(modelTargetSchema).readonly(),
423
+ truncated: z.boolean(),
424
+ })
425
+ .optional(),
311
426
  });
427
+ export type GroupStaleNotification = z.infer<
428
+ typeof groupStaleNotificationSchema
429
+ >;
430
+
431
+ /**
432
+ * One advisory, two scopes, told apart by `scope`.
433
+ *
434
+ * A real discriminated union rather than a flat object with an optional
435
+ * `group`, which is what {@link readDependencySchema} already does for the
436
+ * premise side. While it was flat, the two scopes disagreed about what their
437
+ * fields meant — `model`/`id` held a row in one and the group key in the other,
438
+ * and `conflictingFields`/`currentValues` were empty by construction in the
439
+ * group case — so every consumer had to re-derive the scope from `group`'s
440
+ * presence and read the prose to know which fields were load-bearing.
441
+ */
442
+ export const staleNotificationSchema = z.discriminatedUnion('scope', [
443
+ rowStaleNotificationSchema,
444
+ groupStaleNotificationSchema,
445
+ ]);
312
446
  export type StaleNotification = z.infer<typeof staleNotificationSchema>;
313
447
 
314
448
  /**
@@ -338,7 +472,13 @@ const readRowDependencySchema = z.object({
338
472
  });
339
473
 
340
474
  const readGroupDependencySchema = z.object({
341
- group: z.string(),
475
+ /**
476
+ * The caller-facing view of the format: `reads: [{ group: 'report:abc' }]`
477
+ * type-checks inline, `'nonsense'` does not. The
478
+ * {@link groupStaleNotificationSchema} this premise fires uses the branded
479
+ * view, because there Ablo is the author.
480
+ */
481
+ group: syncGroupRefSchema,
342
482
  readAt: z.number(),
343
483
  onStale: onStaleModeSchema.optional(),
344
484
  });
@@ -361,7 +501,12 @@ export type ReadDependency = z.infer<typeof readDependencySchema>;
361
501
  * same reference: a track names its target exactly as a read does, and the
362
502
  * three ways it differs are stated here as omissions the compiler holds.
363
503
  *
364
- * • no `onStale` a track always notifies; that is what tracking is;
504
+ * • `onStale` NARROWED, not dropped `notify` (the default) reports the
505
+ * change on the tracker's next receipt and lets the commit through;
506
+ * `reject` refuses that commit while the belief is stale, so an agent
507
+ * cannot write on something it has been shown to be out of date about.
508
+ * `overwrite` is excluded because it means "apply my write anyway" and a
509
+ * track guards no write of its own to apply;
365
510
  * • no `fields` — a track fires at row grain, because the server keeps one
366
511
  * row per tracked target and reports that the target moved, not which
367
512
  * column did (`track_dependencies` has no field axis to store one in);
@@ -373,15 +518,36 @@ export type ReadDependency = z.infer<typeof readDependencySchema>;
373
518
  * genuinely cannot carry has to be omitted here on purpose, in one line, rather
374
519
  * than by being quietly left out of a copy.
375
520
  */
376
- const trackReadAtSchema = { readAt: z.number().optional() } as const;
521
+ /**
522
+ * A track's disposition — the premise enum with `overwrite` subtracted, derived
523
+ * from it rather than spelled again, so a mode added to the convention reaches
524
+ * a track unless it is deliberately excluded here.
525
+ */
526
+ export const trackOnStaleSchema = onStaleModeSchema.exclude(['overwrite']);
527
+ export type TrackOnStale = z.infer<typeof trackOnStaleSchema>;
528
+
529
+ /**
530
+ * What a track does when the caller says nothing — reporting, the behavior every
531
+ * track had before the disposition existed, so an existing registration is
532
+ * unchanged.
533
+ *
534
+ * Declared here because the value has to agree in three places that cannot
535
+ * import each other: this schema, the column DEFAULT, and the server's insert.
536
+ * The two SQL copies are pinned to this one by a test
537
+ * (`trackOnStaleDomain.test.ts`) rather than by anyone remembering.
538
+ */
539
+ export const DEFAULT_TRACK_ON_STALE: TrackOnStale = 'notify';
540
+
541
+ const trackBaseSchema = {
542
+ readAt: z.number().optional(),
543
+ onStale: trackOnStaleSchema.optional(),
544
+ } as const;
377
545
 
378
546
  export const trackDependencySchema = z.union([
379
547
  readRowDependencySchema
380
548
  .omit({ onStale: true, fields: true, readAt: true })
381
- .extend(trackReadAtSchema),
382
- readGroupDependencySchema
383
- .omit({ onStale: true, readAt: true })
384
- .extend(trackReadAtSchema),
549
+ .extend(trackBaseSchema),
550
+ readGroupDependencySchema.omit({ onStale: true, readAt: true }).extend(trackBaseSchema),
385
551
  ]);
386
552
  export type TrackDependency = z.infer<typeof trackDependencySchema>;
387
553
 
@@ -725,23 +891,6 @@ export const claimExpiredSchema = z.object({
725
891
  export type ClaimExpired = z.infer<typeof claimExpiredSchema>;
726
892
 
727
893
 
728
- /**
729
- * What a {@link ModelClaim} points at — the target locator as SDK callers see
730
- * it, keyed by `model` and `id` rather than the wire schema's `entityType` and
731
- * `entityId`. This is the public `ModelTarget` shape.
732
- */
733
- export const modelTargetSchema = z
734
- .object({
735
- model: z.string(),
736
- id: z.string(),
737
- field: z.string().optional(),
738
- /** Several named parts at once — see {@link targetRefSchema}. */
739
- fields: z.array(z.string()).readonly().optional(),
740
- meta: z.record(z.string(), z.unknown()).optional(),
741
- })
742
- .readonly();
743
- export type ModelTarget = z.infer<typeof modelTargetSchema>;
744
-
745
894
  /**
746
895
  * The two states a claim can be observed in while it still exists.
747
896
  *
@@ -50,7 +50,15 @@ export function formatClaim(e: ClaimEvent): string {
50
50
 
51
51
  /** A notify-instead-of-abort stale write as one readable line. */
52
52
  export function formatConflict(e: ConflictEvent): string {
53
- const rows = e.rows.map((r) => `${r.model}/${r.id}(${r.fields.join(',')})`).join(', ');
53
+ const rows = e.rows
54
+ .map((r) => {
55
+ // A group conflict reads as "which row moved, and which premise it broke",
56
+ // because those are two different things and the reader needs both to know
57
+ // whether to re-read one row or the whole group.
58
+ const premise = r.group ? ` ${r.via ? `${r.via} → ` : '→ '}${r.group}` : '';
59
+ return `${r.model}/${r.id}(${r.fields.join(',')})${premise}`;
60
+ })
61
+ .join(', ');
54
62
  return `conflict: tx ${e.clientTxId} — ${e.rows.length} row(s) changed underneath${rows ? `: ${rows}` : ''}`;
55
63
  }
56
64
 
package/src/errorCodes.ts CHANGED
@@ -575,6 +575,18 @@ export const ERROR_CODES = {
575
575
  'validation',
576
576
  'The CLI was invoked with an unknown flag or a malformed flag value.'
577
577
  ),
578
+ cli_api_key_missing: client(
579
+ 'auth',
580
+ 'The command needs an API key and none was found on this machine. Run `ablo login` (or set ABLO_API_KEY), then re-run the command.'
581
+ ),
582
+ cli_database_url_missing: client(
583
+ 'validation',
584
+ 'The command needs a database connection string and none was found — no DATABASE_URL in the process environment, .env.local, or .env, and no --url flag.'
585
+ ),
586
+ cli_database_unreachable: client(
587
+ 'transport',
588
+ 'The database named by the connection string could not be reached from this machine. The host, port, network, or credential refused the dial before any statement ran.'
589
+ ),
578
590
  commit_operation_required: wire(
579
591
  'validation',
580
592
  400,
@@ -1029,6 +1041,14 @@ export const ERROR_CODES = {
1029
1041
  'transport',
1030
1042
  'This environment provides no `fetch` implementation, so HTTP requests cannot be made. Run on a platform with `fetch` (Node 18+, modern browsers) or supply a polyfill.'
1031
1043
  ),
1044
+ api_unreachable: client(
1045
+ 'transport',
1046
+ 'The Ablo API could not be reached from this machine — the dial failed before any request arrived. Check the network, any proxy, and an ABLO_API_URL override, then retry.'
1047
+ ),
1048
+ response_unrecognized: client(
1049
+ 'transport',
1050
+ 'The server answered successfully, but with a body this client does not recognize. The client may be older than the server — update it and retry.'
1051
+ ),
1032
1052
  base_url_missing: client(
1033
1053
  'transport',
1034
1054
  'The client has no base URL configured, so it cannot address the server. Set the base URL when constructing the client.'
package/src/errors.ts CHANGED
@@ -810,10 +810,13 @@ const ENVELOPE_KEYS: ReadonlySet<string> = new Set([
810
810
  'request_id',
811
811
  // Read into typed properties below, so they are not detail as well.
812
812
  'error',
813
- 'reason',
814
813
  'requiredCapability',
815
814
  'claims',
816
815
  'heldByClaim',
816
+ // `reason` is deliberately NOT here: it is domain detail (a driver's words
817
+ // beside the server's message), not an envelope field — the canonical
818
+ // envelope schema has no such key — so it must survive into `details` for a
819
+ // consumer that renders it as a secondary line.
817
820
  ]);
818
821
 
819
822
  /** The non-envelope remainder of an error body — what `details` was before it
@@ -839,10 +842,14 @@ export function translateHttpError(
839
842
  : undefined;
840
843
  const flatError = typeof parsed.error === 'string' ? parsed.error : undefined;
841
844
  const code = parsed.code ?? nested?.code ?? flatError;
845
+ // The producer's `message` outranks a `reason`: the message is the sentence a
846
+ // server wrote for a person (what happened and the remedy), while `reason` is
847
+ // raw diagnostic text — a database driver's words — attached beside it. A body
848
+ // that carries only `reason` still reads it, as older endpoints did.
842
849
  const message =
843
850
  nested?.message ??
844
- parsed.reason ??
845
851
  parsed.message ??
852
+ parsed.reason ??
846
853
  flatError ??
847
854
  (typeof body === 'string' ? body : `HTTP ${status}`);
848
855
  const requiredCapability =
package/src/index.ts CHANGED
@@ -47,6 +47,10 @@ export type { ModelData } from './types/modelData.js';
47
47
  // The error hierarchy and its wire/recovery helpers.
48
48
  export * from './errors.js';
49
49
 
50
+ // The price of the product: tiers, rate card, and the bill arithmetic the
51
+ // published pricing page and the runtime caps both derive from.
52
+ export * from './pricing.js';
53
+
50
54
  // Pre-seam leaf surfaces, kept for compatibility with the first extraction.
51
55
  export * from './errorCodes.js';
52
56
  export * from './environment.js';
@@ -107,8 +107,16 @@ export type ConflictDecision =
107
107
  *
108
108
  * The monotonic `sync_id` landing order decides who yields: the stale
109
109
  * committer always recomputes against the newer value, an asymmetry that
110
- * prevents two notifying writers from looping against each other. Retries are
111
- * bounded by the client's reconciliation retry cap.
110
+ * prevents two notifying writers from looping against each other.
111
+ *
112
+ * That rules out livelock, and NOT starvation. Each round adopts a newer
113
+ * `observedSyncId`, so no baseline repeats — but a peer writing faster than
114
+ * the committer's read→decide→write gap keeps winning, and the rounds are
115
+ * unbounded because the engine does not re-issue: the actor does. Progress in
116
+ * the watermark is not progress in the work. The functional `update(id, fn)`
117
+ * bounds its own loop; a hand-rolled one must bound itself. To stop an actor
118
+ * writing at all while a belief it holds is stale, gate the belief —
119
+ * `track(..., { onStale: 'reject' })` — rather than the write.
112
120
  */
113
121
  | { readonly action: 'notify'; readonly reason?: string };
114
122