@pgpmjs/core 7.0.0 → 7.2.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 (102) hide show
  1. package/bundle/apply.d.ts +93 -0
  2. package/bundle/apply.js +137 -0
  3. package/bundle/index.d.ts +12 -0
  4. package/bundle/index.js +31 -0
  5. package/core/class/pgpm.d.ts +2 -2
  6. package/core/class/pgpm.js +6 -6
  7. package/esm/bundle/apply.js +131 -0
  8. package/esm/bundle/index.js +15 -0
  9. package/esm/core/class/pgpm.js +6 -6
  10. package/esm/index.js +7 -3
  11. package/esm/migrate/client.js +1 -1
  12. package/esm/migrate/utils/hash.js +3 -6
  13. package/esm/modules/modules.js +1 -1
  14. package/esm/packaging/package.js +40 -24
  15. package/esm/rebundle/index.js +4 -0
  16. package/esm/rebundle/module.js +111 -0
  17. package/esm/rebundle/rebundle.js +170 -0
  18. package/esm/rebundle/workspace.js +170 -0
  19. package/esm/refactor/rename.js +5 -27
  20. package/esm/resolution/deps.js +2 -2
  21. package/esm/resolution/resolve.js +2 -2
  22. package/esm/slice/slice.js +1 -1
  23. package/esm/workspace/minimal.js +73 -0
  24. package/index.d.ts +6 -2
  25. package/index.js +8 -4
  26. package/migrate/client.js +1 -1
  27. package/migrate/utils/hash.d.ts +2 -4
  28. package/migrate/utils/hash.js +4 -8
  29. package/modules/modules.d.ts +1 -1
  30. package/modules/modules.js +1 -1
  31. package/package.json +4 -2
  32. package/packaging/package.d.ts +23 -1
  33. package/packaging/package.js +42 -25
  34. package/rebundle/index.d.ts +4 -0
  35. package/{files/sql-scripts → rebundle}/index.js +4 -2
  36. package/rebundle/module.d.ts +23 -0
  37. package/rebundle/module.js +116 -0
  38. package/rebundle/rebundle.d.ts +26 -0
  39. package/rebundle/rebundle.js +175 -0
  40. package/rebundle/types.d.ts +185 -0
  41. package/rebundle/workspace.d.ts +15 -0
  42. package/rebundle/workspace.js +173 -0
  43. package/refactor/rename.d.ts +2 -8
  44. package/refactor/rename.js +6 -29
  45. package/resolution/deps.d.ts +1 -1
  46. package/resolution/deps.js +2 -2
  47. package/resolution/resolve.js +2 -2
  48. package/slice/slice.d.ts +1 -1
  49. package/slice/slice.js +1 -1
  50. package/slice/types.d.ts +1 -1
  51. package/workspace/minimal.d.ts +64 -0
  52. package/workspace/minimal.js +82 -0
  53. package/esm/files/extension/index.js +0 -3
  54. package/esm/files/extension/reader.js +0 -79
  55. package/esm/files/extension/writer.js +0 -99
  56. package/esm/files/index.js +0 -6
  57. package/esm/files/plan/generator.js +0 -82
  58. package/esm/files/plan/index.js +0 -5
  59. package/esm/files/plan/parser.js +0 -297
  60. package/esm/files/plan/validators.js +0 -181
  61. package/esm/files/plan/writer.js +0 -142
  62. package/esm/files/sql/header.js +0 -259
  63. package/esm/files/sql/index.js +0 -2
  64. package/esm/files/sql/writer.js +0 -107
  65. package/esm/files/sql-scripts/index.js +0 -2
  66. package/esm/files/sql-scripts/reader.js +0 -19
  67. package/esm/files/types/index.js +0 -1
  68. package/esm/utils/author.js +0 -11
  69. package/files/extension/index.d.ts +0 -2
  70. package/files/extension/index.js +0 -19
  71. package/files/extension/reader.d.ts +0 -24
  72. package/files/extension/reader.js +0 -86
  73. package/files/extension/writer.d.ts +0 -48
  74. package/files/extension/writer.js +0 -107
  75. package/files/index.d.ts +0 -5
  76. package/files/index.js +0 -22
  77. package/files/plan/generator.d.ts +0 -23
  78. package/files/plan/generator.js +0 -90
  79. package/files/plan/index.d.ts +0 -4
  80. package/files/plan/index.js +0 -21
  81. package/files/plan/parser.d.ts +0 -28
  82. package/files/plan/parser.js +0 -304
  83. package/files/plan/validators.d.ts +0 -52
  84. package/files/plan/validators.js +0 -187
  85. package/files/plan/writer.d.ts +0 -27
  86. package/files/plan/writer.js +0 -152
  87. package/files/sql/header.d.ts +0 -114
  88. package/files/sql/header.js +0 -266
  89. package/files/sql/index.d.ts +0 -2
  90. package/files/sql/index.js +0 -18
  91. package/files/sql/writer.d.ts +0 -12
  92. package/files/sql/writer.js +0 -114
  93. package/files/sql-scripts/index.d.ts +0 -1
  94. package/files/sql-scripts/reader.d.ts +0 -8
  95. package/files/sql-scripts/reader.js +0 -23
  96. package/files/types/index.d.ts +0 -46
  97. package/files/types/index.js +0 -17
  98. package/files/types/package.d.ts +0 -20
  99. package/utils/author.d.ts +0 -5
  100. package/utils/author.js +0 -14
  101. /package/esm/{files/types/package.js → rebundle/types.js} +0 -0
  102. /package/{files/types/package.js → rebundle/types.js} +0 -0
