@abloatai/transaction 0.58.0 → 0.59.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 (59) hide show
  1. package/dist/client/ablo.d.ts +3 -4
  2. package/dist/client/ablo.d.ts.map +1 -1
  3. package/dist/client/ablo.js.map +1 -1
  4. package/dist/client/surface.d.ts +15 -0
  5. package/dist/client/surface.d.ts.map +1 -0
  6. package/dist/client/surface.js +29 -0
  7. package/dist/client/surface.js.map +1 -0
  8. package/dist/pricing.d.ts +7 -1
  9. package/dist/pricing.d.ts.map +1 -1
  10. package/dist/pricing.js +16 -2
  11. package/dist/pricing.js.map +1 -1
  12. package/dist/schema/ddl.d.ts.map +1 -1
  13. package/dist/schema/ddl.js +1 -1
  14. package/dist/schema/ddl.js.map +1 -1
  15. package/dist/schema/deployment/backfill.d.ts +44 -0
  16. package/dist/schema/deployment/backfill.d.ts.map +1 -0
  17. package/dist/schema/deployment/backfill.js +57 -0
  18. package/dist/schema/deployment/backfill.js.map +1 -0
  19. package/dist/schema/deployment/contracts.d.ts +526 -0
  20. package/dist/schema/deployment/contracts.d.ts.map +1 -0
  21. package/dist/schema/deployment/contracts.js +77 -0
  22. package/dist/schema/deployment/contracts.js.map +1 -0
  23. package/dist/schema/deployment/fingerprint.d.ts +3 -0
  24. package/dist/schema/deployment/fingerprint.d.ts.map +1 -0
  25. package/dist/schema/deployment/fingerprint.js +18 -0
  26. package/dist/schema/deployment/fingerprint.js.map +1 -0
  27. package/dist/schema/deployment/index.d.ts +18 -0
  28. package/dist/schema/deployment/index.d.ts.map +1 -0
  29. package/dist/schema/deployment/index.js +71 -0
  30. package/dist/schema/deployment/index.js.map +1 -0
  31. package/dist/schema/deployment/postgresCatalog.d.ts +39 -0
  32. package/dist/schema/deployment/postgresCatalog.d.ts.map +1 -0
  33. package/dist/schema/deployment/postgresCatalog.js +86 -0
  34. package/dist/schema/deployment/postgresCatalog.js.map +1 -0
  35. package/dist/schema/deployment/reconcile.d.ts +20 -0
  36. package/dist/schema/deployment/reconcile.d.ts.map +1 -0
  37. package/dist/schema/deployment/reconcile.js +226 -0
  38. package/dist/schema/deployment/reconcile.js.map +1 -0
  39. package/dist/schema/deployment/sequence.d.ts +4 -0
  40. package/dist/schema/deployment/sequence.d.ts.map +1 -0
  41. package/dist/schema/deployment/sequence.js +71 -0
  42. package/dist/schema/deployment/sequence.js.map +1 -0
  43. package/dist/schema/index.d.ts +1 -0
  44. package/dist/schema/index.d.ts.map +1 -1
  45. package/dist/schema/index.js +4 -0
  46. package/dist/schema/index.js.map +1 -1
  47. package/package.json +1 -1
  48. package/src/client/ablo.ts +2 -2
  49. package/src/client/surface.ts +50 -0
  50. package/src/pricing.ts +16 -2
  51. package/src/schema/ddl.ts +3 -1
  52. package/src/schema/deployment/backfill.ts +88 -0
  53. package/src/schema/deployment/contracts.ts +119 -0
  54. package/src/schema/deployment/fingerprint.ts +13 -0
  55. package/src/schema/deployment/index.ts +81 -0
  56. package/src/schema/deployment/postgresCatalog.ts +131 -0
  57. package/src/schema/deployment/reconcile.ts +219 -0
  58. package/src/schema/deployment/sequence.ts +85 -0
  59. package/src/schema/index.ts +5 -0
