@pgpmjs/core 4.12.2 → 4.13.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.
@@ -854,5 +854,5 @@ export const exportMeta = async ({ opts, dbname, database_id }) => {
854
854
  await queryAndParse('uuid_module', `SELECT * FROM metaschema_modules_public.uuid_module WHERE database_id = $1`);
855
855
  await queryAndParse('default_ids_module', `SELECT * FROM metaschema_modules_public.default_ids_module WHERE database_id = $1`);
856
856
  await queryAndParse('denormalized_table_field', `SELECT * FROM metaschema_modules_public.denormalized_table_field WHERE database_id = $1`);
857
- return Object.entries(sql).reduce((m, [_, v]) => m + '\n\n' + v, '');
857
+ return sql;
858
858
  };
@@ -155,12 +155,11 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
155
155
  }
156
156
  writePgpmPlan(results.rows, opts);
157
157
  writePgpmFiles(results.rows, opts);
158
- let meta = await exportMeta({
158
+ const metaResult = await exportMeta({
159
159
  opts: options,
160
160
  dbname: database,
161
161
  database_id: databaseId
162
162
  });
163
- meta = replacer(meta);
164
163
  // Build description for the meta/service extension package
165
164
  const metaDesc = metaExtensionDesc || `${metaExtensionName} service utilities for managing domains, APIs, and services`;
166
165
  // Detect missing modules at workspace level and prompt user
@@ -189,11 +188,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
189
188
  schemas: metaSchemasForReplacement,
190
189
  name: metaExtensionName
191
190
  });