@@ -0,0 +1,93 @@
1
+ import { PgConfig } from 'pg-env';
2
+ import { PgpmScriptKind } from '@pgpmjs/ast/module/types';
3
+ import { BundleIssue, MigrationBundle } from '@pgpmjs/bundle';
4
+ import { PgpmMigrateOptions } from '../migrate/client';
5
+ import { DeployResult, VerifyResult } from '../migrate/types';
6
+ /** A script whose SQL references something outside the allowed namespace. */
7
+ export interface NamespaceViolation {
8
+ change: string;
9
+ kind: PgpmScriptKind;
10
+ /** Offending references reported by the caller's validator. */
11
+ references: string[];
12
+ }
13
+ /**
14
+ * A validator for transpiled bundles: given a script's SQL, return the
15
+ * references that fall *outside* the target namespace (empty = clean). Core
16
+ * stays parser-agnostic; the caller (constructive-db) supplies the AST check.
17
+ */
18
+ export type ReferenceValidator = (sql: string, ctx: {
19
+ change: string;
20
+ kind: PgpmScriptKind;
21
+ }) => string[];
22
+ /** Pre-flight analysis of an apply, computed without touching the database. */
23
+ export interface ApplyBundlePreview {
24
+ name: string;
25
+ /** Changes in deploy order. */
26
+ deployOrder: string[];
27
+ /** Digest/order/integrity problems from {@link verifyBundle}. */
28
+ bundleIssues: BundleIssue[];
29
+ /** References outside the target namespace (only when a validator is given). */
30
+ namespaceViolations: NamespaceViolation[];
31
+ /** Reverse deploy order — the revert sequence that fully undoes this apply. */
32
+ rollbackPlan: string[];
33
+ }
34
+ export interface PreviewBundleApplyOptions {
35
+ validateReferences?: ReferenceValidator;
36
+ }
37
+ /**
38
+ * Analyze a bundle apply without executing it: verify artifact integrity, run
39
+ * the optional namespace-reference validator, and derive the rollback plan.
40
+ * Pure — no database, no disk.
41
+ */
42
+ export declare function previewBundleApply(bundle: MigrationBundle, options?: PreviewBundleApplyOptions): ApplyBundlePreview;
43
+ export interface ApplyBundleOptions extends PreviewBundleApplyOptions {
44
+ /** Target database. */
45
+ config: PgConfig;
46
+ /** Analyze only; never touch the database. Default false. */
47
+ dryRun?: boolean;
48
+ /** Run `verify` after a successful deploy. Default false. */
49
+ verify?: boolean;
50
+ /**
51
+ * Wall-clock budget for the deploy (+ optional verify). On expiry the result
52
+ * is flagged `timedOut` and a {@link BundleApplyError} is thrown. The apply
53
+ * runs in a single transaction (see `useTransaction`), so the database rolls
54
+ * back rather than being left half-applied.
55
+ */
56
+ timeoutMs?: number;
57
+ /** Apply as one atomic transaction so any failure rolls back. Default true. */
58
+ useTransaction?: boolean;
59
+ /** Directory to materialize into (default: a temp dir, removed afterward). */
60
+ workDir?: string;
61
+ migrateOptions?: PgpmMigrateOptions;
62
+ /** Apply even if artifact verification fails. Default false (refuse). */
63
+ allowUnverified?: boolean;
64
+ }
65
+ /** Explicit outcome of an {@link applyBundle} call. */
66
+ export interface ApplyBundleResult {
67
+ preview: ApplyBundlePreview;
68
+ dryRun: boolean;
69
+ /** True when the deploy actually ran against the database. */
70
+ executed: boolean;
71
+ deploy?: DeployResult;
72
+ verify?: VerifyResult;
73
+ durationMs: number;
74
+ timedOut: boolean;
75
+ }
76
+ /** Thrown when an apply is refused pre-flight or exceeds its timeout. */
77
+ export declare class BundleApplyError extends Error {
78
+ readonly preview: ApplyBundlePreview;
79
+ readonly timedOut: boolean;
80
+ constructor(message: string, preview: ApplyBundlePreview, timedOut?: boolean);
81
+ }
82
+ /**
83
+ * Safely apply a {@link MigrationBundle} to a target database.
84
+ *
85
+ * A thin, safety-first wrapper over {@link PgpmMigrate}: it gates on artifact
86
+ * integrity and (optionally) namespace validation, supports a dry run, applies
87
+ * atomically in a single transaction, enforces a timeout, and returns an
88
+ * explicit result including the rollback plan.
89
+ *
90
+ * The mechanics (materialize → deploy → verify) reuse existing plumbing;
91
+ * nothing here re-implements deployment.
92
+ */
93
+ export declare function applyBundle(bundle: MigrationBundle, options: ApplyBundleOptions): Promise<ApplyBundleResult>;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BundleApplyError = void 0;
4
+ exports.previewBundleApply = previewBundleApply;
5
+ exports.applyBundle = applyBundle;
6
+ const fs_1 = require("fs");
7
+ const os_1 = require("os");
8
+ const path_1 = require("path");
9
+ const bundle_1 = require("@pgpmjs/bundle");
10
+ const client_1 = require("../migrate/client");
11
+ /**
12
+ * Analyze a bundle apply without executing it: verify artifact integrity, run
13
+ * the optional namespace-reference validator, and derive the rollback plan.
14
+ * Pure — no database, no disk.
15
+ */
16
+ function previewBundleApply(bundle, options = {}) {
17
+ const namespaceViolations = [];
18
+ if (options.validateReferences) {
19
+ for (const change of bundle.changes) {
20
+ for (const script of [change.deploy, change.revert, change.verify]) {
21
+ if (!script)
22
+ continue;
23
+ const references = options.validateReferences(script.sql, {
24
+ change: change.name,
25
+ kind: script.kind
26
+ });
27
+ if (references.length > 0) {
28
+ namespaceViolations.push({ change: change.name, kind: script.kind, references });
29
+ }
30
+ }
31
+ }
32
+ }
33
+ return {
34
+ name: bundle.manifest.name,
35
+ deployOrder: [...bundle.manifest.deployOrder],
36
+ bundleIssues: (0, bundle_1.verifyBundle)(bundle),
37
+ namespaceViolations,
38
+ rollbackPlan: [...bundle.manifest.deployOrder].reverse()
39
+ };
40
+ }
41
+ /** Thrown when an apply is refused pre-flight or exceeds its timeout. */
42
+ class BundleApplyError extends Error {
43
+ preview;
44
+ timedOut;
45
+ constructor(message, preview, timedOut = false) {
46
+ super(message);
47
+ this.preview = preview;
48
+ this.timedOut = timedOut;
49
+ this.name = 'BundleApplyError';
50
+ }
51
+ }
52
+ exports.BundleApplyError = BundleApplyError;
53
+ const TIMEOUT = Symbol('timeout');
54
+ async function withTimeout(work, ms) {
55
+ if (!ms)
56
+ return work;
57
+ let timer;
58
+ const timeout = new Promise(resolve => {
59
+ timer = setTimeout(() => resolve(TIMEOUT), ms);
60
+ });
61
+ try {
62
+ return await Promise.race([work, timeout]);
63
+ }
64
+ finally {
65
+ clearTimeout(timer);
66
+ }
67
+ }
68
+ /**
69
+ * Safely apply a {@link MigrationBundle} to a target database.
70
+ *
71
+ * A thin, safety-first wrapper over {@link PgpmMigrate}: it gates on artifact
72
+ * integrity and (optionally) namespace validation, supports a dry run, applies
73
+ * atomically in a single transaction, enforces a timeout, and returns an
74
+ * explicit result including the rollback plan.
75
+ *
76
+ * The mechanics (materialize → deploy → verify) reuse existing plumbing;
77
+ * nothing here re-implements deployment.
78
+ */
79
+ async function applyBundle(bundle, options) {
80
+ const started = Date.now();
81
+ const preview = previewBundleApply(bundle, options);
82
+ if (!options.allowUnverified && preview.bundleIssues.length > 0) {
83
+ throw new BundleApplyError(`Refusing to apply "${preview.name}": bundle failed integrity verification ` +
84
+ `(${preview.bundleIssues.length} issue(s): ${preview.bundleIssues.map(i => i.kind).join(', ')})`, preview);
85
+ }
86
+ if (preview.namespaceViolations.length > 0) {
87
+ throw new BundleApplyError(`Refusing to apply "${preview.name}": ${preview.namespaceViolations.length} ` +
88
+ `reference(s) fall outside the target namespace`, preview);
89
+ }
90
+ if (options.dryRun) {
91
+ return {
92
+ preview,
93
+ dryRun: true,
94
+ executed: false,
95
+ durationMs: Date.now() - started,
96
+ timedOut: false
97
+ };
98
+ }
99
+ const ownWorkDir = options.workDir === undefined;
100
+ const workDir = options.workDir ?? (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), 'pgpm-apply-'));
101
+ (0, bundle_1.materializeBundle)(bundle, workDir);
102
+ const migrate = new client_1.PgpmMigrate(options.config, options.migrateOptions);
103
+ const useTransaction = options.useTransaction ?? true;
104
+ try {
105
+ const run = (async () => {
106
+ const deploy = await migrate.deploy({ modulePath: workDir, useTransaction });
107
+ let verify;
108
+ if (options.verify) {
109
+ verify = await migrate.verify({ modulePath: workDir });
110
+ }
111
+ return { deploy, verify };
112
+ })();
113
+ const outcome = await withTimeout(run, options.timeoutMs);
114
+ if (outcome === TIMEOUT) {
115
+ throw new BundleApplyError(`Apply of "${preview.name}" exceeded ${options.timeoutMs}ms`, preview, true);
116
+ }
117
+ return {
118
+ preview,
119
+ dryRun: false,
120
+ executed: true,
121
+ deploy: outcome.deploy,
122
+ verify: outcome.verify,
123
+ durationMs: Date.now() - started,
124
+ timedOut: false
125
+ };
126
+ }
127
+ finally {
128
+ if (ownWorkDir) {
129
+ try {
130
+ (0, fs_1.rmSync)(workDir, { recursive: true, force: true });
131
+ }
132
+ catch {
133
+ // best-effort cleanup
134
+ }
135
+ }
136
+ }
137
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * pgpm migration bundles: the portable, content-addressed artifact layer.
3
+ *
4
+ * A {@link MigrationBundle} is a deterministic snapshot of a pgpm module — plan,
5
+ * control, ordered changes, and per-change/whole-bundle sha256 digests — with an
6
+ * optional provenance manifest. It is the substrate for the `exportMigrationBundle`
7
+ * / `applyMigrationBundle` / `transpileMigrationBundle` cloud functions: build one
8
+ * from a module AST, transport or transform it, verify its digests, then
9
+ * materialize it back into a deployable module.
10
+ */
11
+ export * from '@pgpmjs/bundle';
12
+ export * from './apply';
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ /**
18
+ * pgpm migration bundles: the portable, content-addressed artifact layer.
19
+ *
20
+ * A {@link MigrationBundle} is a deterministic snapshot of a pgpm module — plan,
21
+ * control, ordered changes, and per-change/whole-bundle sha256 digests — with an
22
+ * optional provenance manifest. It is the substrate for the `exportMigrationBundle`
23
+ * / `applyMigrationBundle` / `transpileMigrationBundle` cloud functions: build one
24
+ * from a module AST, transport or transform it, verify its digests, then
25
+ * materialize it back into a deployable module.
26
+ */
27
+ // Pure artifact layer moved to @pgpmjs/bundle; re-exported here so existing
28
+ // `@pgpmjs/core` consumers keep importing the whole bundle surface unchanged.
29
+ __exportStar(require("@pgpmjs/bundle"), exports);
30
+ // applyBundle stays in core: it drives PgpmMigrate (the deploy engine).
31
+ __exportStar(require("./apply"), exports);
@@ -1,7 +1,7 @@
1
1
  import { PgpmOptions, PgpmWorkspaceConfig } from '@pgpmjs/types';