@@ -0,0 +1,71 @@
1
+ const PHASES = [
2
+ 'intent',
3
+ 'expand',
4
+ 'dual_write',
5
+ 'backfill',
6
+ 'verify',
7
+ 'switch',
8
+ 'contract',
9
+ 'recover',
10
+ ];
11
+ function statusOf(findings) {
12
+ if (findings.some(({ severity }) => severity === 'blocker' || severity === 'error')) {
13
+ return 'blocked';
14
+ }
15
+ if (findings.some(({ code }) => code === 'lifecycle_ready'))
16
+ return 'ready';
17
+ return findings.every(({ severity }) => severity === 'warning' || severity === 'info')
18
+ ? 'advisory'
19
+ : 'ready';
20
+ }
21
+ function manifestStep(finding) {
22
+ const status = statusOf([finding]);
23
+ return {
24
+ id: finding.id,
25
+ phase: finding.phase,
26
+ owner: finding.owner,
27
+ title: finding.message,
28
+ action: finding.action,
29
+ dependsOn: finding.dependsOn ?? [],
30
+ findingIds: [finding.id],
31
+ status,
32
+ executableByAblo: finding.owner === 'ablo' && status === 'ready',
33
+ };
34
+ }
35
+ function groupedStep(phase, owner, findings) {
36
+ const status = statusOf(findings);
37
+ return {
38
+ id: `${phase}:${owner}`,
39
+ phase,
40
+ owner,
41
+ title: `${phase} — ${owner.replaceAll('_', ' ')}`,
42
+ action: [...new Set(findings.map(({ action }) => action))].join(' '),
43
+ dependsOn: [],
44
+ findingIds: findings.map(({ id }) => id),
45
+ status,
46
+ executableByAblo: owner === 'ablo' && status === 'ready',
47
+ };
48
+ }
49
+ /** Preserve manifest gates while grouping ordinary diagnostics by phase/owner. */
50
+ export function sequenceDeployment(findings) {
51
+ const steps = [];
52
+ let previousPhase = [];
53
+ for (const phase of PHASES) {
54
+ const phaseFindings = findings.filter((finding) => finding.phase === phase);
55
+ const manifest = phaseFindings.filter(({ id }) => id.startsWith('manifest:'));
56
+ const ordinary = phaseFindings.filter(({ id }) => !id.startsWith('manifest:'));
57
+ const phaseSteps = manifest.map(manifestStep);
58
+ for (const owner of [...new Set(ordinary.map(({ owner }) => owner))]) {
59
+ phaseSteps.push(groupedStep(phase, owner, ordinary.filter((finding) => finding.owner === owner)));
60
+ }
61
+ for (const step of phaseSteps) {
62
+ const explicit = step.dependsOn;
63
+ const barrier = explicit.length === 0 ? previousPhase : [];
64
+ steps.push({ ...step, dependsOn: [...new Set([...explicit, ...barrier])] });
65
+ }
66
+ if (phaseSteps.length > 0)
67
+ previousPhase = phaseSteps.map(({ id }) => id);
68
+ }
69
+ return steps;
70
+ }
71
+ //# sourceMappingURL=sequence.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sequence.js","sourceRoot":"","sources":["../../../src/schema/deployment/sequence.ts"],"names":[],"mappings":"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"}
@@ -38,6 +38,7 @@ export { selectModels, omitModels, omittedModelError } from './select.js';
38
38
  export { generateProvisionPlan, generateMigrationPlan, appSchemaName, camelToSnake, snakeToCamel, q, sqlType, type ProvisionPlan, type MigrationPlan, } from './ddl.js';
39
39
  export { PG_LOCK_NOT_AVAILABLE, resolveDdlLockTimeout, resolveDdlMaxLockAttempts, ddlLockRetryBackoffMs, type DdlLockEnv, } from './ddlLock.js';
40
40
  export { diffSchema, classifyMigration, classifyCast, isAutoApplicable, isBlockerResolved, unresolvedBlockers, type BackfillValue, type MigrationStep, type FieldChanges, type FieldColumnChange, type FieldTypeChange, type NullabilityChange, type EnumValuesChange, type IndexChange, type CastSafety, type FieldType, type RenameHints, type MigrationSignal, type MigrationClassification, type WarningCode, type BlockerCode, } from './diff.js';
41
+ export * from './deployment/index.js';
41
42
  export { generateTypes } from './generate.js';
42
43
  export { query, defineQueries, type QueryDef, type QueryRecord, type Queries, type InferQueryInput, type InferQueryResult, } from './queries.js';
43
44
  export { abloOpenApi, schemaToOpenApi, type SchemaToOpenApiOptions } from './openapi.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"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"}
