-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathdeploy-bundle.zod.ts
More file actions
227 lines (172 loc) · 8.59 KB
/
Copy pathdeploy-bundle.zod.ts
File metadata and controls
227 lines (172 loc) · 8.59 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
import { z } from 'zod';
/**
* Deploy Bundle Protocol
*
* Defines the schemas for metadata-driven deployment:
* Schema Push → Zod Validate → Diff → DDL Sync → Register
*
* This eliminates traditional CI/CD pipelines for schema changes.
* A "deploy" is a bundle of metadata (objects, views, flows, permissions)
* that is validated, diffed against the current state, and applied
* as DDL migrations directly to the tenant database.
*
* Target: 2-5 second deploys vs. 2-15 minute traditional Docker/CI/CD.
*/
// ==========================================================================
// 1. Deploy Status
// ==========================================================================
/**
* Deployment lifecycle status.
*/
export const DeployStatusEnum = z.enum([
'validating', // Zod schema validation in progress
'diffing', // Comparing desired state vs current state
'migrating', // Executing DDL statements
'registering', // Updating metadata registry
'ready', // Deployment complete and live
'failed', // Deployment failed at some stage
'rolling_back', // Rollback in progress
]).describe('Deployment lifecycle status');
export type DeployStatus = z.infer<typeof DeployStatusEnum>;
// ==========================================================================
// 2. Deploy Diff
// ==========================================================================
/**
* Schema change descriptor for a single entity (object/field).
*/
export const SchemaChangeSchema = z.object({
/** Type of entity being changed */
entityType: z.enum(['object', 'field', 'index', 'view', 'flow', 'permission']).describe('Entity type'),
/** Name of the entity */
entityName: z.string().min(1).describe('Entity name'),
/** Parent entity name (e.g., object name for a field change) */
parentEntity: z.string().optional().describe('Parent entity name'),
/** Type of change */
changeType: z.enum(['added', 'modified', 'removed']).describe('Change type'),
/** Previous value (for modified/removed) */
oldValue: z.unknown().optional().describe('Previous value'),
/** New value (for added/modified) */
newValue: z.unknown().optional().describe('New value'),
}).describe('Individual schema change');
export type SchemaChange = z.infer<typeof SchemaChangeSchema>;
/**
* Deploy Diff — what changed between current and desired state.
*/
export const DeployDiffSchema = z.object({
/** List of all schema changes */
changes: z.array(SchemaChangeSchema).default([]).describe('List of schema changes'),
/** Summary counts */
summary: z.object({
added: z.number().int().min(0).default(0).describe('Number of added entities'),
modified: z.number().int().min(0).default(0).describe('Number of modified entities'),
removed: z.number().int().min(0).default(0).describe('Number of removed entities'),
}).describe('Change summary counts'),
/** Whether the diff contains breaking changes (e.g., column removal) */
hasBreakingChanges: z.boolean().default(false).describe('Whether diff contains breaking changes'),
}).describe('Schema diff between current and desired state');
export type DeployDiff = z.infer<typeof DeployDiffSchema>;
// ==========================================================================
// 3. Migration Plan
// ==========================================================================
/**
* A single DDL migration statement.
*/
export const MigrationStatementSchema = z.object({
/** SQL DDL statement to execute */
sql: z.string().min(1).describe('SQL DDL statement'),
/** Whether this statement is reversible */
reversible: z.boolean().default(true).describe('Whether the statement can be reversed'),
/** Reverse SQL statement (for rollback) */
rollbackSql: z.string().optional().describe('Reverse SQL for rollback'),
/** Execution order (lower = earlier) */
order: z.number().int().min(0).describe('Execution order'),
}).describe('Single DDL migration statement');
export type MigrationStatement = z.infer<typeof MigrationStatementSchema>;
/**
* Migration Plan — ordered list of DDL statements to execute.
*/
export const MigrationPlanSchema = z.object({
/** Ordered list of migration statements */
statements: z.array(MigrationStatementSchema).default([]).describe('Ordered DDL statements'),
/** SQL dialect the statements are written for */
dialect: z.string().min(1).describe('Target SQL dialect'),
/** Whether the entire plan is reversible */
reversible: z.boolean().default(true).describe('Whether the plan can be fully rolled back'),
/** Estimated execution time in milliseconds */
estimatedDurationMs: z.number().int().min(0).optional().describe('Estimated execution time'),
}).describe('Ordered migration plan');
export type MigrationPlan = z.infer<typeof MigrationPlanSchema>;
// ==========================================================================
// 4. Deploy Validation
// ==========================================================================
/**
* Validation issue found during bundle validation.
*/
export const DeployValidationIssueSchema = z.object({
/** Severity of the issue */
severity: z.enum(['error', 'warning', 'info']).describe('Issue severity'),
/** Entity path where the issue was found */
path: z.string().describe('Entity path (e.g., objects.project_task.fields.name)'),
/** Human-readable issue description */
message: z.string().describe('Issue description'),
/** Zod error code if applicable */
code: z.string().optional().describe('Validation error code'),
}).describe('Validation issue');
export type DeployValidationIssue = z.infer<typeof DeployValidationIssueSchema>;
/**
* Zod validation result for the entire deploy bundle.
*/
export const DeployValidationResultSchema = z.object({
/** Whether the bundle passed validation */
valid: z.boolean().describe('Whether the bundle is valid'),
/** List of validation issues */
issues: z.array(DeployValidationIssueSchema).default([]).describe('Validation issues'),
/** Number of errors */
errorCount: z.number().int().min(0).default(0).describe('Number of errors'),
/** Number of warnings */
warningCount: z.number().int().min(0).default(0).describe('Number of warnings'),
}).describe('Bundle validation result');
export type DeployValidationResult = z.infer<typeof DeployValidationResultSchema>;
// ==========================================================================
// 5. Deploy Bundle & Manifest
// ==========================================================================
/**
* Deploy Manifest — metadata about the deployment.
*/
export const DeployManifestSchema = z.object({
/** Deployment version (semver) */
version: z.string().min(1).describe('Deployment version'),
/** SHA256 checksum of the bundle contents */
checksum: z.string().optional().describe('SHA256 checksum'),
/** Object definitions included in this deployment */
objects: z.array(z.string()).default([]).describe('Object names included'),
/** View definitions included */
views: z.array(z.string()).default([]).describe('View names included'),
/** Flow definitions included */
flows: z.array(z.string()).default([]).describe('Flow names included'),
/** Permission definitions included */
permissions: z.array(z.string()).default([]).describe('Permission names included'),
/** Timestamp of bundle creation (ISO 8601) */
createdAt: z.string().datetime().optional().describe('Bundle creation time'),
}).describe('Deployment manifest');
export type DeployManifest = z.infer<typeof DeployManifestSchema>;
/**
* Deploy Bundle — container for all metadata being deployed.
* This is the primary input to the deploy pipeline.
*/
export const DeployBundleSchema = z.object({
/** Bundle manifest with version and contents list */
manifest: DeployManifestSchema,
/** Object definitions (JSON-serialized ObjectStack objects) */
objects: z.array(z.record(z.string(), z.unknown())).default([]).describe('Object definitions'),
/** View definitions */
views: z.array(z.record(z.string(), z.unknown())).default([]).describe('View definitions'),
/** Flow definitions */
flows: z.array(z.record(z.string(), z.unknown())).default([]).describe('Flow definitions'),
/** Permission definitions */
permissions: z.array(z.record(z.string(), z.unknown())).default([]).describe('Permission definitions'),
/** Seed data records to populate after schema migration */
seedData: z.array(z.record(z.string(), z.unknown())).default([]).describe('Seed data records'),
}).describe('Deploy bundle containing all metadata for deployment');
export type DeployBundle = z.infer<typeof DeployBundleSchema>;