2
2
  import { PgConfig } from 'pg-env';
3
- import { ExtensionInfo } from '../../files';
4
- import { PackageAnalysisResult, RenameOptions } from '../../files/types';
3
+ import { ExtensionInfo } from '@pgpmjs/ast/files';
4
+ import { PackageAnalysisResult, RenameOptions } from '@pgpmjs/ast/files/types';
5
5
  import { ModuleMap } from '../../modules/modules';
6
6
  export declare enum PackageContext {
7
7
  Outside = "outside",
@@ -49,12 +49,12 @@ const path_1 = __importStar(require("path"));
49
49
  const pg_cache_1 = require("pg-cache");
50
50
  const yanse_1 = __importDefault(require("yanse"));
51
51
  const extensions_1 = require("../../extensions/extensions");
52
- const files_1 = require("../../files");
53
- const files_2 = require("../../files");
54
- const writer_1 = require("../../files/extension/writer");
55
- const generator_1 = require("../../files/plan/generator");
56
- const parser_1 = require("../../files/plan/parser");
57
- const validators_1 = require("../../files/plan/validators");
52
+ const files_1 = require("@pgpmjs/ast/files");
53
+ const files_2 = require("@pgpmjs/ast/files");
54
+ const writer_1 = require("@pgpmjs/ast/files/extension/writer");
55
+ const generator_1 = require("@pgpmjs/ast/files/plan/generator");
56
+ const parser_1 = require("@pgpmjs/ast/files/plan/parser");
57
+ const validators_1 = require("@pgpmjs/ast/files/plan/validators");
58
58
  const client_1 = require("../../migrate/client");
59
59
  const modules_1 = require("../../modules/modules");
60
60
  const package_1 = require("../../packaging/package");
@@ -0,0 +1,131 @@
1
+ import { mkdtempSync, rmSync } from 'fs';
2
+ import { tmpdir } from 'os';
3
+ import { join } from 'path';
4
+ import { materializeBundle, verifyBundle } from '@pgpmjs/bundle';
5
+ import { PgpmMigrate } from '../migrate/client';
6
+ /**
7
+ * Analyze a bundle apply without executing it: verify artifact integrity, run
8
+ * the optional namespace-reference validator, and derive the rollback plan.
9
+ * Pure — no database, no disk.
10
+ */
11
+ export function previewBundleApply(bundle, options = {}) {
12
+ const namespaceViolations = [];
13
+ if (options.validateReferences) {
14
+ for (const change of bundle.changes) {
15
+ for (const script of [change.deploy, change.revert, change.verify]) {
16
+ if (!script)
17
+ continue;
18
+ const references = options.validateReferences(script.sql, {
19
+ change: change.name,
20
+ kind: script.kind
21
+ });
22
+ if (references.length > 0) {
23
+ namespaceViolations.push({ change: change.name, kind: script.kind, references });
24
+ }
25
+ }
26
+ }
27
+ }
28
+ return {
29
+ name: bundle.manifest.name,
30
+ deployOrder: [...bundle.manifest.deployOrder],
31
+ bundleIssues: verifyBundle(bundle),
32
+ namespaceViolations,
33
+ rollbackPlan: [...bundle.manifest.deployOrder].reverse()
34
+ };
35
+ }
36
+ /** Thrown when an apply is refused pre-flight or exceeds its timeout. */
37
+ export class BundleApplyError extends Error {
38
+ preview;
39
+ timedOut;
40
+ constructor(message, preview, timedOut = false) {
41
+ super(message);
42
+ this.preview = preview;
43
+ this.timedOut = timedOut;
44
+ this.name = 'BundleApplyError';
45
+ }
46
+ }
47
+ const TIMEOUT = Symbol('timeout');
48
+ async function withTimeout(work, ms) {
49
+ if (!ms)
50
+ return work;
51
+ let timer;
52
+ const timeout = new Promise(resolve => {
53
+ timer = setTimeout(() => resolve(TIMEOUT), ms);
54
+ });
55
+ try {
56
+ return await Promise.race([work, timeout]);
57
+ }
58
+ finally {
59
+ clearTimeout(timer);
60
+ }
61
+ }
62
+ /**
63
+ * Safely apply a {@link MigrationBundle} to a target database.
64
+ *
65
+ * A thin, safety-first wrapper over {@link PgpmMigrate}: it gates on artifact
66
+ * integrity and (optionally) namespace validation, supports a dry run, applies
67
+ * atomically in a single transaction, enforces a timeout, and returns an
68
+ * explicit result including the rollback plan.
69
+ *
70
+ * The mechanics (materialize → deploy → verify) reuse existing plumbing;
71
+ * nothing here re-implements deployment.
72
+ */
73
+ export async function applyBundle(bundle, options) {
74
+ const started = Date.now();
75
+ const preview = previewBundleApply(bundle, options);
76
+ if (!options.allowUnverified && preview.bundleIssues.length > 0) {
77
+ throw new BundleApplyError(`Refusing to apply "${preview.name}": bundle failed integrity verification ` +
78
+ `(${preview.bundleIssues.length} issue(s): ${preview.bundleIssues.map(i => i.kind).join(', ')})`, preview);
79
+ }
80
+ if (preview.namespaceViolations.length > 0) {
81
+ throw new BundleApplyError(`Refusing to apply "${preview.name}": ${preview.namespaceViolations.length} ` +
82
+ `reference(s) fall outside the target namespace`, preview);
83
+ }
84
+ if (options.dryRun) {
85
+ return {
86
+ preview,
87
+ dryRun: true,
88
+ executed: false,
89
+ durationMs: Date.now() - started,
90
+ timedOut: false
91
+ };
92
+ }
93
+ const ownWorkDir = options.workDir === undefined;
94
+ const workDir = options.workDir ?? mkdtempSync(join(tmpdir(), 'pgpm-apply-'));
95
+ materializeBundle(bundle, workDir);
96
+ const migrate = new PgpmMigrate(options.config, options.migrateOptions);
97
+ const useTransaction = options.useTransaction ?? true;
98
+ try {
99
+ const run = (async () => {
100
+ const deploy = await migrate.deploy({ modulePath: workDir, useTransaction });
101
+ let verify;
102
+ if (options.verify) {
103
+ verify = await migrate.verify({ modulePath: workDir });
104
+ }
105
+ return { deploy, verify };
106
+ })();
107
+ const outcome = await withTimeout(run, options.timeoutMs);
108
+ if (outcome === TIMEOUT) {
109
+ throw new BundleApplyError(`Apply of "${preview.name}" exceeded ${options.timeoutMs}ms`, preview, true);
110
+ }
111
+ return {
112
+ preview,
113
+ dryRun: false,
114
+ executed: true,
115
+ deploy: outcome.deploy,
116
+ verify: outcome.verify,
117
+ durationMs: Date.now() - started,
118
+ timedOut: false
119
+ };
120
+ }
121
+ finally {
122
+ if (ownWorkDir) {
123
+ try {
124
+ rmSync(workDir, { recursive: true, force: true });
125
+ }
126
+ catch {
127
+ // best-effort cleanup
128
+ }
129
+ }
130
+ }
131
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * pgpm migration bundles: the portable, content-addressed artifact layer.
3
+ *
4
+ * A {@link MigrationBundle} is a deterministic snapshot of a pgpm module — plan,
5
+ * control, ordered changes, and per-change/whole-bundle sha256 digests — with an
6
+ * optional provenance manifest. It is the substrate for the `exportMigrationBundle`
7
+ * / `applyMigrationBundle` / `transpileMigrationBundle` cloud functions: build one
8
+ * from a module AST, transport or transform it, verify its digests, then
9
+ * materialize it back into a deployable module.
10
+ */
11
+ // Pure artifact layer moved to @pgpmjs/bundle; re-exported here so existing
12
+ // `@pgpmjs/core` consumers keep importing the whole bundle surface unchanged.
13
+ export * from '@pgpmjs/bundle';
14
+ // applyBundle stays in core: it drives PgpmMigrate (the deploy engine).
15
+ export * from './apply';
@@ -10,12 +10,12 @@ import path, { dirname, resolve } from 'path';
10
10
  import { getPgPool } from 'pg-cache';
11
11
  import yanse from 'yanse';
12
12
  import { getAvailableExtensions } from '../../extensions/extensions';
13
- import { generatePlan, writePlan, writePlanFile } from '../../files';
14
- import { getExtensionInfo, getExtensionName, getInstalledExtensions, parseControlFile, writeExtensions } from '../../files';
15
- import { generateControlFileContent, writeExtensionMakefile } from '../../files/extension/writer';
16
- import { getNow as getPlanTimestamp } from '../../files/plan/generator';
17
- import { parsePlanFile } from '../../files/plan/parser';
18
- import { isValidChangeName, isValidTagName, parseReference } from '../../files/plan/validators';
13
+ import { generatePlan, writePlan, writePlanFile } from '@pgpmjs/ast/files';
14
+ import { getExtensionInfo, getExtensionName, getInstalledExtensions, parseControlFile, writeExtensions } from '@pgpmjs/ast/files';
15
+ import { generateControlFileContent, writeExtensionMakefile } from '@pgpmjs/ast/files/extension/writer';
16
+ import { getNow as getPlanTimestamp } from '@pgpmjs/ast/files/plan/generator';
17
+ import { parsePlanFile } from '@pgpmjs/ast/files/plan/parser';
18
+ import { isValidChangeName, isValidTagName, parseReference } from '@pgpmjs/ast/files/plan/validators';
19
19
  import { PgpmMigrate } from '../../migrate/client';
20
20
  import { getExtensionsAndModules, getExtensionsAndModulesChanges, latestChange, latestChangeAndVersion } from '../../modules/modules';
21
21
  import { packageModule } from '../../packaging/package';
package/esm/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './core/class/pgpm';
2
2
  export * from './slice';
3
+ export * from './rebundle';
3
4
  export * from './extensions/extensions';
4
5
  export * from './modules/modules';
5
6
  export * from './packaging/package';
@@ -8,16 +9,19 @@ export * from './resolution/deps';
8
9
  export * from './resolution/resolve';
9
10
  export * from './workspace/paths';
10
11
  export * from './workspace/utils';
12
+ export * from './workspace/minimal';
11
13
  export * from './core/template-scaffold';
12
14
  export * from './core/boilerplate-types';
13
15
  export * from './core/boilerplate-scanner';
14
- // Export package-files functionality (now integrated into core)
15
- export * from './files';
16
+ // Re-export the module AST layer (moved to the @pgpmjs/ast leaf package)
17
+ export * from '@pgpmjs/ast/files';
18
+ export * from '@pgpmjs/ast/module';
19
+ export * from './bundle';
16
20
  export * from './refactor';
17
21
  export { cleanSql } from './migrate/clean';
18
22
  export { PgpmMigrate } from './migrate/client';
19
23
  export { PgpmInit } from './init/client';
20
24
  export * from './roles';
21
- export { parseAuthor } from './utils/author';
25
+ export { parseAuthor } from '@pgpmjs/ast';
22
26
  export { hashFile, hashString } from './migrate/utils/hash';
23
27
  export { executeQuery, withTransaction } from './migrate/utils/transaction';
@@ -3,7 +3,7 @@ import { errors, extractPgErrorFields, formatPgError, formatPgErrorFields } from
3
3
  import { readFileSync } from 'fs';
4
4
  import { dirname, join } from 'path';
5
5
  import { getPgPool } from 'pg-cache';
6
- import { parsePlanFile, parsePlanFileSimple, readScript } from '../files';
6
+ import { parsePlanFile, parsePlanFileSimple, readScript } from '@pgpmjs/ast/files';
7
7
  import { resolveDependencies } from '../resolution/deps';
8
8
  import { resolveTagToChangeName } from '../resolution/resolve';
9
9
  import { cleanSql } from './clean';
@@ -1,7 +1,10 @@
1
+ import { hashString } from '@pgpmjs/ast';
1
2
  import { createHash } from 'crypto';
2
3
  import { readFile } from 'fs/promises';
3
4
  import { parse } from 'pgsql-parser';
4
5
  import { cleanTree } from '../../packaging/package';
6
+ // Re-exported from @pgpmjs/ast so existing `@pgpmjs/core` consumers keep importing it here.
7
+ export { hashString };
5
8
  /**
6
9
  * Generate SHA256 hash of a file's contents
7
10
  */
@@ -9,12 +12,6 @@ export async function hashFile(filePath) {
9
12
  const content = await readFile(filePath, 'utf-8');
10
13
  return createHash('sha256').update(content).digest('hex');
11
14
  }
12
- /**
13
- * Generate SHA256 hash of a string
14
- */
15
- export function hashString(content) {
16
- return createHash('sha256').update(content).digest('hex');
17
- }
18
15
  /**
19
16
  * Generate SHA256 hash of a SQL file's parsed and cleaned AST
20
17
  */
@@ -1,4 +1,4 @@
1
- import { getLatestChange } from '../files';
1
+ import { getLatestChange } from '@pgpmjs/ast/files';
2
2
  import { errors } from '@pgpmjs/types';
3
3
  /**
4
4
  * Mapping from control file names (used in extensions list) to npm package names.
@@ -3,7 +3,7 @@ import { mkdirSync, readFileSync, rmSync, writeFileSync } from 'fs';
3
3
  import { relative } from 'path';
4
4
  import { deparse } from 'pgsql-deparser';
5
5
  import { parse } from 'pgsql-parser';
6
- import { getExtensionName } from '../files';
6
+ import { getExtensionName } from '@pgpmjs/ast/files';
7
7
  import { resolve, resolveWithPlan } from '../resolution/resolve';
8
8
  import { transformProps } from './transform';
9
9
  const log = new Logger('package');
@@ -15,8 +15,8 @@ export const cleanTree = (tree) => {
15
15
  location: noop,
16
16
  });
17
17
  };
18
- const filterStatements = (stmts, extension) => {
19
- if (!extension)
18
+ const filterStatements = (stmts, stripTransactions) => {
19
+ if (!stripTransactions)
20
20
  return stmts;
21
21
  return stmts.filter(node => {
22
22
  const stmt = node.stmt;
@@ -24,7 +24,39 @@ const filterStatements = (stmts, extension) => {
24
24
  !stmt.hasOwnProperty('CreateExtensionStmt');
25
25
  });
26
26
  };
27
- export const packageModule = async (packageDir, { usePlan = true, extension = true, pretty = true, functionDelimiter = '$EOFCODE$', outputDiff = false } = {}) => {
27
+ /**
28
+ * Merge a concatenated SQL string into a single deparsed script.
29
+ *
30
+ * Parses the input, optionally strips transaction/extension statements, deparses
31
+ * with a stable function delimiter, and round-trips the result to detect any AST
32
+ * drift. This is the shared engine behind `packageModule` (whole-module packaging)
33
+ * and per-chunk rebundling — both must merge statements identically.
34
+ */
35
+ export const mergeSqlStatements = async (sql, { stripTransactions = false, echoExtensionName, pretty = true, functionDelimiter = '$EOFCODE$' } = {}) => {
36
+ if (!sql?.trim()) {
37
+ return { sql: '' };
38
+ }
39
+ const parsed = await parse(sql);
40
+ parsed.stmts = filterStatements(parsed.stmts, stripTransactions);
41
+ const topLine = echoExtensionName
42
+ ? `\\echo Use "CREATE EXTENSION ${echoExtensionName}" to load this file. \\quit\n`
43
+ : '';
44
+ const finalSql = await deparse(parsed, { pretty, functionDelimiter });
45
+ const tree1 = parsed.stmts;
46
+ const reparsed = await parse(finalSql);
47
+ const tree2 = filterStatements(reparsed.stmts, stripTransactions);
48
+ const results = {
49
+ sql: `${topLine}${finalSql}`,
50
+ };
51
+ const diff = JSON.stringify(cleanTree(tree1)) !== JSON.stringify(cleanTree(tree2));
52
+ if (diff) {
53
+ results.diff = true;
54
+ results.tree1 = JSON.stringify(cleanTree(tree1), null, 2);
55
+ results.tree2 = JSON.stringify(cleanTree(tree2), null, 2);
56
+ }
57
+ return results;
58
+ };
59
+ export const packageModule = async (packageDir, { usePlan = true, extension = true, pretty = true, functionDelimiter = '$EOFCODE$' } = {}) => {
28
60
  const resolveFn = usePlan ? resolveWithPlan : resolve;
29
61
  const sql = resolveFn(packageDir);
30
62
  if (!sql?.trim()) {
@@ -33,28 +65,12 @@ export const packageModule = async (packageDir, { usePlan = true, extension = tr
33
65
  }
34
66
  const extname = getExtensionName(packageDir);
35
67
  try {
36
- const parsed = await parse(sql);
37
- parsed.stmts = filterStatements(parsed.stmts, extension);
38
- const topLine = extension
39
- ? `\\echo Use "CREATE EXTENSION ${extname}" to load this file. \\quit\n`
40
- : '';
41
- const finalSql = await deparse(parsed, {
68
+ return await mergeSqlStatements(sql, {
69
+ stripTransactions: extension,
70
+ echoExtensionName: extension ? extname : undefined,
42
71
  pretty,
43
- functionDelimiter
72
+ functionDelimiter,
44
73
  });
45
- const tree1 = parsed.stmts;
46
- const reparsed = await parse(finalSql);
47
- const tree2 = filterStatements(reparsed.stmts, extension);
48
- const results = {
49
- sql: `${topLine}${finalSql}`,
50
- };
51
- const diff = JSON.stringify(cleanTree(tree1)) !== JSON.stringify(cleanTree(tree2));
52
- if (diff) {
53
- results.diff = true;
54
- results.tree1 = JSON.stringify(cleanTree(tree1), null, 2);
55
- results.tree2 = JSON.stringify(cleanTree(tree2), null, 2);
56
- }
57
- return results;
58
74
  }
59
75
  catch (e) {
60
76
  log.error(`❌ Failed to parse SQL for ${packageDir}`);
@@ -0,0 +1,4 @@
1
+ export * from './types';
2
+ export * from './rebundle';
3
+ export * from './module';
4
+ export * from './workspace';