192
- const metaPackage = [
193
- {
194
- deps: [],
195
- deploy: 'migrate/meta',
196
- content: `SET session_replication_role TO replica;
191
+ // Create separate files for each table type
192
+ const metaPackage = [];
193
+ // Common header for all meta files
194
+ const commonHeader = `SET session_replication_role TO replica;
197
195
  -- using replica in case we are deploying triggers to metaschema_public
198
196
 
199
197
  -- unaccent, postgis affected and require grants
@@ -207,24 +205,92 @@ DO $LQLMIGRATION$
207
205
  EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', current_database(), 'app_admin');
208
206
 
209
207
  END;
210
- $LQLMIGRATION$;
211
-
212
- ${meta}
213
-
214
- -- TODO: Research needed - These UPDATE statements may be a security leak.
215
- -- They appear to rebind exported metadata to the target database after import,
216
- -- but exposing dbname in services_public tables could leak internal database names.
217
- -- Consider removing entirely or gating behind an explicit flag.
218
- -- UPDATE services_public.apis
219
- -- SET dbname = current_database() WHERE TRUE;
208
+ $LQLMIGRATION$;`;
209
+ const commonFooter = `
210
+ SET session_replication_role TO DEFAULT;`;
211
+ // Define table ordering with dependencies
212
+ // Tables that depend on 'database' being inserted first
213
+ const tableOrder = [
214
+ 'database',
215
+ 'database_extension',
216
+ 'schema',
217
+ 'table',
218
+ 'field',
219
+ 'policy',
220
+ 'index',
221
+ 'trigger',
222
+ 'trigger_function',
223
+ 'rls_function',
224
+ 'limit_function',
225
+ 'procedure',
226
+ 'foreign_key_constraint',
227
+ 'primary_key_constraint',
228
+ 'unique_constraint',
229
+ 'check_constraint',
230
+ 'full_text_search',
231
+ 'schema_grant',
232
+ 'table_grant',
233
+ 'domains',
234
+ 'sites',
235
+ 'apis',
236
+ 'apps',
237
+ 'site_modules',
238
+ 'site_themes',
239
+ 'site_metadata',
240
+ 'api_modules',
241
+ 'api_extensions',
242
+ 'api_schemas',
243
+ 'rls_module',
244
+ 'user_auth_module',
245
+ 'memberships_module',
246
+ 'permissions_module',
247
+ 'limits_module',
248
+ 'levels_module',
249
+ 'users_module',
250
+ 'hierarchy_module',
251
+ 'membership_types_module',
252
+ 'invites_module',
253
+ 'emails_module',
254
+ 'tokens_module',
255
+ 'secrets_module',
256
+ 'profiles_module',
257
+ 'encrypted_secrets_module',
258
+ 'connected_accounts_module',
259
+ 'phone_numbers_module',
260
+ 'crypto_addresses_module',
261
+ 'crypto_auth_module',
262
+ 'field_module',
263
+ 'uuid_module',
264
+ 'default_ids_module',
265
+ 'denormalized_table_field'
266
+ ];
267
+ // Track which tables have content for dependency resolution
268
+ const tablesWithContent = [];
269
+ // Create a file for each table type that has content
270
+ for (const tableName of tableOrder) {
271
+ const tableSql = metaResult[tableName];
272
+ if (tableSql) {
273
+ const replacedSql = metaReplacer.replacer(tableSql);
274
+ // Determine dependencies - each table depends on the previous tables that have content
275
+ // This ensures proper ordering during deployment
276
+ const deps = tableName === 'database'
277
+ ? []
278
+ : tablesWithContent.length > 0
279
+ ? [`migrate/${tablesWithContent[tablesWithContent.length - 1]}`]
280
+ : [];
281
+ metaPackage.push({
282
+ deps,
283
+ deploy: `migrate/${tableName}`,
284
+ content: `${commonHeader}
220
285
 
221
- -- UPDATE services_public.sites
222
- -- SET dbname = current_database() WHERE TRUE;
286
+ ${replacedSql}
223
287
 
224
- SET session_replication_role TO DEFAULT;
288
+ ${commonFooter}
225
289
  `
290
+ });
291
+ tablesWithContent.push(tableName);
226
292
  }
227
- ];
293
+ }
228
294
  opts.replacer = metaReplacer.replacer;
229
295
  opts.name = metaExtensionName;
230
296
  opts.outdir = svcOutdir;
@@ -4,5 +4,6 @@ interface ExportMetaParams {
4
4
  dbname: string;
5
5
  database_id: string;
6
6
  }
7
- export declare const exportMeta: ({ opts, dbname, database_id }: ExportMetaParams) => Promise<string>;
7
+ export type ExportMetaResult = Record<string, string>;
8
+ export declare const exportMeta: ({ opts, dbname, database_id }: ExportMetaParams) => Promise<ExportMetaResult>;
8
9
  export {};
@@ -857,6 +857,6 @@ const exportMeta = async ({ opts, dbname, database_id }) => {
857
857
  await queryAndParse('uuid_module', `SELECT * FROM metaschema_modules_public.uuid_module WHERE database_id = $1`);
858
858
  await queryAndParse('default_ids_module', `SELECT * FROM metaschema_modules_public.default_ids_module WHERE database_id = $1`);
859
859
  await queryAndParse('denormalized_table_field', `SELECT * FROM metaschema_modules_public.denormalized_table_field WHERE database_id = $1`);
860
- return Object.entries(sql).reduce((m, [_, v]) => m + '\n\n' + v, '');
860
+ return sql;
861
861
  };
862
862
  exports.exportMeta = exportMeta;
@@ -161,12 +161,11 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
161
161
  }
162
162
  (0, files_1.writePgpmPlan)(results.rows, opts);
163
163
  (0, files_1.writePgpmFiles)(results.rows, opts);
164
- let meta = await (0, export_meta_1.exportMeta)({
164
+ const metaResult = await (0, export_meta_1.exportMeta)({
165
165
  opts: options,
166
166
  dbname: database,
167
167
  database_id: databaseId
168
168
  });
169
- meta = replacer(meta);
170
169
  // Build description for the meta/service extension package
171
170
  const metaDesc = metaExtensionDesc || `${metaExtensionName} service utilities for managing domains, APIs, and services`;
172
171
  // Detect missing modules at workspace level and prompt user
@@ -195,11 +194,10 @@ const exportMigrationsToDisk = async ({ project, options, database, databaseId,
195
194
  schemas: metaSchemasForReplacement,
196
195
  name: metaExtensionName
197
196
  });
198
- const metaPackage = [
199
- {
200
- deps: [],
201
- deploy: 'migrate/meta',
202
- content: `SET session_replication_role TO replica;
197
+ // Create separate files for each table type
198
+ const metaPackage = [];
199
+ // Common header for all meta files
200
+ const commonHeader = `SET session_replication_role TO replica;
203
201
  -- using replica in case we are deploying triggers to metaschema_public
204
202
 
205
203
  -- unaccent, postgis affected and require grants
@@ -213,24 +211,92 @@ DO $LQLMIGRATION$
213
211
  EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', current_database(), 'app_admin');
214
212
 
215
213
  END;
216
- $LQLMIGRATION$;
217
-
218
- ${meta}
219
-
220
- -- TODO: Research needed - These UPDATE statements may be a security leak.
221
- -- They appear to rebind exported metadata to the target database after import,
222
- -- but exposing dbname in services_public tables could leak internal database names.
223
- -- Consider removing entirely or gating behind an explicit flag.
224
- -- UPDATE services_public.apis
225
- -- SET dbname = current_database() WHERE TRUE;
214
+ $LQLMIGRATION$;`;
215
+ const commonFooter = `
216
+ SET session_replication_role TO DEFAULT;`;
217
+ // Define table ordering with dependencies
218
+ // Tables that depend on 'database' being inserted first
219
+ const tableOrder = [
220
+ 'database',
221
+ 'database_extension',
222
+ 'schema',
223
+ 'table',
224
+ 'field',
225
+ 'policy',
226
+ 'index',
227
+ 'trigger',
228
+ 'trigger_function',
229
+ 'rls_function',
230
+ 'limit_function',
231
+ 'procedure',
232
+ 'foreign_key_constraint',
233
+ 'primary_key_constraint',
234
+ 'unique_constraint',
235
+ 'check_constraint',
236
+ 'full_text_search',
237
+ 'schema_grant',
238
+ 'table_grant',
239
+ 'domains',
240
+ 'sites',
241
+ 'apis',
242
+ 'apps',
243
+ 'site_modules',
244
+ 'site_themes',
245
+ 'site_metadata',
246
+ 'api_modules',
247
+ 'api_extensions',
248
+ 'api_schemas',
249
+ 'rls_module',
250
+ 'user_auth_module',
251
+ 'memberships_module',
252
+ 'permissions_module',
253
+ 'limits_module',
254
+ 'levels_module',
255
+ 'users_module',
256
+ 'hierarchy_module',
257
+ 'membership_types_module',
258
+ 'invites_module',
259
+ 'emails_module',
260
+ 'tokens_module',
261
+ 'secrets_module',
262
+ 'profiles_module',
263
+ 'encrypted_secrets_module',
264
+ 'connected_accounts_module',
265
+ 'phone_numbers_module',
266
+ 'crypto_addresses_module',
267
+ 'crypto_auth_module',
268
+ 'field_module',
269
+ 'uuid_module',
270
+ 'default_ids_module',
271
+ 'denormalized_table_field'
272
+ ];
273
+ // Track which tables have content for dependency resolution
274
+ const tablesWithContent = [];
275
+ // Create a file for each table type that has content
276
+ for (const tableName of tableOrder) {
277
+ const tableSql = metaResult[tableName];
278
+ if (tableSql) {
279
+ const replacedSql = metaReplacer.replacer(tableSql);
280
+ // Determine dependencies - each table depends on the previous tables that have content
281
+ // This ensures proper ordering during deployment
282
+ const deps = tableName === 'database'
283
+ ? []
284
+ : tablesWithContent.length > 0
285
+ ? [`migrate/${tablesWithContent[tablesWithContent.length - 1]}`]
286
+ : [];
287
+ metaPackage.push({
288
+ deps,
289
+ deploy: `migrate/${tableName}`,
290
+ content: `${commonHeader}
226
291
 
227
- -- UPDATE services_public.sites
228
- -- SET dbname = current_database() WHERE TRUE;
292
+ ${replacedSql}
229
293
 
230
- SET session_replication_role TO DEFAULT;
294
+ ${commonFooter}
231
295
  `
296
+ });
297
+ tablesWithContent.push(tableName);
232
298
  }
233
- ];
299
+ }
234
300
  opts.replacer = metaReplacer.replacer;
235
301
  opts.name = metaExtensionName;
236
302
  opts.outdir = svcOutdir;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pgpmjs/core",
3
- "version": "4.12.2",
3
+ "version": "4.13.0",
4
4
  "author": "Constructive <developers@constructive.io>",
5
5
  "description": "PGPM Package and Migration Tools",
6
6
  "main": "index.js",
@@ -64,5 +64,5 @@
64
64
  "pgsql-parser": "^17.9.11",
65
65
  "yanse": "^0.1.11"
66
66
  },
67
- "gitHead": "8ba31ce871a6fec054ee6ea5627f44d69c635828"
67
+ "gitHead": "eff12e839462a1d5435f04318db1f44fc141e235"
68
68
  }