@void/deploy-core 0.21.9 → 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 +87 -3
- package/dist/index.mjs +212 -1
- package/package.json +2 -2
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 = {
|
|
@@ -263,6 +345,8 @@ export type SandboxMigrationMeta = {
|
|
|
263
345
|
newSqliteClasses?: ReadonlyArray<string>;
|
|
264
346
|
deletedClasses?: ReadonlyArray<string>;
|
|
265
347
|
};
|
|
348
|
+
/** Named images under the Durable Object scheduling policy must be digest-pinned. */
|
|
349
|
+
export declare function isSandboxRegistryImage(image: string): boolean;
|
|
266
350
|
export type SandboxManifestMeta = {
|
|
267
351
|
bindingName: string;
|
|
268
352
|
className: string;
|
|
@@ -273,10 +357,10 @@ export type SandboxManifestMeta = {
|
|
|
273
357
|
migrations?: ReadonlyArray<SandboxMigrationMeta>;
|
|
274
358
|
/** Written by the managed platform, never accepted from deployment clients. */
|
|
275
359
|
runtime?: {
|
|
276
|
-
protocol: 1;
|
|
360
|
+
protocol: 1 | 2;
|
|
277
361
|
};
|
|
278
362
|
/** Managed transport implemented by the application's bundled Void runtime. */
|
|
279
|
-
controllerProtocol?: 1;
|
|
363
|
+
controllerProtocol?: 1 | 2;
|
|
280
364
|
};
|
|
281
365
|
export type LiveMigrationMeta = {
|
|
282
366
|
tag: string;
|
|
@@ -405,4 +489,4 @@ export type DeployEvent = {
|
|
|
405
489
|
step: string;
|
|
406
490
|
};
|
|
407
491
|
//#endregion
|
|
408
|
-
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
|
/**
|
|
@@ -470,5 +677,9 @@ function sortUniqueStrings(values) {
|
|
|
470
677
|
function sameStringSet(a, b) {
|
|
471
678
|
return a.length === b.length && a.every((value, index) => value === b[index]);
|
|
472
679
|
}
|
|
680
|
+
/** Named images under the Durable Object scheduling policy must be digest-pinned. */
|
|
681
|
+
function isSandboxRegistryImage(image) {
|
|
682
|
+
return /^registry\.cloudflare\.com\/[^/@\s]+\/[^@\s]+@sha256:[a-f\d]{64}$/.test(image);
|
|
683
|
+
}
|
|
473
684
|
//#endregion
|
|
474
|
-
export { DEPLOY_ARTIFACT_VERSION, MANIFEST_REDIRECT_RULE_STATUSES, MAX_REVALIDATE_QUERY_ALLOWLIST_ENTRIES, MAX_ROUTING_RULES, SUPPORTED_MANIFEST_VERSIONS, appendSqliteClassMigration, createDeployArtifact, createDeployIntent, createDeployResourcePlan, createDeployUploadPayload, 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 };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@void/deploy-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"repository": {
|
|
5
5
|
"type": "git",
|
|
6
6
|
"url": "git+https://github.com/voidzero-dev/void.git",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"typescript": "7.0.2",
|
|
24
|
-
"vite-plus": "0.
|
|
24
|
+
"vite-plus": "1.0.0"
|
|
25
25
|
},
|
|
26
26
|
"scripts": {
|
|
27
27
|
"build": "vp pack",
|