@pgpmjs/core 3.0.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 (140) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +99 -0
  3. package/core/boilerplate-scanner.d.ts +41 -0
  4. package/core/boilerplate-scanner.js +106 -0
  5. package/core/boilerplate-types.d.ts +52 -0
  6. package/core/boilerplate-types.js +6 -0
  7. package/core/class/pgpm.d.ts +150 -0
  8. package/core/class/pgpm.js +1470 -0
  9. package/core/template-scaffold.d.ts +29 -0
  10. package/core/template-scaffold.js +168 -0
  11. package/esm/core/boilerplate-scanner.js +96 -0
  12. package/esm/core/boilerplate-types.js +5 -0
  13. package/esm/core/class/pgpm.js +1430 -0
  14. package/esm/core/template-scaffold.js +161 -0
  15. package/esm/export/export-meta.js +240 -0
  16. package/esm/export/export-migrations.js +180 -0
  17. package/esm/extensions/extensions.js +31 -0
  18. package/esm/files/extension/index.js +3 -0
  19. package/esm/files/extension/reader.js +79 -0
  20. package/esm/files/extension/writer.js +63 -0
  21. package/esm/files/index.js +6 -0
  22. package/esm/files/plan/generator.js +49 -0
  23. package/esm/files/plan/index.js +5 -0
  24. package/esm/files/plan/parser.js +296 -0
  25. package/esm/files/plan/validators.js +181 -0
  26. package/esm/files/plan/writer.js +114 -0
  27. package/esm/files/sql/index.js +1 -0
  28. package/esm/files/sql/writer.js +107 -0
  29. package/esm/files/sql-scripts/index.js +2 -0
  30. package/esm/files/sql-scripts/reader.js +19 -0
  31. package/esm/files/types/index.js +1 -0
  32. package/esm/files/types/package.js +1 -0
  33. package/esm/index.js +21 -0
  34. package/esm/init/client.js +144 -0
  35. package/esm/init/sql/bootstrap-roles.sql +55 -0
  36. package/esm/init/sql/bootstrap-test-roles.sql +72 -0
  37. package/esm/migrate/clean.js +23 -0
  38. package/esm/migrate/client.js +551 -0
  39. package/esm/migrate/index.js +5 -0
  40. package/esm/migrate/sql/procedures.sql +258 -0
  41. package/esm/migrate/sql/schema.sql +37 -0
  42. package/esm/migrate/types.js +1 -0
  43. package/esm/migrate/utils/event-logger.js +28 -0
  44. package/esm/migrate/utils/hash.js +27 -0
  45. package/esm/migrate/utils/transaction.js +125 -0
  46. package/esm/modules/modules.js +49 -0
  47. package/esm/packaging/package.js +96 -0
  48. package/esm/packaging/transform.js +70 -0
  49. package/esm/projects/deploy.js +123 -0
  50. package/esm/projects/revert.js +75 -0
  51. package/esm/projects/verify.js +61 -0
  52. package/esm/resolution/deps.js +526 -0
  53. package/esm/resolution/resolve.js +101 -0
  54. package/esm/utils/debug.js +147 -0
  55. package/esm/utils/target-utils.js +37 -0
  56. package/esm/workspace/paths.js +43 -0
  57. package/esm/workspace/utils.js +31 -0
  58. package/export/export-meta.d.ts +8 -0
  59. package/export/export-meta.js +244 -0
  60. package/export/export-migrations.d.ts +17 -0
  61. package/export/export-migrations.js +187 -0
  62. package/extensions/extensions.d.ts +5 -0
  63. package/extensions/extensions.js +35 -0
  64. package/files/extension/index.d.ts +2 -0
  65. package/files/extension/index.js +19 -0
  66. package/files/extension/reader.d.ts +24 -0
  67. package/files/extension/reader.js +86 -0
  68. package/files/extension/writer.d.ts +39 -0
  69. package/files/extension/writer.js +70 -0
  70. package/files/index.d.ts +5 -0
  71. package/files/index.js +22 -0
  72. package/files/plan/generator.d.ts +22 -0
  73. package/files/plan/generator.js +57 -0
  74. package/files/plan/index.d.ts +4 -0
  75. package/files/plan/index.js +21 -0
  76. package/files/plan/parser.d.ts +27 -0
  77. package/files/plan/parser.js +303 -0
  78. package/files/plan/validators.d.ts +52 -0
  79. package/files/plan/validators.js +187 -0
  80. package/files/plan/writer.d.ts +27 -0
  81. package/files/plan/writer.js +124 -0
  82. package/files/sql/index.d.ts +1 -0
  83. package/files/sql/index.js +17 -0
  84. package/files/sql/writer.d.ts +12 -0
  85. package/files/sql/writer.js +114 -0
  86. package/files/sql-scripts/index.d.ts +1 -0
  87. package/files/sql-scripts/index.js +18 -0
  88. package/files/sql-scripts/reader.d.ts +8 -0
  89. package/files/sql-scripts/reader.js +23 -0
  90. package/files/types/index.d.ts +46 -0
  91. package/files/types/index.js +17 -0
  92. package/files/types/package.d.ts +20 -0
  93. package/files/types/package.js +2 -0
  94. package/index.d.ts +21 -0
  95. package/index.js +45 -0
  96. package/init/client.d.ts +26 -0
  97. package/init/client.js +148 -0
  98. package/init/sql/bootstrap-roles.sql +55 -0
  99. package/init/sql/bootstrap-test-roles.sql +72 -0
  100. package/migrate/clean.d.ts +1 -0
  101. package/migrate/clean.js +27 -0
  102. package/migrate/client.d.ts +80 -0
  103. package/migrate/client.js +555 -0
  104. package/migrate/index.d.ts +5 -0
  105. package/migrate/index.js +21 -0
  106. package/migrate/sql/procedures.sql +258 -0
  107. package/migrate/sql/schema.sql +37 -0
  108. package/migrate/types.d.ts +67 -0
  109. package/migrate/types.js +2 -0
  110. package/migrate/utils/event-logger.d.ts +13 -0
  111. package/migrate/utils/event-logger.js +32 -0
  112. package/migrate/utils/hash.d.ts +12 -0
  113. package/migrate/utils/hash.js +32 -0
  114. package/migrate/utils/transaction.d.ts +27 -0
  115. package/migrate/utils/transaction.js +129 -0
  116. package/modules/modules.d.ts +31 -0
  117. package/modules/modules.js +56 -0
  118. package/package.json +70 -0
  119. package/packaging/package.d.ts +19 -0
  120. package/packaging/package.js +102 -0
  121. package/packaging/transform.d.ts +22 -0
  122. package/packaging/transform.js +75 -0
  123. package/projects/deploy.d.ts +8 -0
  124. package/projects/deploy.js +160 -0
  125. package/projects/revert.d.ts +15 -0
  126. package/projects/revert.js +112 -0
  127. package/projects/verify.d.ts +8 -0
  128. package/projects/verify.js +98 -0
  129. package/resolution/deps.d.ts +57 -0
  130. package/resolution/deps.js +531 -0
  131. package/resolution/resolve.d.ts +37 -0
  132. package/resolution/resolve.js +107 -0
  133. package/utils/debug.d.ts +21 -0
  134. package/utils/debug.js +153 -0
  135. package/utils/target-utils.d.ts +5 -0
  136. package/utils/target-utils.js +40 -0
  137. package/workspace/paths.d.ts +14 -0
  138. package/workspace/paths.js +50 -0
  139. package/workspace/utils.d.ts +8 -0
  140. package/workspace/utils.js +36 -0
