@void/deploy-core 0.22.0 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/index.d.mts +83 -1
- package/dist/index.mjs +208 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -9,6 +9,10 @@ manifest validation, and provider-neutral resource planning. It has no
|
|
|
9
9
|
dependency on either deployment target and must remain usable from Node.js and
|
|
10
10
|
Cloudflare Workers.
|
|
11
11
|
|
|
12
|
+
SQL migration tokenization, classification, and canonical hash inputs are shared
|
|
13
|
+
here. Each target supplies its hashing implementation and independently validates
|
|
14
|
+
submitted plans; CLI execution policy stays in the CLI.
|
|
15
|
+
|
|
12
16
|
The package also defines the adapter boundary implemented by deployment
|
|
13
17
|
targets. Target-specific orchestration belongs in `void` or `platform/`, not
|
|
14
18
|
here.
|
package/dist/index.d.mts
CHANGED
|
@@ -166,6 +166,88 @@ export declare function createDeployUploadPayload(artifact: DeployArtifact, meta
|
|
|
166
166
|
preflight: "true" | null;
|
|
167
167
|
}): DeployUploadPayload;
|
|
168
168
|
//#endregion
|
|
169
|
+
//#region src/canonical-json.d.ts
|
|
170
|
+
export declare function canonicalJson(value: unknown): string;
|
|
171
|
+
//#endregion
|
|
172
|
+
//#region src/sql-tokenize.d.ts
|
|
173
|
+
/**
|
|
174
|
+
* Char-by-char SQL tokenizer shared by the destructive-SQL guard
|
|
175
|
+
* (projects.ts) and the single-statement classifier (remote-runner.ts).
|
|
176
|
+
*
|
|
177
|
+
* Walks the input and yields contiguous segments. `sql` runs are
|
|
178
|
+
* emitted as `{ kind: 'sql', start, end }` so callers can `sql.slice`
|
|
179
|
+
* without character-by-character allocations. Special regions are
|
|
180
|
+
* yielded whole with their delimiters included in `[start, end)`.
|
|
181
|
+
*
|
|
182
|
+
* Lexical forms handled (SQLite / drizzle-kit SQLite output):
|
|
183
|
+
* - `-- line` line comments (terminate at `\n` or EOF)
|
|
184
|
+
* - `/* block *\/` block comments (tolerate unterminated — span to EOF)
|
|
185
|
+
* - `'...'` string literals with `''` doubled-quote escape
|
|
186
|
+
* - `"..."` / `` `...` `` identifier quotes with `""` / `` `` `` escape
|
|
187
|
+
* - `[...]` bracket identifiers, NO `]]` escape — a single
|
|
188
|
+
* `]` closes the identifier (SQLite semantics;
|
|
189
|
+
* differs from MS SQL)
|
|
190
|
+
*
|
|
191
|
+
* Not handled — intentional, both current callers target SQLite only:
|
|
192
|
+
* - Postgres `$tag$ ... $tag$` dollar-quoting
|
|
193
|
+
* - Postgres `E'...'` extended string literals
|
|
194
|
+
*
|
|
195
|
+
* Adding a new quoting form anywhere should land here so both callers
|
|
196
|
+
* stay in lockstep.
|
|
197
|
+
*/
|
|
198
|
+
type SqlSegment = {
|
|
199
|
+
kind: "sql";
|
|
200
|
+
start: number;
|
|
201
|
+
end: number;
|
|
202
|
+
} | {
|
|
203
|
+
kind: "line-comment";
|
|
204
|
+
start: number;
|
|
205
|
+
end: number;
|
|
206
|
+
} | {
|
|
207
|
+
kind: "block-comment";
|
|
208
|
+
start: number;
|
|
209
|
+
end: number;
|
|
210
|
+
} | {
|
|
211
|
+
kind: "string";
|
|
212
|
+
start: number;
|
|
213
|
+
end: number;
|
|
214
|
+
} | {
|
|
215
|
+
kind: "ident-quote";
|
|
216
|
+
open: "\"" | "`" | "[";
|
|
217
|
+
start: number;
|
|
218
|
+
end: number;
|
|
219
|
+
};
|
|
220
|
+
export declare function tokenizeSql(sql: string): Generator<SqlSegment>;
|
|
221
|
+
//#endregion
|
|
222
|
+
//#region src/migration-plan.d.ts
|
|
223
|
+
export declare const MIGRATION_CLASSIFIER_VERSION = 1;
|
|
224
|
+
export declare const UNSPLIT_MULTI_STATEMENT_PRAGMA = "-- void:allow-unsplit-multi-statement";
|
|
225
|
+
type SqlMigrationClassification = {
|
|
226
|
+
kind: "single-statement";
|
|
227
|
+
stability: "stable";
|
|
228
|
+
} | {
|
|
229
|
+
kind: "breakpoint-framed";
|
|
230
|
+
stability: "stable";
|
|
231
|
+
statements: Array<string>;
|
|
232
|
+
} | {
|
|
233
|
+
kind: "legacy-unsplit-multi-statement";
|
|
234
|
+
stability: "legacy";
|
|
235
|
+
allowLegacy: boolean;
|
|
236
|
+
};
|
|
237
|
+
type MigrationPlanHashInput = SqlMigrationClassification & {
|
|
238
|
+
name: string;
|
|
239
|
+
dialect: "sqlite";
|
|
240
|
+
classifierVersion: number;
|
|
241
|
+
sqlHash: string;
|
|
242
|
+
};
|
|
243
|
+
export declare function canonicalSql(sql: string): string;
|
|
244
|
+
export declare function hasStandalonePragma(sql: string, pragma: string): boolean;
|
|
245
|
+
export declare function isSingleStatement(sql: string): boolean;
|
|
246
|
+
/** Classify SQL without imposing the caller's execution policy or hashing implementation. */
|
|
247
|
+
export declare function classifySqlMigration(sql: string): SqlMigrationClassification;
|
|
248
|
+
/** Preserve the classifier-v1 hash preimage; policy markers and warnings are not hashed. */
|
|
249
|
+
export declare function getMigrationPlanHashInput(plan: MigrationPlanHashInput): unknown;
|
|
250
|
+
//#endregion
|
|
169
251
|
//#region src/index.d.ts
|
|
170
252
|
export declare const SUPPORTED_MANIFEST_VERSIONS: ReadonlyArray<number>;
|
|
171
253
|
export type MigrationMeta = {
|
|
@@ -407,4 +489,4 @@ export type DeployEvent = {
|
|
|
407
489
|
step: string;
|
|
408
490
|
};
|
|
409
491
|
//#endregion
|
|
410
|
-
export type { DeployAppType, DeployArtifact, DeployAssetManifestEntry, DeployAssetUploadRequest, DeployCapabilitySupport, DeployIntent, DeployResource, DeployResourcePlan, DeployRuntimeFeatures, DeploySourceMetadata, DeployTarget, DeployTargetAdapter, DeployTargetCapabilities, DeployUploadPayload };
|
|
492
|
+
export type { DeployAppType, DeployArtifact, DeployAssetManifestEntry, DeployAssetUploadRequest, DeployCapabilitySupport, DeployIntent, DeployResource, DeployResourcePlan, DeployRuntimeFeatures, DeploySourceMetadata, DeployTarget, DeployTargetAdapter, DeployTargetCapabilities, DeployUploadPayload, MigrationPlanHashInput, SqlMigrationClassification, SqlSegment };
|
package/dist/index.mjs
CHANGED
|
@@ -395,6 +395,213 @@ function createDeployUploadPayload(artifact, metadata) {
|
|
|
395
395
|
};
|
|
396
396
|
}
|
|
397
397
|
//#endregion
|
|
398
|
+
//#region src/canonical-json.ts
|
|
399
|
+
function canonicalJson(value) {
|
|
400
|
+
if (value === null) return "null";
|
|
401
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return JSON.stringify(value);
|
|
402
|
+
if (Array.isArray(value)) return `[${value.map((entry) => canonicalJson(entry)).join(",")}]`;
|
|
403
|
+
if (typeof value === "object") {
|
|
404
|
+
const object = value;
|
|
405
|
+
return `{${Object.keys(object).sort().filter((key) => object[key] !== void 0).map((key) => `${JSON.stringify(key)}:${canonicalJson(object[key])}`).join(",")}}`;
|
|
406
|
+
}
|
|
407
|
+
throw new TypeError(`Unsupported canonical JSON value: ${typeof value}`);
|
|
408
|
+
}
|
|
409
|
+
//#endregion
|
|
410
|
+
//#region src/sql-tokenize.ts
|
|
411
|
+
function* tokenizeSql(sql) {
|
|
412
|
+
const sqlLength = sql.length;
|
|
413
|
+
let index = 0;
|
|
414
|
+
let runStart = 0;
|
|
415
|
+
const flushRun = (upTo) => {
|
|
416
|
+
if (upTo > runStart) return {
|
|
417
|
+
kind: "sql",
|
|
418
|
+
start: runStart,
|
|
419
|
+
end: upTo
|
|
420
|
+
};
|
|
421
|
+
};
|
|
422
|
+
while (index < sqlLength) {
|
|
423
|
+
const character = sql[index];
|
|
424
|
+
const next = index + 1 < sqlLength ? sql[index + 1] : "";
|
|
425
|
+
if (character === "-" && next === "-") {
|
|
426
|
+
const run = flushRun(index);
|
|
427
|
+
if (run) yield run;
|
|
428
|
+
let scanIndex = index + 2;
|
|
429
|
+
while (scanIndex < sqlLength && sql[scanIndex] !== "\n") scanIndex++;
|
|
430
|
+
yield {
|
|
431
|
+
kind: "line-comment",
|
|
432
|
+
start: index,
|
|
433
|
+
end: scanIndex
|
|
434
|
+
};
|
|
435
|
+
index = scanIndex;
|
|
436
|
+
runStart = index;
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
if (character === "/" && next === "*") {
|
|
440
|
+
const run = flushRun(index);
|
|
441
|
+
if (run) yield run;
|
|
442
|
+
let scanIndex = index + 2;
|
|
443
|
+
while (scanIndex < sqlLength - 1 && !(sql[scanIndex] === "*" && sql[scanIndex + 1] === "/")) scanIndex++;
|
|
444
|
+
const end = Math.min(scanIndex + 2, sqlLength);
|
|
445
|
+
yield {
|
|
446
|
+
kind: "block-comment",
|
|
447
|
+
start: index,
|
|
448
|
+
end
|
|
449
|
+
};
|
|
450
|
+
index = end;
|
|
451
|
+
runStart = index;
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
if (character === "'") {
|
|
455
|
+
const run = flushRun(index);
|
|
456
|
+
if (run) yield run;
|
|
457
|
+
let scanIndex = index + 1;
|
|
458
|
+
while (scanIndex < sqlLength) {
|
|
459
|
+
if (sql[scanIndex] === "'" && sql[scanIndex + 1] === "'") {
|
|
460
|
+
scanIndex += 2;
|
|
461
|
+
continue;
|
|
462
|
+
}
|
|
463
|
+
if (sql[scanIndex] === "'") {
|
|
464
|
+
scanIndex++;
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
scanIndex++;
|
|
468
|
+
}
|
|
469
|
+
yield {
|
|
470
|
+
kind: "string",
|
|
471
|
+
start: index,
|
|
472
|
+
end: scanIndex
|
|
473
|
+
};
|
|
474
|
+
index = scanIndex;
|
|
475
|
+
runStart = index;
|
|
476
|
+
continue;
|
|
477
|
+
}
|
|
478
|
+
if (character === "\"" || character === "`" || character === "[") {
|
|
479
|
+
const run = flushRun(index);
|
|
480
|
+
if (run) yield run;
|
|
481
|
+
const close = character === "[" ? "]" : character;
|
|
482
|
+
let scanIndex = index + 1;
|
|
483
|
+
while (scanIndex < sqlLength) {
|
|
484
|
+
if (close !== "]" && sql[scanIndex] === close && sql[scanIndex + 1] === close) {
|
|
485
|
+
scanIndex += 2;
|
|
486
|
+
continue;
|
|
487
|
+
}
|
|
488
|
+
if (sql[scanIndex] === close) {
|
|
489
|
+
scanIndex++;
|
|
490
|
+
break;
|
|
491
|
+
}
|
|
492
|
+
scanIndex++;
|
|
493
|
+
}
|
|
494
|
+
yield {
|
|
495
|
+
kind: "ident-quote",
|
|
496
|
+
open: character,
|
|
497
|
+
start: index,
|
|
498
|
+
end: scanIndex
|
|
499
|
+
};
|
|
500
|
+
index = scanIndex;
|
|
501
|
+
runStart = index;
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
index++;
|
|
505
|
+
}
|
|
506
|
+
const tail = flushRun(sqlLength);
|
|
507
|
+
if (tail) yield tail;
|
|
508
|
+
}
|
|
509
|
+
//#endregion
|
|
510
|
+
//#region src/migration-plan.ts
|
|
511
|
+
const MIGRATION_CLASSIFIER_VERSION = 1;
|
|
512
|
+
const UNSPLIT_MULTI_STATEMENT_PRAGMA = "-- void:allow-unsplit-multi-statement";
|
|
513
|
+
function canonicalSql(sql) {
|
|
514
|
+
return (sql.charCodeAt(0) === 65279 ? sql.slice(1) : sql).replace(/\r\n?/g, "\n");
|
|
515
|
+
}
|
|
516
|
+
function isStandalonePragmaLine(line, pragma) {
|
|
517
|
+
return line.trim() === pragma;
|
|
518
|
+
}
|
|
519
|
+
function hasStandalonePragma(sql, pragma) {
|
|
520
|
+
return canonicalSql(sql).split("\n").some((line) => isStandalonePragmaLine(line, pragma));
|
|
521
|
+
}
|
|
522
|
+
function isBreakpointComment(text) {
|
|
523
|
+
return text.trim() === "--> statement-breakpoint";
|
|
524
|
+
}
|
|
525
|
+
function splitBreakpointFramedSql(sql) {
|
|
526
|
+
const normalized = canonicalSql(sql);
|
|
527
|
+
const statements = [];
|
|
528
|
+
let cursor = 0;
|
|
529
|
+
for (const segment of tokenizeSql(normalized)) {
|
|
530
|
+
if (segment.kind !== "line-comment") continue;
|
|
531
|
+
if (isBreakpointComment(normalized.slice(segment.start, segment.end))) {
|
|
532
|
+
const statement = normalized.slice(cursor, segment.start).trim();
|
|
533
|
+
if (statement) statements.push(statement);
|
|
534
|
+
cursor = segment.end;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
const statement = normalized.slice(cursor).trim();
|
|
538
|
+
if (statement) statements.push(statement);
|
|
539
|
+
return statements;
|
|
540
|
+
}
|
|
541
|
+
function hasStatementBreakpointOutsideLiterals(sql) {
|
|
542
|
+
const normalized = canonicalSql(sql);
|
|
543
|
+
for (const segment of tokenizeSql(normalized)) {
|
|
544
|
+
if (segment.kind !== "line-comment") continue;
|
|
545
|
+
if (isBreakpointComment(normalized.slice(segment.start, segment.end))) return true;
|
|
546
|
+
}
|
|
547
|
+
return false;
|
|
548
|
+
}
|
|
549
|
+
function isSingleStatement(sql) {
|
|
550
|
+
const normalized = canonicalSql(sql);
|
|
551
|
+
let statements = 0;
|
|
552
|
+
let inStatement = false;
|
|
553
|
+
for (const segment of tokenizeSql(normalized)) {
|
|
554
|
+
if (segment.kind === "line-comment" || segment.kind === "block-comment") continue;
|
|
555
|
+
if (segment.kind === "string" || segment.kind === "ident-quote") {
|
|
556
|
+
inStatement = true;
|
|
557
|
+
continue;
|
|
558
|
+
}
|
|
559
|
+
for (let index = segment.start; index < segment.end; index++) {
|
|
560
|
+
const character = normalized[index];
|
|
561
|
+
if (character === ";") {
|
|
562
|
+
if (inStatement) {
|
|
563
|
+
statements++;
|
|
564
|
+
inStatement = false;
|
|
565
|
+
}
|
|
566
|
+
continue;
|
|
567
|
+
}
|
|
568
|
+
if (character !== " " && character !== " " && character !== "\n" && character !== "\r") inStatement = true;
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (inStatement) statements++;
|
|
572
|
+
return statements === 1;
|
|
573
|
+
}
|
|
574
|
+
/** Classify SQL without imposing the caller's execution policy or hashing implementation. */
|
|
575
|
+
function classifySqlMigration(sql) {
|
|
576
|
+
if (hasStatementBreakpointOutsideLiterals(sql)) return {
|
|
577
|
+
kind: "breakpoint-framed",
|
|
578
|
+
stability: "stable",
|
|
579
|
+
statements: splitBreakpointFramedSql(sql)
|
|
580
|
+
};
|
|
581
|
+
if (isSingleStatement(sql)) return {
|
|
582
|
+
kind: "single-statement",
|
|
583
|
+
stability: "stable"
|
|
584
|
+
};
|
|
585
|
+
return {
|
|
586
|
+
kind: "legacy-unsplit-multi-statement",
|
|
587
|
+
stability: "legacy",
|
|
588
|
+
allowLegacy: hasStandalonePragma(sql, UNSPLIT_MULTI_STATEMENT_PRAGMA)
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
/** Preserve the classifier-v1 hash preimage; policy markers and warnings are not hashed. */
|
|
592
|
+
function getMigrationPlanHashInput(plan) {
|
|
593
|
+
const base = {
|
|
594
|
+
classifierVersion: plan.classifierVersion,
|
|
595
|
+
dialect: plan.dialect,
|
|
596
|
+
kind: plan.kind,
|
|
597
|
+
name: plan.name,
|
|
598
|
+
sqlHash: plan.sqlHash,
|
|
599
|
+
stability: plan.stability
|
|
600
|
+
};
|
|
601
|
+
if (plan.kind === "breakpoint-framed") base.statements = plan.statements;
|
|
602
|
+
return base;
|
|
603
|
+
}
|
|
604
|
+
//#endregion
|
|
398
605
|
//#region src/index.ts
|
|
399
606
|
const SUPPORTED_MANIFEST_VERSIONS = [3];
|
|
400
607
|
/**
|
|
@@ -475,4 +682,4 @@ function isSandboxRegistryImage(image) {
|
|
|
475
682
|
return /^registry\.cloudflare\.com\/[^/@\s]+\/[^@\s]+@sha256:[a-f\d]{64}$/.test(image);
|
|
476
683
|
}
|
|
477
684
|
//#endregion
|
|
478
|
-
export { DEPLOY_ARTIFACT_VERSION, MANIFEST_REDIRECT_RULE_STATUSES, MAX_REVALIDATE_QUERY_ALLOWLIST_ENTRIES, MAX_ROUTING_RULES, SUPPORTED_MANIFEST_VERSIONS, appendSqliteClassMigration, createDeployArtifact, createDeployIntent, createDeployResourcePlan, createDeployUploadPayload, isSandboxRegistryImage, planWebSocketMigrations, validateManifestShape };
|
|
685
|
+
export { DEPLOY_ARTIFACT_VERSION, MANIFEST_REDIRECT_RULE_STATUSES, MAX_REVALIDATE_QUERY_ALLOWLIST_ENTRIES, MAX_ROUTING_RULES, MIGRATION_CLASSIFIER_VERSION, SUPPORTED_MANIFEST_VERSIONS, UNSPLIT_MULTI_STATEMENT_PRAGMA, appendSqliteClassMigration, canonicalJson, canonicalSql, classifySqlMigration, createDeployArtifact, createDeployIntent, createDeployResourcePlan, createDeployUploadPayload, getMigrationPlanHashInput, hasStandalonePragma, isSandboxRegistryImage, isSingleStatement, planWebSocketMigrations, tokenizeSql, validateManifestShape };
|