@abloatai/transaction 0.46.0 → 0.48.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 (91) hide show
  1. package/dist/ai-sdk/modelTools.d.ts.map +1 -1
  2. package/dist/ai-sdk/modelTools.js +1 -2
  3. package/dist/ai-sdk/modelTools.js.map +1 -1
  4. package/dist/ai-sdk/updateTool.d.ts.map +1 -1
  5. package/dist/ai-sdk/updateTool.js +0 -1
  6. package/dist/ai-sdk/updateTool.js.map +1 -1
  7. package/dist/auth/capability.d.ts +8 -0
  8. package/dist/auth/capability.d.ts.map +1 -1
  9. package/dist/auth/capability.js +2 -0
  10. package/dist/auth/capability.js.map +1 -1
  11. package/dist/auth/capabilityLifecycle.d.ts +4 -0
  12. package/dist/auth/capabilityLifecycle.d.ts.map +1 -1
  13. package/dist/auth/deliveryPartition.d.ts +14 -0
  14. package/dist/auth/deliveryPartition.d.ts.map +1 -0
  15. package/dist/auth/deliveryPartition.js +16 -0
  16. package/dist/auth/deliveryPartition.js.map +1 -0
  17. package/dist/auth/identity.d.ts +2 -0
  18. package/dist/auth/identity.d.ts.map +1 -1
  19. package/dist/auth/identity.js +3 -0
  20. package/dist/auth/identity.js.map +1 -1
  21. package/dist/auth/schemas.d.ts +8 -0
  22. package/dist/auth/schemas.d.ts.map +1 -1
  23. package/dist/auth/schemas.js +3 -0
  24. package/dist/auth/schemas.js.map +1 -1
  25. package/dist/auth/sessionMint.d.ts.map +1 -1
  26. package/dist/auth/sessionMint.js +1 -0
  27. package/dist/auth/sessionMint.js.map +1 -1
  28. package/dist/branches.d.ts +61 -12
  29. package/dist/branches.d.ts.map +1 -1
  30. package/dist/branches.js +25 -3
  31. package/dist/branches.js.map +1 -1
  32. package/dist/errorCodes.d.ts +18 -2
  33. package/dist/errorCodes.d.ts.map +1 -1
  34. package/dist/errorCodes.js +28 -12
  35. package/dist/errorCodes.js.map +1 -1
  36. package/dist/footprint.d.ts +10 -22
  37. package/dist/footprint.d.ts.map +1 -1
  38. package/dist/footprint.js +14 -24
  39. package/dist/footprint.js.map +1 -1
  40. package/dist/pricing.d.ts +21 -2
  41. package/dist/pricing.d.ts.map +1 -1
  42. package/dist/pricing.js +32 -8
  43. package/dist/pricing.js.map +1 -1
  44. package/dist/resources/httpResources.d.ts +0 -1
  45. package/dist/resources/httpResources.d.ts.map +1 -1
  46. package/dist/resources/modelOperations.d.ts +13 -3
  47. package/dist/resources/modelOperations.d.ts.map +1 -1
  48. package/dist/schema/openapi.js +4 -4
  49. package/dist/schema/openapi.js.map +1 -1
  50. package/dist/source/index.d.ts +1 -1
  51. package/dist/source/index.d.ts.map +1 -1
  52. package/dist/source/migrations.d.ts +1 -1
  53. package/dist/source/migrations.d.ts.map +1 -1
  54. package/dist/source/migrations.js +18 -7
  55. package/dist/source/migrations.js.map +1 -1
  56. package/dist/transport/httpTransport.d.ts.map +1 -1
  57. package/dist/transport/httpTransport.js +5 -10
  58. package/dist/transport/httpTransport.js.map +1 -1
  59. package/dist/transport/wsTransport.d.ts +8 -0
  60. package/dist/transport/wsTransport.d.ts.map +1 -1
  61. package/dist/transport/wsTransport.js +8 -0
  62. package/dist/transport/wsTransport.js.map +1 -1
  63. package/dist/wire/dataSourceResponses.d.ts +17 -1
  64. package/dist/wire/dataSourceResponses.d.ts.map +1 -1
  65. package/dist/wire/dataSourceResponses.js +18 -0
  66. package/dist/wire/dataSourceResponses.js.map +1 -1
  67. package/dist/wire/index.d.ts +2 -2
  68. package/dist/wire/index.d.ts.map +1 -1
  69. package/dist/wire/index.js +1 -1
  70. package/dist/wire/index.js.map +1 -1
  71. package/package.json +1 -1
  72. package/src/ai-sdk/modelTools.ts +1 -2
  73. package/src/ai-sdk/updateTool.ts +0 -1
  74. package/src/auth/capability.ts +2 -0
  75. package/src/auth/deliveryPartition.ts +18 -0
  76. package/src/auth/identity.ts +5 -0
  77. package/src/auth/schemas.ts +3 -0
  78. package/src/auth/sessionMint.ts +1 -0
  79. package/src/branches.ts +27 -3
  80. package/src/errorCodes.ts +113 -17
  81. package/src/footprint.ts +18 -27
  82. package/src/pricing.ts +36 -8
  83. package/src/resources/httpResources.ts +0 -1
  84. package/src/resources/modelOperations.ts +14 -3
  85. package/src/schema/openapi.ts +4 -4
  86. package/src/source/index.ts +1 -1
  87. package/src/source/migrations.ts +21 -7
  88. package/src/transport/httpTransport.ts +5 -10
  89. package/src/transport/wsTransport.ts +18 -0
  90. package/src/wire/dataSourceResponses.ts +20 -0
  91. package/src/wire/index.ts +2 -0