@@ -0,0 +1,29 @@
1
+ import { BoilerplateQuestion } from './boilerplate-types';
2
+ export type TemplateKind = 'workspace' | 'module';
3
+ export interface ScaffoldTemplateOptions {
4
+ type: TemplateKind;
5
+ outputDir: string;
6
+ templateRepo?: string;
7
+ branch?: string;
8
+ templatePath?: string;
9
+ answers: Record<string, any>;
10
+ noTty?: boolean;
11
+ cacheTtlMs?: number;
12
+ toolName?: string;
13
+ cwd?: string;
14
+ cacheBaseDir?: string;
15
+ /** Override the boilerplate directory (e.g., "default", "supabase") */
16
+ dir?: string;
17
+ }
18
+ export interface ScaffoldTemplateResult {
19
+ cacheUsed: boolean;
20
+ cacheExpired: boolean;
21
+ cachePath?: string;
22
+ templateDir: string;
23
+ /** Questions loaded from .boilerplate.json, if any */
24
+ questions?: BoilerplateQuestion[];
25
+ }
26
+ export declare const DEFAULT_TEMPLATE_REPO = "https://github.com/constructive-io/pgpm-boilerplates.git";
27
+ export declare const DEFAULT_TEMPLATE_TTL_MS: number;
28
+ export declare const DEFAULT_TEMPLATE_TOOL_NAME = "pgpm";
29
+ export declare function scaffoldTemplate(options: ScaffoldTemplateOptions): Promise<ScaffoldTemplateResult>;
@@ -0,0 +1,168 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.DEFAULT_TEMPLATE_TOOL_NAME = exports.DEFAULT_TEMPLATE_TTL_MS = exports.DEFAULT_TEMPLATE_REPO = void 0;
7
+ exports.scaffoldTemplate = scaffoldTemplate;
8
+ const fs_1 = __importDefault(require("fs"));
9
+ const os_1 = __importDefault(require("os"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const create_gen_app_1 = require("create-gen-app");
12
+ const boilerplate_scanner_1 = require("./boilerplate-scanner");
13
+ exports.DEFAULT_TEMPLATE_REPO = 'https://github.com/constructive-io/pgpm-boilerplates.git';
14
+ exports.DEFAULT_TEMPLATE_TTL_MS = 7 * 24 * 60 * 60 * 1000; // 1 week
15
+ exports.DEFAULT_TEMPLATE_TOOL_NAME = 'pgpm';
16
+ const templatizer = new create_gen_app_1.Templatizer();
17
+ const looksLikePath = (value) => {
18
+ return (value.startsWith('.') || value.startsWith('/') || value.startsWith('~'));
19
+ };
20
+ const normalizeQuestions = (questions) => questions?.map((q) => ({
21
+ ...q,
22
+ type: q.type || 'text',
23
+ }));
24
+ const attachQuestionsToTemplatizer = (templ, questions) => {
25
+ if (!questions?.length || typeof templ?.extract !== 'function')
26
+ return;
27
+ const originalExtract = templ.extract.bind(templ);
28
+ templ.extract = async (templateDir) => {
29
+ const extracted = await originalExtract(templateDir);
30
+ extracted.projectQuestions = {
31
+ questions: normalizeQuestions(questions),
32
+ };
33
+ return extracted;
34
+ };
35
+ };
36
+ /**
37
+ * Resolve the template path using the new metadata-driven resolution.
38
+ *
39
+ * Resolution order:
40
+ * 1. If explicit `templatePath` is provided, use it directly
41
+ * 2. If `.boilerplates.json` exists, use its `dir` field to find the base directory
42
+ * 3. Look for `{baseDir}/{type}` (e.g., "default/module")
43
+ * 4. Fallback to legacy structure: `{type}` directly in root
44
+ */
45
+ const resolveFromPath = (templateDir, templatePath, type, dirOverride) => {
46
+ // If explicit templatePath is provided, use it directly
47
+ if (templatePath) {
48
+ const candidateDir = path_1.default.isAbsolute(templatePath)
49
+ ? templatePath
50
+ : path_1.default.join(templateDir, templatePath);
51
+ if (fs_1.default.existsSync(candidateDir) &&
52
+ fs_1.default.statSync(candidateDir).isDirectory()) {
53
+ return {
54
+ fromPath: path_1.default.relative(templateDir, candidateDir) || '.',
55
+ resolvedTemplatePath: candidateDir,
56
+ };
57
+ }
58
+ return {
59
+ fromPath: templatePath,
60
+ resolvedTemplatePath: path_1.default.join(templateDir, templatePath),
61
+ };
62
+ }
63
+ // Try new metadata-driven resolution
64
+ const rootConfig = (0, boilerplate_scanner_1.readBoilerplatesConfig)(templateDir);
65
+ const baseDir = dirOverride ?? rootConfig?.dir;
66
+ if (baseDir) {
67
+ // New structure: {templateDir}/{baseDir}/{type}
68
+ const newStructurePath = path_1.default.join(templateDir, baseDir, type);
69
+ if (fs_1.default.existsSync(newStructurePath) &&
70
+ fs_1.default.statSync(newStructurePath).isDirectory()) {
71
+ return {
72
+ fromPath: path_1.default.join(baseDir, type),
73
+ resolvedTemplatePath: newStructurePath,
74
+ };
75
+ }
76
+ }
77
+ // Fallback to legacy structure: {templateDir}/{type}
78
+ const legacyPath = path_1.default.join(templateDir, type);
79
+ if (fs_1.default.existsSync(legacyPath) && fs_1.default.statSync(legacyPath).isDirectory()) {
80
+ return {
81
+ fromPath: type,
82
+ resolvedTemplatePath: legacyPath,
83
+ };
84
+ }
85
+ // Default fallback
86
+ return {
87
+ fromPath: type,
88
+ resolvedTemplatePath: path_1.default.join(templateDir, type),
89
+ };
90
+ };
91
+ async function scaffoldTemplate(options) {
92
+ const { type, outputDir, templateRepo = exports.DEFAULT_TEMPLATE_REPO, branch, templatePath, answers, noTty = false, cacheTtlMs = exports.DEFAULT_TEMPLATE_TTL_MS, toolName = exports.DEFAULT_TEMPLATE_TOOL_NAME, cwd, cacheBaseDir, dir, } = options;
93
+ const resolvedRepo = looksLikePath(templateRepo)
94
+ ? path_1.default.resolve(cwd ?? process.cwd(), templateRepo)
95
+ : templateRepo;
96
+ // Handle local template directories without caching
97
+ if (looksLikePath(templateRepo) &&
98
+ fs_1.default.existsSync(resolvedRepo) &&
99
+ fs_1.default.statSync(resolvedRepo).isDirectory()) {
100
+ const { fromPath, resolvedTemplatePath } = resolveFromPath(resolvedRepo, templatePath, type, dir);
101
+ // Read boilerplate config for questions
102
+ const boilerplateConfig = (0, boilerplate_scanner_1.readBoilerplateConfig)(resolvedTemplatePath);
103
+ // Inject questions into the templatizer pipeline so prompt types and defaults are applied
104
+ attachQuestionsToTemplatizer(templatizer, boilerplateConfig?.questions);
105
+ await templatizer.process(resolvedRepo, outputDir, {
106
+ argv: answers,
107
+ noTty,
108
+ fromPath,
109
+ });
110
+ return {
111
+ cacheUsed: false,
112
+ cacheExpired: false,
113
+ templateDir: resolvedRepo,
114
+ questions: boilerplateConfig?.questions,
115
+ };
116
+ }
117
+ // Remote repo with caching
118
+ const cacheManager = new create_gen_app_1.CacheManager({
119
+ toolName,
120
+ ttl: cacheTtlMs,
121
+ baseDir: cacheBaseDir ??
122
+ process.env.PGPM_CACHE_BASE_DIR ??
123
+ (process.env.JEST_WORKER_ID
124
+ ? path_1.default.join(os_1.default.tmpdir(), `pgpm-cache-${process.env.JEST_WORKER_ID}`)
125
+ : undefined),
126
+ });
127
+ const gitCloner = new create_gen_app_1.GitCloner();
128
+ const normalizedUrl = gitCloner.normalizeUrl(resolvedRepo);
129
+ const cacheKey = cacheManager.createKey(normalizedUrl, branch);
130
+ const expiredMetadata = cacheManager.checkExpiration(cacheKey);
131
+ if (expiredMetadata) {
132
+ cacheManager.clear(cacheKey);
133
+ }
134
+ let templateDir;
135
+ let cacheUsed = false;
136
+ const cachedPath = cacheManager.get(cacheKey);
137
+ if (cachedPath && !expiredMetadata) {
138
+ templateDir = cachedPath;
139
+ cacheUsed = true;
140
+ }
141
+ else {
142
+ const tempDest = path_1.default.join(cacheManager.getReposDir(), cacheKey);
143
+ gitCloner.clone(normalizedUrl, tempDest, {
144
+ branch,
145
+ depth: 1,
146
+ singleBranch: true,
147
+ });
148
+ cacheManager.set(cacheKey, tempDest);
149
+ templateDir = tempDest;
150
+ }
151
+ const { fromPath, resolvedTemplatePath } = resolveFromPath(templateDir, templatePath, type, dir);
152
+ // Read boilerplate config for questions
153
+ const boilerplateConfig = (0, boilerplate_scanner_1.readBoilerplateConfig)(resolvedTemplatePath);
154
+ // Inject questions into the templatizer pipeline so prompt types and defaults are applied
155
+ attachQuestionsToTemplatizer(templatizer, boilerplateConfig?.questions);
156
+ await templatizer.process(templateDir, outputDir, {
157
+ argv: answers,
158
+ noTty,
159
+ fromPath,
160
+ });
161
+ return {
162
+ cacheUsed,
163
+ cacheExpired: Boolean(expiredMetadata),
164
+ cachePath: templateDir,
165
+ templateDir,
166
+ questions: boilerplateConfig?.questions,
167
+ };
168
+ }
@@ -0,0 +1,96 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ /**
4
+ * Read the root `.boilerplates.json` configuration from a template repository.
5
+ * This file specifies the default directory containing boilerplate templates.
6
+ *
7
+ * @param templateDir - The root directory of the template repository
8
+ * @returns The root config or null if not found
9
+ */
10
+ export function readBoilerplatesConfig(templateDir) {
11
+ const configPath = path.join(templateDir, '.boilerplates.json');
12
+ if (fs.existsSync(configPath)) {
13
+ try {
14
+ const content = fs.readFileSync(configPath, 'utf-8');
15
+ return JSON.parse(content);
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ return null;
22
+ }
23
+ /**
24
+ * Read the `.boilerplate.json` configuration from a boilerplate directory.
25
+ * This file specifies the boilerplate type and questions.
26
+ *
27
+ * @param boilerplatePath - The path to the boilerplate directory
28
+ * @returns The boilerplate config or null if not found
29
+ */
30
+ export function readBoilerplateConfig(boilerplatePath) {
31
+ const jsonPath = path.join(boilerplatePath, '.boilerplate.json');
32
+ if (fs.existsSync(jsonPath)) {
33
+ try {
34
+ const content = fs.readFileSync(jsonPath, 'utf-8');
35
+ return JSON.parse(content);
36
+ }
37
+ catch {
38
+ return null;
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+ /**
44
+ * Scan a base directory for boilerplate templates.
45
+ * Each subdirectory with a `.boilerplate.json` file is considered a boilerplate.
46
+ *
47
+ * @param baseDir - The directory to scan (e.g., "default/")
48
+ * @returns Array of scanned boilerplates with their configurations
49
+ */
50
+ export function scanBoilerplates(baseDir) {
51
+ const boilerplates = [];
52
+ if (!fs.existsSync(baseDir)) {
53
+ return boilerplates;
54
+ }
55
+ const entries = fs.readdirSync(baseDir, { withFileTypes: true });
56
+ for (const entry of entries) {
57
+ if (!entry.isDirectory()) {
58
+ continue;
59
+ }
60
+ const boilerplatePath = path.join(baseDir, entry.name);
61
+ const config = readBoilerplateConfig(boilerplatePath);
62
+ if (config) {
63
+ boilerplates.push({
64
+ name: entry.name,
65
+ path: boilerplatePath,
66
+ type: config.type ?? 'module',
67
+ questions: config.questions
68
+ });
69
+ }
70
+ }
71
+ return boilerplates;
72
+ }
73
+ /**
74
+ * Find a boilerplate by type within a scanned list.
75
+ *
76
+ * @param boilerplates - Array of scanned boilerplates
77
+ * @param type - The type to find ('workspace' or 'module')
78
+ * @returns The matching boilerplate or undefined
79
+ */
80
+ export function findBoilerplateByType(boilerplates, type) {
81
+ return boilerplates.find((bp) => bp.type === type);
82
+ }
83
+ /**
84
+ * Resolve the base directory for boilerplates in a template repository.
85
+ * Uses `.boilerplates.json` if present, otherwise returns empty string.
86
+ *
87
+ * @param templateDir - The root directory of the template repository
88
+ * @returns The resolved base directory path
89
+ */
90
+ export function resolveBoilerplateBaseDir(templateDir) {
91
+ const rootConfig = readBoilerplatesConfig(templateDir);
92
+ if (rootConfig?.dir) {
93
+ return path.join(templateDir, rootConfig.dir);
94
+ }
95
+ return templateDir;
96
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Boilerplate metadata types for the new metadata-driven resolution system.
3
+ * These types support the `.boilerplate.json` and `.boilerplates.json` configuration files.
4
+ */
5
+ export {};