@@ -68,6 +68,10 @@ export { generateProvisionPlan, generateMigrationPlan, appSchemaName, camelToSna
68
68
  export { PG_LOCK_NOT_AVAILABLE, resolveDdlLockTimeout, resolveDdlMaxLockAttempts, ddlLockRetryBackoffMs, } from './ddlLock.js';
69
69
  // Schema diff + migration planning — produces the plan the DDL layer turns into SQL.
70
70
  export { diffSchema, classifyMigration, classifyCast, isAutoApplicable, isBlockerResolved, unresolvedBlockers, } from './diff.js';
71
+ // One source/active/database deployment skeleton. CLI check/plan/push/migrate,
72
+ // server activation, and runtime drift project this contract instead of
73
+ // maintaining lateral planners.
74
+ export * from './deployment/index.js';
71
75
  // Schema → TypeScript type emission.
72
76
  export { generateTypes } from './generate.js';
73
77
  // Query definition DSL + type inference
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/schema/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@abloatai/transaction",
3
- "version": "0.58.0",
3
+ "version": "0.59.0",
4
4
  "description": "The headless Ablo transaction client and canonical contracts for reads, commits, confirmation, claims, and durable observation.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -23,9 +23,9 @@
23
23
  import {
24
24
  createAbloHttpClient,
25
25
  type AbloHttpClient,
26
- type AbloHttpClientOptions,
27
26
  } from '../transport/http/client.js';
28
27
  import type { SchemaRecord } from '../schema/schema.js';
28
+ import type { PublicAbloOptions } from './surface.js';
29
29
  import type * as _Streams from '../types/streams.js';
30
30
  import type * as _SchemaTypes from '../schema/schema.js';
31
31
  import type * as _Global from '../types/global.js';
@@ -40,7 +40,7 @@ import type * as _Http from './resources/httpResources.js';
40
40
  * socket carve lands (ADR 0016, follow-up 3a).
41
41
  */
42
42
  export function Ablo<const S extends SchemaRecord>(
43
- options: AbloHttpClientOptions<S> & { transport?: 'http' },
43
+ options: PublicAbloOptions<S>,
44
44
  ): AbloHttpClient<S> {
45
45
  return createAbloHttpClient(options);
46
46
  }
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Machine-checked names on the public `Ablo({ ... })` configuration surface.
3
+ *
4
+ * Documentation reads this tuple. Type equality below makes a constructor
5
+ * option change fail compilation until the reference inventory changes with it.
6
+ * `onCommitReceipt` is an internal transport callback, so it is deliberately
7
+ * removed from the public factory type before the equality check.
8
+ */
9
+
10
+ import type { AbloHttpClientOptions } from '../transport/http/client.js';
11
+ import type { SchemaRecord } from '../schema/schema.js';
12
+
13
+ export type PublicAbloOptions<S extends SchemaRecord = SchemaRecord> = Omit<
14
+ AbloHttpClientOptions<S>,
15
+ 'onCommitReceipt' | 'transport'
16
+ > & {
17
+ readonly transport?: 'http';
18
+ };
19
+
20
+ export const PUBLIC_ABLO_OPTION_KEYS = [
21
+ 'schema',
22
+ 'apiKey',
23
+ 'authEndpoint',
24
+ 'authToken',
25
+ 'baseURL',
26
+ 'dangerouslyAllowBrowser',
27
+ 'fetch',
28
+ 'authTimeoutMs',
29
+ 'allowCrossOriginAuthEndpoint',
30
+ 'bootstrapBaseUrl',
31
+ 'defaultHeaders',
32
+ 'defaultQuery',
33
+ 'observability',
34
+ 'durableWrites',
35
+ 'commitOutbox',
36
+ 'commitOutboxScope',
37
+ 'transport',
38
+ 'timeoutMs',
39
+ ] as const;
40
+
41
+ type Equal<A, B> =
42
+ (<T>() => T extends A ? 1 : 2) extends (<T>() => T extends B ? 1 : 2)
43
+ ? true
44
+ : false;
45
+ type Expect<T extends true> = T;
46
+
47
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
48
+ type _PublicAbloOptionsExact = Expect<
49
+ Equal<(typeof PUBLIC_ABLO_OPTION_KEYS)[number], keyof PublicAbloOptions & string>
50
+ >;
package/src/pricing.ts CHANGED
@@ -46,7 +46,7 @@ export type { MeterEvent, PlanTier, RateBracket };
46
46
  * could observe the difference. It is emitted into the generated pricing
47
47
  * documentation so a stale copy is identifiable on sight.
48
48
  */
49
- export const PRICING_VERSION = '2026-08-24';
49
+ export const PRICING_VERSION = '2026-08-28';
50
50
 
51
51
  /**
52
52
  * Resolve a stored plan string (`stripe_subscription.plan`) to a tier.
@@ -257,7 +257,7 @@ export const PLANS = z
257
257
  hardCapOps: null,
258
258
  hardCapOpsPerDay: null,
259
259
  storageGib: 50,
260
- maxConcurrentConnections: 1_000,
260
+ maxConcurrentConnections: 5_000,
261
261
  contractPriced: false,
262
262
  features: ['auditExport'],
263
263
  },
@@ -397,3 +397,17 @@ export function dailyOpsCapForTier(tier: PlanTier): number | null {
397
397
  export function connectionCapForTier(tier: PlanTier): number | null {
398
398
  return PLANS[tier].maxConcurrentConnections;
399
399
  }
400
+
401
+ /**
402
+ * The first public tier that can reserve the requested connection capacity.
403
+ * A `null` cap is negotiated capacity, not infinity in the runtime; it is the
404
+ * commercial catch-all that sends the buyer into an Enterprise capacity plan.
405
+ */
406
+ export function selectPlanForConnectionCapacity(connections: number): PlanTier {
407
+ const requested = Number.isFinite(connections) ? Math.max(1, Math.ceil(connections)) : 1;
408
+ for (const tier of PLAN_ORDER) {
409
+ const cap = PLANS[tier].maxConcurrentConnections;
410
+ if (cap === null || requested <= cap) return tier;
411
+ }
412
+ return 'enterprise';
413
+ }
package/src/schema/ddl.ts CHANGED
@@ -318,7 +318,9 @@ export function generateProvisionPlan(
318
318
  for (const [fieldName, meta] of Object.entries(model.fields)) {
319
319
  const col = meta.column ?? camelToSnake(fieldName);
320
320
  if (BASE_COLUMNS.has(col) || col === orgCol) continue;
321
- statements.push(`ALTER TABLE ${qt} ADD COLUMN IF NOT EXISTS ${q(col)} ${sqlType(meta.type)};`);
321
+ statements.push(
322
+ `ALTER TABLE ${qt} ADD COLUMN IF NOT EXISTS ${q(col)} ${sqlType(meta.type)}${meta.isOptional ? '' : ' NOT NULL'};`,
323
+ );
322
324
  if (meta.type === 'enum' && meta.enumValues && meta.enumValues.length > 0) {
323
325
  const cname = `${table}_${col}_enum`;
324
326
  const allowed = meta.enumValues.map((v) => `'${v.replace(/'/g, "''")}'`).join(', ');
@@ -0,0 +1,88 @@
1
+ export type BackfillStatus = 'pending' | 'running' | 'paused' | 'succeeded' | 'failed' | 'cancelled';
2
+
3
+ export interface BackfillCheckpoint {
4
+ readonly jobId: string;
5
+ readonly idempotencyKey: string;
6
+ readonly cursor: string | null;
7
+ readonly processed: number;
8
+ readonly batches: number;
9
+ readonly status: BackfillStatus;
10
+ readonly updatedAt: string;
11
+ readonly error?: string;
12
+ }
13
+
14
+ export interface BackfillBatchResult {
15
+ readonly nextCursor: string | null;
16
+ readonly processed: number;
17
+ readonly done: boolean;
18
+ }
19
+
20
+ export interface ResumableBackfillEffects {
21
+ readonly load: (jobId: string) => Promise<BackfillCheckpoint | null>;
22
+ readonly save: (checkpoint: BackfillCheckpoint) => Promise<void>;
23
+ /** Must be idempotent for the job idempotency key and input cursor. */
24
+ readonly runBatch: (input: { jobId: string; idempotencyKey: string; cursor: string | null; limit: number; signal?: AbortSignal }) => Promise<BackfillBatchResult>;
25
+ readonly now?: () => string;
26
+ readonly retry?: (error: unknown, attempt: number) => Promise<void>;
27
+ /** Operational throttle checked before each batch; pause preserves the checkpoint for an exact resume. */
28
+ readonly beforeBatch?: (checkpoint: BackfillCheckpoint) => Promise<'run' | 'pause'>;
29
+ readonly onProgress?: (checkpoint: BackfillCheckpoint) => Promise<void> | void;
30
+ }
31
+
32
+ export interface ResumableBackfillOptions {
33
+ readonly jobId: string;
34
+ readonly idempotencyKey: string;
35
+ readonly batchSize?: number;
36
+ readonly maxBatches?: number;
37
+ readonly maxAttempts?: number;
38
+ readonly signal?: AbortSignal;
39
+ }
40
+
41
+ /** Bounded, resumable runner. A durable effect owns checkpoints; the transform owns idempotency. */
42
+ export async function runResumableBackfill(effects: ResumableBackfillEffects, options: ResumableBackfillOptions): Promise<BackfillCheckpoint> {
43
+ if (!options.jobId || !options.idempotencyKey) throw new Error('backfill jobId and idempotencyKey are required');
44
+ const batchSize = options.batchSize ?? 500;
45
+ const maxBatches = options.maxBatches ?? 100;
46
+ const maxAttempts = options.maxAttempts ?? 3;
47
+ if (batchSize < 1 || maxBatches < 1 || maxAttempts < 1) throw new Error('backfill bounds must be positive');
48
+ const now = effects.now ?? (() => new Date().toISOString());
49
+ let checkpoint = await effects.load(options.jobId) ?? { jobId: options.jobId, idempotencyKey: options.idempotencyKey, cursor: null, processed: 0, batches: 0, status: 'pending' as const, updatedAt: now() };
50
+ if (checkpoint.idempotencyKey !== options.idempotencyKey) throw new Error(`backfill job ${options.jobId} was created with a different idempotency key`);
51
+ if (checkpoint.status === 'succeeded') return checkpoint;
52
+ for (let batch = 0; batch < maxBatches; batch++) {
53
+ if (options.signal?.aborted) {
54
+ checkpoint = { ...checkpoint, status: 'cancelled', updatedAt: now() };
55
+ await effects.save(checkpoint);
56
+ return checkpoint;
57
+ }
58
+ if (await effects.beforeBatch?.(checkpoint) === 'pause') {
59
+ checkpoint = { ...checkpoint, status: 'paused', updatedAt: now() };
60
+ await effects.save(checkpoint);
61
+ await effects.onProgress?.(checkpoint);
62
+ return checkpoint;
63
+ }
64
+ let result: BackfillBatchResult | undefined;
65
+ let lastError: unknown;
66
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
67
+ try {
68
+ result = await effects.runBatch({ jobId: options.jobId, idempotencyKey: options.idempotencyKey, cursor: checkpoint.cursor, limit: batchSize, ...(options.signal ? { signal: options.signal } : {}) });
69
+ break;
70
+ } catch (error) {
71
+ lastError = error;
72
+ if (attempt < maxAttempts) await effects.retry?.(error, attempt);
73
+ }
74
+ }
75
+ if (!result) {
76
+ checkpoint = { ...checkpoint, status: 'failed', updatedAt: now(), error: lastError instanceof Error ? lastError.message : String(lastError) };
77
+ await effects.save(checkpoint);
78
+ await effects.onProgress?.(checkpoint);
79
+ return checkpoint;
80
+ }
81
+ if (!result.done && result.nextCursor === checkpoint.cursor) throw new Error(`backfill job ${options.jobId} did not advance its cursor`);
82
+ checkpoint = { jobId: checkpoint.jobId, idempotencyKey: checkpoint.idempotencyKey, cursor: result.nextCursor, processed: checkpoint.processed + result.processed, batches: checkpoint.batches + 1, status: result.done ? 'succeeded' : 'running', updatedAt: now() };
83
+ await effects.save(checkpoint);
84
+ await effects.onProgress?.(checkpoint);
85
+ if (result.done) return checkpoint;
86
+ }
87
+ return checkpoint;
88
+ }
@@ -0,0 +1,119 @@
1
+ import { z } from 'zod';
2
+ import type { SchemaJSON } from '../serialize.js';
3
+ import type { BackfillValue, RenameHints } from '../diff.js';
4
+ import type { MigrationStep } from '../diff.js';
5
+
6
+ export const deploymentPhaseSchema = z.enum(['intent', 'expand', 'dual_write', 'backfill', 'verify', 'switch', 'contract', 'recover']);
7
+ export type DeploymentPhase = z.infer<typeof deploymentPhaseSchema>;
8
+ export const deploymentOwnerSchema = z.enum(['application', 'application_migration', 'ablo']);
9
+ export type DeploymentOwner = z.infer<typeof deploymentOwnerSchema>;
10
+ export const deploymentSeveritySchema = z.enum(['blocker', 'error', 'warning', 'info']);
11
+ export type DeploymentSeverity = z.infer<typeof deploymentSeveritySchema>;
12
+ export const deploymentCategorySchema = z.enum(['policy_intent', 'physical_contract', 'compatibility', 'data_movement', 'destructive_contract', 'advisory', 'observation']);
13
+ export type DeploymentCategory = z.infer<typeof deploymentCategorySchema>;
14
+ export const deploymentDirectionSchema = z.enum(['source_to_active', 'source_to_database', 'active_to_database', 'client_to_active']);
15
+ export type DeploymentDirection = z.infer<typeof deploymentDirectionSchema>;
16
+
17
+ export const databaseColumnSnapshotSchema = z.object({
18
+ name: z.string(), dataType: z.string(), nullable: z.boolean(), default: z.string().nullable(), primary: z.boolean(), unique: z.boolean(),
19
+ /** Capped count of rows whose required routing value is NULL; absent for ordinary columns. */
20
+ nullCount: z.number().int().nonnegative().nullable().optional(),
21
+ });
22
+ export type DatabaseColumnSnapshot = z.infer<typeof databaseColumnSnapshotSchema>;
23
+ export const databaseIndexSnapshotSchema = z.object({
24
+ name: z.string(), columns: z.array(z.string()), unique: z.boolean(), valid: z.boolean(), ready: z.boolean(), predicate: z.string().nullable(),
25
+ });
26
+ export const databaseForeignKeySnapshotSchema = z.object({
27
+ name: z.string(), columns: z.array(z.string()), referencedSchema: z.string(), referencedTable: z.string(), referencedColumns: z.array(z.string()), validated: z.boolean(),
28
+ });
29
+ export const databaseTableSnapshotSchema = z.object({
30
+ schema: z.string(), name: z.string(), columns: z.record(z.string(), databaseColumnSnapshotSchema),
31
+ indexes: z.array(databaseIndexSnapshotSchema).optional(), foreignKeys: z.array(databaseForeignKeySnapshotSchema).optional(),
32
+ rowLevelSecurity: z.boolean().nullable(), forceRowLevelSecurity: z.boolean().nullable(),
33
+ replicaIdentity: z.string().nullable(), publicationMember: z.boolean().nullable(),
34
+ });
35
+ export type DatabaseTableSnapshot = z.infer<typeof databaseTableSnapshotSchema>;
36
+ export const databaseSnapshotSchema = z.object({
37
+ observedAt: z.string(), subject: z.string(), fingerprint: z.string(), appSchema: z.string(), ownership: z.enum(['application', 'ablo']),
38
+ tables: z.record(z.string(), databaseTableSnapshotSchema),
39
+ });
40
+ export type DatabaseSnapshot = z.infer<typeof databaseSnapshotSchema>;
41
+
42
+ export interface SourceSchemaSnapshot { readonly observedAt: string; readonly path: string; readonly hash: string; readonly schema: SchemaJSON; }
43
+ export interface ActiveSchemaSnapshot { readonly observedAt: string; readonly schemaId: string; readonly version: number; readonly hash: string; readonly pushedAt: string | null; readonly schema: SchemaJSON; }
44
+ export interface DeploymentTarget { readonly organizationId: string | null; readonly projectId: string | null; readonly branchId: string | null; readonly databaseSubject: string | null; readonly confirmed: boolean; }
45
+ export const deploymentGateSchema = z.object({
46
+ id: z.string().min(1),
47
+ phase: z.enum(['expand', 'dual_write', 'backfill', 'verify', 'switch', 'contract']),
48
+ owner: deploymentOwnerSchema,
49
+ resource: z.string().min(1),
50
+ title: z.string().min(1),
51
+ action: z.string().min(1),
52
+ status: z.enum(['pending', 'ready', 'satisfied']),
53
+ dependsOn: z.array(z.string()).default([]),
54
+ approval: z.string().min(1).optional(),
55
+ });
56
+ export const deploymentManifestSchema = z.object({
57
+ id: z.string().min(1),
58
+ live: z.boolean().default(true),
59
+ targetPhase: z.enum(['expand', 'dual_write', 'backfill', 'verify', 'switch', 'contract']).default('expand'),
60
+ gates: z.array(deploymentGateSchema),
61
+ });
62
+ export type DeploymentGate = z.infer<typeof deploymentGateSchema>;
63
+ export type DeploymentManifest = z.infer<typeof deploymentManifestSchema>;
64
+ export interface DeploymentIntent { readonly renames?: RenameHints; readonly backfills?: readonly BackfillValue[]; readonly acceptDestructive?: boolean; readonly manifest?: DeploymentManifest; }
65
+ export interface DeploymentObservation { readonly target: DeploymentTarget; readonly source: SourceSchemaSnapshot; readonly active: ActiveSchemaSnapshot | null; readonly database: DatabaseSnapshot | null; readonly intent?: DeploymentIntent; readonly supplementalFindings?: readonly DeploymentFinding[]; }
66
+
67
+ export interface DeploymentFinding {
68
+ readonly id: string; readonly code: string; readonly category: DeploymentCategory; readonly severity: DeploymentSeverity;
69
+ readonly direction: DeploymentDirection; readonly phase: DeploymentPhase; readonly owner: DeploymentOwner;
70
+ readonly model?: string; readonly field?: string; readonly column?: string; readonly from?: unknown; readonly to?: unknown;
71
+ readonly message: string; readonly action: string;
72
+ readonly dependsOn?: readonly string[];
73
+ }
74
+ export interface DeploymentStep {
75
+ readonly id: string; readonly phase: DeploymentPhase; readonly owner: DeploymentOwner; readonly title: string; readonly action: string;
76
+ readonly dependsOn: readonly string[]; readonly findingIds: readonly string[]; readonly status: 'ready' | 'blocked' | 'advisory'; readonly executableByAblo: boolean;
77
+ }
78
+ export interface SchemaDeploymentPlan {
79
+ readonly id: 'ablo-schema-deployment-plan-v1'; readonly mode: 'plan'; readonly createdAt: string; readonly fingerprint: string; readonly target: DeploymentTarget;
80
+ readonly states: { readonly source: Omit<SourceSchemaSnapshot, 'schema'>; readonly active: Omit<ActiveSchemaSnapshot, 'schema'> | null; readonly database: Omit<DatabaseSnapshot, 'tables'> | null; };
81
+ readonly findings: readonly DeploymentFinding[]; readonly steps: readonly DeploymentStep[]; readonly outcome: 'aligned' | 'ready' | 'blocked';
82
+ readonly operations: { readonly sourceToActive: readonly MigrationStep[]; readonly provision: readonly MigrationStep[] };
83
+ readonly rollbackTarget: { readonly schemaId: string; readonly version: number; readonly hash: string; readonly strategy: 'reactivate_artifact'; } | null;
84
+ readonly recovery: 'rollback' | 'forward_only';
85
+ }
86
+ export interface DeploymentApplyResult { readonly plan: SchemaDeploymentPlan; readonly appliedStepIds: readonly string[]; readonly verification: SchemaDeploymentPlan; readonly recorded: boolean; }
87
+
88
+ export const deploymentTargetSchema = z.object({
89
+ organizationId: z.string().nullable(), projectId: z.string().nullable(), branchId: z.string().nullable(), databaseSubject: z.string().nullable(), confirmed: z.boolean(),
90
+ });
91
+ export const deploymentFindingSchema = z.object({
92
+ id: z.string(), code: z.string(), category: deploymentCategorySchema, severity: deploymentSeveritySchema,
93
+ direction: deploymentDirectionSchema, phase: deploymentPhaseSchema, owner: deploymentOwnerSchema,
94
+ model: z.string().optional(), field: z.string().optional(), column: z.string().optional(), from: z.unknown().optional(), to: z.unknown().optional(),
95
+ message: z.string(), action: z.string(),
96
+ dependsOn: z.array(z.string()).readonly().optional(),
97
+ });
98
+ export const deploymentStepSchema = z.object({
99
+ id: z.string(), phase: deploymentPhaseSchema, owner: deploymentOwnerSchema, title: z.string(), action: z.string(),
100
+ dependsOn: z.array(z.string()).readonly(), findingIds: z.array(z.string()).readonly(), status: z.enum(['ready', 'blocked', 'advisory']), executableByAblo: z.boolean(),
101
+ });
102
+ export const schemaDeploymentPlanSchema = z.object({
103
+ id: z.literal('ablo-schema-deployment-plan-v1'), mode: z.literal('plan'), createdAt: z.string(), fingerprint: z.string(), target: deploymentTargetSchema,
104
+ states: z.object({
105
+ source: z.object({ observedAt: z.string(), path: z.string(), hash: z.string() }),
106
+ active: z.object({ observedAt: z.string(), schemaId: z.string(), version: z.number(), hash: z.string(), pushedAt: z.string().nullable() }).nullable(),
107
+ database: z.object({ observedAt: z.string(), subject: z.string(), fingerprint: z.string(), appSchema: z.string(), ownership: z.enum(['application', 'ablo']) }).nullable(),
108
+ }),
109
+ findings: z.array(deploymentFindingSchema).readonly(), steps: z.array(deploymentStepSchema).readonly(), outcome: z.enum(['aligned', 'ready', 'blocked']),
110
+ // Submitted operations are never executed. Apply re-observes and returns a
111
+ // server-built plan; this field is retained only to validate the full plan
112
+ // envelope and obtain its fingerprint.
113
+ operations: z.object({
114
+ sourceToActive: z.array(z.custom<MigrationStep>()).readonly(),
115
+ provision: z.array(z.custom<MigrationStep>()).readonly(),
116
+ }),
117
+ rollbackTarget: z.object({ schemaId: z.string(), version: z.number(), hash: z.string(), strategy: z.literal('reactivate_artifact') }).nullable(),
118
+ recovery: z.enum(['rollback', 'forward_only']),
119
+ });
@@ -0,0 +1,13 @@
1
+ function canonical(value: unknown): string {
2
+ if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`;
3
+ if (value && typeof value === 'object') return `{${Object.entries(value as Record<string, unknown>).sort(([a], [b]) => a.localeCompare(b)).map(([key, item]) => `${JSON.stringify(key)}:${canonical(item)}`).join(',')}}`;
4
+ return JSON.stringify(value);
5
+ }
6
+
7
+ /** Stable non-cryptographic identity used only to detect a changed reviewed plan. */
8
+ export function deploymentFingerprint(value: unknown): string {
9
+ const input = canonical(value);
10
+ let hash = 0xcbf29ce484222325n;
11
+ for (let index = 0; index < input.length; index++) { hash ^= BigInt(input.charCodeAt(index)); hash = BigInt.asUintN(64, hash * 0x100000001b3n); }
12
+ return `plan_${hash.toString(16).padStart(16, '0')}`;
13
+ }
@@ -0,0 +1,81 @@
1
+ import type { DeploymentApplyResult, DeploymentObservation, DeploymentStep, SchemaDeploymentPlan } from './contracts.js';
2
+ import { deploymentFingerprint } from './fingerprint.js';
3
+ import { reconcileDeploymentManifest, reconcilePolicyIntent, reconcileSchemaToDatabase, reconcileSourceToActiveResult } from './reconcile.js';
4
+ import { sequenceDeployment } from './sequence.js';
5
+
6
+ export * from './contracts.js';
7
+ export { deploymentFingerprint } from './fingerprint.js';
8
+ export { reconcileClientToActive, reconcileDeploymentManifest, reconcilePolicyIntent, reconcileSchemaToDatabase, reconcileSourceToActive, reconcileSourceToActiveResult } from './reconcile.js';
9
+ export { sequenceDeployment } from './sequence.js';
10
+ export * from './backfill.js';
11
+ export * from './postgresCatalog.js';
12
+
13
+ function planStates(observation: DeploymentObservation): SchemaDeploymentPlan['states'] {
14
+ const { schema: _sourceSchema, ...source } = observation.source;
15
+ const active = observation.active ? (({ schema: _activeSchema, ...state }) => state)(observation.active) : null;
16
+ const database = observation.database ? (({ tables: _tables, ...state }) => state)(observation.database) : null;
17
+ return { source, active, database };
18
+ }
19
+
20
+ /** The one pure reconciliation skeleton every lifecycle surface projects. */
21
+ export function buildSchemaDeploymentPlan(observation: DeploymentObservation, now = new Date().toISOString()): SchemaDeploymentPlan {
22
+ const sourceToActive = reconcileSourceToActiveResult(
23
+ observation.active?.schema ?? null,
24
+ observation.source.schema,
25
+ observation.intent?.renames,
26
+ observation.intent?.backfills,
27
+ );
28
+ const provision = reconcileSourceToActiveResult(null, observation.source.schema).operations;
29
+ const findings = [
30
+ ...reconcilePolicyIntent(observation.source.schema),
31
+ ...reconcileDeploymentManifest(observation.intent?.manifest),
32
+ ...sourceToActive.findings,
33
+ ...reconcileSchemaToDatabase(observation.source.schema, observation.database, 'source_to_database'),
34
+ ...(observation.active ? reconcileSchemaToDatabase(observation.active.schema, observation.database, 'active_to_database') : []),
35
+ ...(observation.supplementalFindings ?? []),
36
+ ];
37
+ const unique = [...new Map(findings.map((finding) => [finding.id, finding])).values()].map((finding) =>
38
+ observation.intent?.acceptDestructive && finding.category === 'destructive_contract' &&
39
+ finding.code !== 'mixed_expand_contract' && finding.code !== 'contract_approval_required' && finding.code !== 'lifecycle_dependency_unsatisfied'
40
+ ? { ...finding, severity: 'warning' as const, action: `${finding.action} Destructive intent was explicitly accepted for this reviewed plan.` }
41
+ : finding
42
+ );
43
+ const steps = sequenceDeployment(unique);
44
+ const blocking = unique.some(({ severity }) => severity === 'blocker' || severity === 'error');
45
+ const meaningful = unique.some(({ category }) => category !== 'advisory');
46
+ const states = planStates(observation);
47
+ const destructive = unique.some(({ category }) => category === 'destructive_contract');
48
+ const rollbackTarget = observation.active && !destructive ? { schemaId: observation.active.schemaId, version: observation.active.version, hash: observation.active.hash, strategy: 'reactivate_artifact' as const } : null;
49
+ const fingerprint = deploymentFingerprint({
50
+ target: observation.target,
51
+ states: {
52
+ source: { hash: states.source.hash },
53
+ active: states.active ? { schemaId: states.active.schemaId, version: states.active.version, hash: states.active.hash } : null,
54
+ database: states.database ? { subject: states.database.subject, fingerprint: states.database.fingerprint, ownership: states.database.ownership } : null,
55
+ },
56
+ intent: observation.intent ?? {}, findings: unique, steps, operations: { sourceToActive: sourceToActive.operations, provision },
57
+ });
58
+ return { id: 'ablo-schema-deployment-plan-v1', mode: 'plan', createdAt: now, fingerprint, target: observation.target, states, findings: unique, steps, operations: { sourceToActive: sourceToActive.operations, provision }, outcome: blocking ? 'blocked' : meaningful ? 'ready' : 'aligned', rollbackTarget, recovery: rollbackTarget ? 'rollback' : 'forward_only' };
59
+ }
60
+
61
+ export interface SchemaDeploymentLifecycleEffects {
62
+ readonly observe: () => Promise<DeploymentObservation>;
63
+ readonly approve?: (plan: SchemaDeploymentPlan) => Promise<boolean>;
64
+ readonly apply?: (step: DeploymentStep, plan: SchemaDeploymentPlan) => Promise<void>;
65
+ readonly record?: (result: Omit<DeploymentApplyResult, 'recorded'>) => Promise<void>;
66
+ }
67
+
68
+ /** One observe → reconcile → sequence → approve → apply → verify → record path. */
69
+ export async function runSchemaDeploymentLifecycle(effects: SchemaDeploymentLifecycleEffects, mode: 'plan' | 'apply' = 'plan'): Promise<SchemaDeploymentPlan | DeploymentApplyResult> {
70
+ const plan = buildSchemaDeploymentPlan(await effects.observe());
71
+ if (mode === 'plan') return plan;
72
+ if (plan.outcome === 'blocked') throw new Error(`schema deployment plan ${plan.fingerprint} is blocked`);
73
+ if (!effects.apply) throw new Error('schema deployment apply effect is not configured');
74
+ if (effects.approve && !(await effects.approve(plan))) throw new Error('schema deployment was not approved');
75
+ const appliedStepIds: string[] = [];
76
+ for (const step of plan.steps) if (step.executableByAblo && step.status === 'ready') { await effects.apply(step, plan); appliedStepIds.push(step.id); }
77
+ const verification = buildSchemaDeploymentPlan(await effects.observe());
78
+ const unrecorded = { plan, appliedStepIds, verification };
79
+ await effects.record?.(unrecorded);
80
+ return { ...unrecorded, recorded: effects.record !== undefined };
81
+ }