@@ -276,7 +276,6 @@ export interface ModelMutationOptions extends ClaimedOptions {
276
276
  readonly idempotencyKey?: string | null;
277
277
  readonly readAt?: number | null;
278
278
  readonly onStale?: OnStaleMode | null;
279
- readonly wait?: CommitWait;
280
279
  readonly claim?: Claim | ClaimOptions | null;
281
280
  /** Fencing token (Option B) from the claim; server-validated at commit. */
282
281
  readonly fenceToken?: number | null;
@@ -558,15 +558,26 @@ export interface ModelRetrieveParams extends ServerRetrieveOptions {
558
558
  readonly id: string;
559
559
  }
560
560
 
561
+ /**
562
+ * Options shared by schema model writes.
563
+ *
564
+ * Reactive clients apply the row change optimistically before returning from
565
+ * the call. The returned promise has one stable settlement contract across
566
+ * reactive and stateless clients: it resolves only after authoritative
567
+ * confirmation. Callers that need an earlier queued receipt use the lower-level
568
+ * `commits.create` resource instead.
569
+ */
570
+ export type ModelWriteOptions = Omit<MutationOptions, 'wait'>;
571
+
561
572
  export interface ModelCreateParams<T, CreateInput>
562
- extends MutationOptions {
573
+ extends ModelWriteOptions {
563
574
  readonly data: CreateInput;
564
575
  readonly id?: string | null;
565
576
  readonly claim?: Claim<T> | ClaimTargetOptions<CreateInput> | null;
566
577
  }
567
578
 
568
579
  export interface ModelUpdateParams<T, Fields = T>
569
- extends MutationOptions {
580
+ extends ModelWriteOptions {
570
581
  readonly id: string;
571
582
  /**
572
583
  * Patch only fields declared by the model's Zod input shape. Hydrated rows
@@ -578,7 +589,7 @@ export interface ModelUpdateParams<T, Fields = T>
578
589
  }
579
590
 
580
591
  export interface ModelDeleteParams<T, Fields = T>
581
- extends MutationOptions {
592
+ extends ModelWriteOptions {
582
593
  readonly id: string;
583
594
  readonly claim?: Claim<T> | ClaimTargetOptions<Fields> | null;
584
595
  }
@@ -644,7 +644,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
644
644
  '/v1/branches/{id}/status': {
645
645
  get: {
646
646
  tags: ['branches'],
647
- summary: 'Diagnose one branch plane',
647
+ summary: 'Diagnose one branch',
648
648
  description:
649
649
  'Returns branch lifecycle, active schema, compatibility with the parent schema, safe datasource coordinates, and readiness blockers.',
650
650
  parameters: [idParam()],
@@ -699,7 +699,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
699
699
  description:
700
700
  'One round trip per cadence for a worker holding many rows, instead of ' +
701
701
  'one per row. Takes only `ttl`; the leases are whichever ones your ' +
702
- 'credential holds on this plane.',
702
+ 'credential holds on this branch.',
703
703
  requestBody: optionalJsonBody(derive(claimHeartbeatRequestSchema, 'input')),
704
704
  responses: {
705
705
  '200': namedResp(
@@ -812,7 +812,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
812
812
  tags: ['schema'],
813
813
  summary: 'What the models look like',
814
814
  description:
815
- "The schema deployed on your credential's plane: every model with its " +
815
+ "The schema deployed on your credential's branch: every model with its " +
816
816
  'fields, their types, and its relations. Read this when you have no ' +
817
817
  'local schema declaration to read types from — it is what makes a ' +
818
818
  'field typo a local check rather than a rejected write. Each model ' +
@@ -835,7 +835,7 @@ export function abloOpenApi(options: SchemaToOpenApiOptions = {}): Json {
835
835
  'How a caller without a socket learns what its peers did. Omit ' +
836
836
  '`after` for the most recent entries, then copy each page\'s ' +
837
837
  '`next_cursor` back as `after` to walk forward. Scope is taken from ' +
838
- 'your key — organization, plane and sync groups — so a caller cannot ' +
838
+ 'your key — organization, branch and sync groups — so a caller cannot ' +
839
839
  'widen what it sees by asking.',
840
840
  parameters: queryParams(logQuerySchema),
841
841
  responses: {
@@ -192,5 +192,5 @@ export {
192
192
  type FootprintArtifact,
193
193
  type FootprintKind,
194
194
  type FootprintNames,
195
- type FootprintPlane,
195
+ type DataSourceIdentity,
196
196
  } from '../footprint.js';
@@ -22,15 +22,28 @@ import type { Migration } from './contract.js';
22
22
  * block resolves the ledger through the search path and runs the `ALTER` only
23
23
  * when the column is absent — a true no-op on a table that already has it.
24
24
  */
25
- function addColumnIfAbsent(column: string, definition: string): string {
25
+ function quoteIdent(value: string): string {
26
+ return `"${value.replace(/"/g, '""')}"`;
27
+ }
28
+
29
+ function quoteLiteral(value: string): string {
30
+ return `'${value.replace(/'/g, "''")}'`;
31
+ }
32
+
33
+ function qualified(schema: string, relation: string): string {
34
+ return `${quoteIdent(schema)}.${quoteIdent(relation)}`;
35
+ }
36
+
37
+ function addColumnIfAbsent(schema: string, column: string, definition: string): string {
38
+ const ledger = qualified(schema, 'ablo_idempotency');
26
39
  return `DO $$
27
- DECLARE ledger regclass := to_regclass('ablo_idempotency');
40
+ DECLARE ledger regclass := to_regclass(${quoteLiteral(ledger)});
28
41
  BEGIN
29
42
  IF ledger IS NOT NULL AND NOT EXISTS (
30
43
  SELECT 1 FROM pg_attribute
31
44
  WHERE attrelid = ledger AND attname = '${column}' AND attnum > 0 AND NOT attisdropped
32
45
  ) THEN
33
- ALTER TABLE ablo_idempotency ADD COLUMN ${column} ${definition};
46
+ ALTER TABLE ${ledger} ADD COLUMN ${quoteIdent(column)} ${definition};
34
47
  END IF;
35
48
  END $$;`;
36
49
  }
@@ -39,11 +52,12 @@ END $$;`;
39
52
  * rows carry a bounded `expires_at` (set by the adapter at write time) so the
40
53
  * customer can prune them; the `infinity` column default is only a fallback for
41
54
  * a row inserted without one. */
42
- export function idempotencyLedgerMigrations(): readonly Migration[] {
55
+ export function idempotencyLedgerMigrations(schema = 'public'): readonly Migration[] {
56
+ const ledger = qualified(schema, 'ablo_idempotency');
43
57
  return [
44
58
  {
45
59
  name: 'ablo_idempotency',
46
- up: `CREATE TABLE IF NOT EXISTS ablo_idempotency (
60
+ up: `CREATE TABLE IF NOT EXISTS ${ledger} (
47
61
  client_tx_id TEXT PRIMARY KEY,
48
62
  response JSONB NOT NULL,
49
63
  request_hash TEXT,
@@ -64,11 +78,11 @@ export function idempotencyLedgerMigrations(): readonly Migration[] {
64
78
  // setup — owned by another role — the no-op still errors "must be owner".
65
79
  // Guard on the catalog (readable regardless of ownership) so the ALTER
66
80
  // runs only when the column is genuinely absent, keeping re-runs safe.
67
- up: addColumnIfAbsent('request_hash', 'TEXT'),
81
+ up: addColumnIfAbsent(schema, 'request_hash', 'TEXT'),
68
82
  },
69
83
  {
70
84
  name: 'ablo_idempotency_permanent_retention',
71
- up: addColumnIfAbsent('expires_at', `TIMESTAMPTZ NOT NULL DEFAULT 'infinity'`),
85
+ up: addColumnIfAbsent(schema, 'expires_at', `TIMESTAMPTZ NOT NULL DEFAULT 'infinity'`),
72
86
  },
73
87
  ];
74
88
  }
@@ -1433,7 +1433,6 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1433
1433
  idempotencyKey: options.idempotencyKey,
1434
1434
  readAt: options.readAt,
1435
1435
  onStale: options.onStale,
1436
- wait: options.wait,
1437
1436
  claim: options.claim,
1438
1437
  },
1439
1438
  `${modelName} ${action}`
@@ -1476,7 +1475,10 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1476
1475
  method,
1477
1476
  idempotencyKey: clientTxId,
1478
1477
  body: requestBody,
1479
- wait: options?.wait ?? 'confirmed',
1478
+ // Model methods have one promise contract: authoritative
1479
+ // confirmation. Early queued receipts remain available through the
1480
+ // lower-level `commits.create` lifecycle surface.
1481
+ wait: 'confirmed',
1480
1482
  },
1481
1483
  beforeSettlement
1482
1484
  );
@@ -1837,7 +1839,6 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1837
1839
  mutateModel('update', name, id, patch, {
1838
1840
  readAt,
1839
1841
  onStale: 'reject',
1840
- wait: 'confirmed',
1841
1842
  }),
1842
1843
  });
1843
1844
  }
@@ -1879,13 +1880,7 @@ export function createHttpTransport(options: HttpTransportOptions): HttpTranspor
1879
1880
  name,
1880
1881
  id,
1881
1882
  params.data,
1882
- {
1883
- ...options,
1884
- // This method returns the authoritative row, not a receipt. A
1885
- // queued source acceptance cannot satisfy that return contract,
1886
- // even when the caller supplied `wait: 'queued'`.
1887
- wait: 'confirmed',
1888
- },
1883
+ options,
1889
1884
  async () => {
1890
1885
  const read = await retrieveModel<T>(name, { id });
1891
1886
  if (read.data === undefined) {
@@ -70,6 +70,7 @@ import {
70
70
  noopSocketObservability,
71
71
  type SocketObservability,
72
72
  } from '../observability.js';
73
+ import type { DeliveryPartitionRoute } from '../auth/deliveryPartition.js';
73
74
 
74
75
  /**
75
76
  * Ceiling for the exponential reconnect backoff (`reconnectDelay * 2^n`,
@@ -95,6 +96,11 @@ export interface WsTransportOptions {
95
96
  */
96
97
  userId?: string;
97
98
  organizationId?: string;
99
+ /**
100
+ * Opaque server-resolved delivery route. The transport echoes it on every
101
+ * WebSocket upgrade; the receiving gateway authenticates and verifies it.
102
+ */
103
+ deliveryPartition?: DeliveryPartitionRoute | null;
98
104
  lastSyncId?: number;
99
105
  syncGroups?: string[];
100
106
  capabilities?: SyncCapabilities;
@@ -450,6 +456,7 @@ export class WsTransport<
450
456
  lastSyncId: 0,
451
457
  userId: '',
452
458
  organizationId: '',
459
+ deliveryPartition: null,
453
460
  capabilities: {
454
461
  partialBootstrap: true,
455
462
  compressedDeltas: true,
@@ -672,6 +679,12 @@ export class WsTransport<
672
679
  this.options.syncGroups.forEach((group) => {
673
680
  params.append('syncGroups', group);
674
681
  });
682
+ if (this.options.deliveryPartition) {
683
+ params.set(
684
+ 'deliveryPartition',
685
+ `${this.options.deliveryPartition.index}-${this.options.deliveryPartition.count}`,
686
+ );
687
+ }
675
688
 
676
689
  const wsUrl = `${this.options.url}?${params.toString()}`;
677
690
 
@@ -1488,4 +1501,9 @@ export class WsTransport<
1488
1501
  setSyncGroups(syncGroups: readonly string[]): void {
1489
1502
  this.options.syncGroups = [...syncGroups];
1490
1503
  }
1504
+
1505
+ /** Seeds the server-resolved route before the held first connection opens. */
1506
+ setDeliveryPartition(deliveryPartition: DeliveryPartitionRoute | null): void {
1507
+ this.options.deliveryPartition = deliveryPartition;
1508
+ }
1491
1509
  }
@@ -42,8 +42,10 @@ export const READINESS_ITEMS = [
42
42
  'wal_level',
43
43
  'publication',
44
44
  'replication_role',
45
+ 'replication_slot_capacity',
45
46
  'replica_identity',
46
47
  'table_select',
48
+ 'snapshot_row_security',
47
49
  'write_role',
48
50
  'row_security',
49
51
  'database_privileges',
@@ -153,6 +155,22 @@ export const datasourceValidationResponseSchema = z.object({
153
155
  });
154
156
  export type DatasourceValidationResponse = z.infer<typeof datasourceValidationResponseSchema>;
155
157
 
158
+ /** `POST /v1/datasources/resnapshot` — accepted reset of the direct source's
159
+ * initial load. The replacement snapshot completes asynchronously. */
160
+ export const datasourceResnapshotResponseSchema = z.object({
161
+ object: z.literal('datasource_resnapshot'),
162
+ initial_snapshot: z.object({ status: z.literal('loading') }),
163
+ replication_slot: z
164
+ .object({
165
+ slot: z.string(),
166
+ released: z.boolean(),
167
+ detail: z.string().optional(),
168
+ remove_with: z.string().optional(),
169
+ })
170
+ .optional(),
171
+ });
172
+ export type DatasourceResnapshotResponse = z.infer<typeof datasourceResnapshotResponseSchema>;
173
+
156
174
  /**
157
175
  * `POST /v1/datasources/locate` — which plane already holds a database.
158
176
  * A targeted lookup, not an enumeration: the caller must hold the connection
@@ -161,6 +179,8 @@ export type DatasourceValidationResponse = z.infer<typeof datasourceValidationRe
161
179
  */
162
180
  export const datasourceLocationResponseSchema = z.object({
163
181
  object: z.literal('datasource_location').optional(),
182
+ /** False with held=null means another organization owns the binding. */
183
+ available: z.boolean().optional(),
164
184
  held: z
165
185
  .object({
166
186
  project: z.string().nullable(),
package/src/wire/index.ts CHANGED
@@ -315,6 +315,7 @@ export {
315
315
  datasourceListResponseSchema,
316
316
  datasourceValidationResponseSchema,
317
317
  datasourceLocationResponseSchema,
318
+ datasourceResnapshotResponseSchema,
318
319
  datasourceDisconnectedResponseSchema,
319
320
  } from './dataSourceResponses.js';
320
321
  export type {
@@ -326,6 +327,7 @@ export type {
326
327
  DatasourceListResponse,
327
328
  DatasourceValidationResponse,
328
329
  DatasourceLocationResponse,
330
+ DatasourceResnapshotResponse,
329
331
  DatasourceDisconnectedResponse,
330
332
  } from './dataSourceResponses.js';
331
333