@pgpmjs/export 0.27.0 → 0.28.1

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.
@@ -1,8 +1,9 @@
1
+ import { getMissingInstallableModules, parseAuthor, PgpmPackage } from '@pgpmjs/core';
1
2
  import { mkdirSync, rmSync } from 'fs';
2
3
  import { sync as glob } from 'glob';
3
4
  import { toSnakeCase } from 'inflekt';
4
5
  import path from 'path';
5
- import { PgpmPackage, getMissingInstallableModules, parseAuthor } from '@pgpmjs/core';
6
+ import metaExportTables from './meta-export-tables.json';
6
7
  import { lookupByPgUdt } from './type-map';
7
8
  // =============================================================================
8
9
  // Shared constants
@@ -46,6 +47,38 @@ export const mapPgTypeToFieldType = (udtName) => {
46
47
  const entry = lookupByPgUdt(udtName);
47
48
  return entry?.fieldType ?? 'text';
48
49
  };
50
+ /**
51
+ * Query actual columns from information_schema for a given table.
52
+ * Returns a map of column_name -> { udt_name, column_default }.
53
+ */
54
+ export const getTableColumnsWithDefaults = async (pool, schemaName, tableName) => {
55
+ const result = await pool.query(`
56
+ SELECT column_name, udt_name, column_default
57
+ FROM information_schema.columns
58
+ WHERE table_schema = $1 AND table_name = $2
59
+ ORDER BY ordinal_position
60
+ `, [schemaName, tableName]);
61
+ const columns = new Map();
62
+ for (const row of result.rows) {
63
+ columns.set(row.column_name, {
64
+ udt_name: row.udt_name,
65
+ column_default: row.column_default
66
+ });
67
+ }
68
+ return columns;
69
+ };
70
+ /**
71
+ * Detect whether a column default expression is a non-deterministic timestamp
72
+ * default (now(), CURRENT_TIMESTAMP, clock_timestamp(), or now() + interval).
73
+ * Exported INSERT rows must omit these columns so the DDL DEFAULT applies at
74
+ * deploy time, keeping generated migrations deterministic.
75
+ */
76
+ export const isTimestampDefaultColumn = (columnDefault) => {
77
+ if (!columnDefault)
78
+ return false;
79
+ const normalized = columnDefault.trim().toLowerCase();
80
+ return /^(now\s*\(\s*\)|current_timestamp(?:\s*\(\s*\d*\s*\))?|clock_timestamp\s*\(\s*\))(?:\s*\+\s*'[^']*'\s*::\s*interval)?$/i.test(normalized);
81
+ };
49
82
  /**
50
83
  * Required extensions for service/meta exports.
51
84
  * Includes native PostgreSQL extensions and pgpm modules for metadata management.
@@ -81,207 +114,17 @@ $LQLMIGRATION$;`;
81
114
  export const META_COMMON_FOOTER = `
82
115
  SET session_replication_role TO DEFAULT;`;
83
116
  /**
84
- * Ordered list of meta tables for export.
85
- * Tables are processed in this order to satisfy foreign key dependencies.
117
+ * Hand-authored per-table overrides layered on top of the generated
118
+ * table manifest. Only exceptional exporter behavior lives here:
119
+ * conflict handling, special field types (image, upload, url) that
120
+ * cannot be inferred, GraphQL type name overrides, and columns whose
121
+ * values must come from DDL defaults at deploy time.
86
122
  */
87
- export const META_TABLE_ORDER = [
88
- 'database',
89
- 'schema',
90
- 'function',
91
- 'table',
92
- 'field',
93
- 'spatial_relation',
94
- 'policy',
95
- 'index',
96
- 'trigger',
97
- 'trigger_function',
98
- 'rls_function',
99
- 'foreign_key_constraint',
100
- 'primary_key_constraint',
101
- 'unique_constraint',
102
- 'check_constraint',
103
- 'full_text_search',
104
- 'schema_grant',
105
- 'table_grant',
106
- 'default_privilege',
107
- 'domains',
108
- 'sites',
109
- 'apis',
110
- 'apps',
111
- 'site_modules',
112
- 'site_themes',
113
- 'site_metadata',
114
- 'api_modules',
115
- 'api_extensions',
116
- 'api_schemas',
117
- 'database_settings',
118
- 'api_settings',
119
- 'rls_settings',
120
- 'cors_settings',
121
- 'pubkey_settings',
122
- 'webauthn_settings',
123
- 'rls_module',
124
- 'user_auth_module',
125
- 'memberships_module',
126
- 'permissions_module',
127
- 'limits_module',
128
- 'levels_module',
129
- 'events_module',
130
- 'users_module',
131
- 'hierarchy_module',
132
- 'membership_types_module',
133
- 'invites_module',
134
- 'emails_module',
135
- 'sessions_module',
136
- 'user_state_module',
137
- 'profiles_module',
138
- 'config_secrets_user_module',
139
- 'user_credentials_module',
140
- 'user_settings_module',
141
- 'connected_accounts_module',
142
- 'phone_numbers_module',
143
- 'crypto_addresses_module',
144
- 'crypto_auth_module',
145
- 'field_module',
146
- 'table_module',
147
- 'secure_table_provision',
148
- 'uuid_module',
149
- 'default_ids_module',
150
- 'denormalized_table_field',
151
- // NOTE: blueprint_template, blueprint, and blueprint_construction are intentionally
152
- // excluded from the export flow — they are runtime-only tables not exported as metadata.
153
- 'relation_provision',
154
- 'entity_type_provision',
155
- 'rate_limits_module',
156
- 'storage_module',
157
- 'billing_module',
158
- 'billing_provider_module',
159
- 'devices_module',
160
- 'identity_providers_module',
161
- 'integration_providers_module',
162
- 'notifications_module',
163
- 'plans_module',
164
- 'realtime_module',
165
- 'session_secrets_module',
166
- 'infra_secrets_module',
167
- 'infra_config_module',
168
- 'internal_secrets_module',
169
- 'i18n_module',
170
- 'agent_module',
171
- 'function_module',
172
- 'namespace_module',
173
- 'merkle_store_module',
174
- 'graph_module',
175
- 'graph_execution_module',
176
- 'function_deployment_module',
177
- 'function_invocation_module',
178
- 'compute_log_module',
179
- 'db_usage_module',
180
- 'storage_log_module',
181
- 'transfer_log_module',
182
- 'webauthn_auth_module',
183
- 'webauthn_credentials_module',
184
- 'inference_log_module',
185
- 'rate_limit_meters_module'
186
- ];
187
- /**
188
- * Shared metadata table configuration.
189
- *
190
- * Fields are discovered dynamically at runtime via introspection:
191
- * - SQL flow: uses information_schema.columns + mapPgTypeToFieldType()
192
- * - GraphQL flow: uses __type introspection + mapGraphQLTypeToFieldType()
193
- *
194
- * Only `typeOverrides` are hardcoded for special types (image, upload, url)
195
- * that cannot be inferred from database/GraphQL types alone.
196
- *
197
- */
198
- export const META_TABLE_CONFIG = {
199
- // =============================================================================
200
- // metaschema_public tables
201
- // =============================================================================
202
- database: {
203
- schema: 'metaschema_public',
204
- table: 'database'
205
- },
206
- schema: {
207
- schema: 'metaschema_public',
208
- table: 'schema'
209
- },
210
- function: {
211
- schema: 'metaschema_public',
212
- table: 'function'
213
- },
214
- table: {
215
- schema: 'metaschema_public',
216
- table: 'table'
217
- },
123
+ export const META_TABLE_OVERRIDES = {
218
124
  field: {
219
- schema: 'metaschema_public',
220
- table: 'field',
221
125
  conflictDoNothing: true
222
126
  },
223
- policy: {
224
- schema: 'metaschema_public',
225
- table: 'policy'
226
- },
227
- index: {
228
- schema: 'metaschema_public',
229
- table: 'index'
230
- },
231
- trigger: {
232
- schema: 'metaschema_public',
233
- table: 'trigger'
234
- },
235
- trigger_function: {
236
- schema: 'metaschema_public',
237
- table: 'trigger_function'
238
- },
239
- rls_function: {
240
- schema: 'metaschema_public',
241
- table: 'rls_function'
242
- },
243
- foreign_key_constraint: {
244
- schema: 'metaschema_public',
245
- table: 'foreign_key_constraint'
246
- },
247
- primary_key_constraint: {
248
- schema: 'metaschema_public',
249
- table: 'primary_key_constraint'
250
- },
251
- unique_constraint: {
252
- schema: 'metaschema_public',
253
- table: 'unique_constraint'
254
- },
255
- check_constraint: {
256
- schema: 'metaschema_public',
257
- table: 'check_constraint'
258
- },
259
- full_text_search: {
260
- schema: 'metaschema_public',
261
- table: 'full_text_search'
262
- },
263
- schema_grant: {
264
- schema: 'metaschema_public',
265
- table: 'schema_grant'
266
- },
267
- table_grant: {
268
- schema: 'metaschema_public',
269
- table: 'table_grant'
270
- },
271
- default_privilege: {
272
- schema: 'metaschema_public',
273
- table: 'default_privilege'
274
- },
275
- // =============================================================================
276
- // services_public tables
277
- // =============================================================================
278
- domains: {
279
- schema: 'services_public',
280
- table: 'domains'
281
- },
282
127
  sites: {
283
- schema: 'services_public',
284
- table: 'sites',
285
128
  typeOverrides: {
286
129
  og_image: 'image',
287
130
  favicon: 'upload',
@@ -293,326 +136,55 @@ export const META_TABLE_CONFIG = {
293
136
  }
294
137
  },
295
138
  apis: {
296
- schema: 'services_public',
297
- table: 'apis',
298
139
  columnDefaults: {
299
140
  dbname: 'current_database()'
300
141
  }
301
142
  },
302
143
  apps: {
303
- schema: 'services_public',
304
- table: 'apps',
305
144
  typeOverrides: {
306
145
  app_image: 'image',
307
146
  app_store_link: 'url',
308
147
  play_store_link: 'url'
309
148
  }
310
149
  },
311
- site_modules: {
312
- schema: 'services_public',
313
- table: 'site_modules'
314
- },
315
- site_themes: {
316
- schema: 'services_public',
317
- table: 'site_themes'
318
- },
319
150
  site_metadata: {
320
- schema: 'services_public',
321
- table: 'site_metadata',
322
151
  typeOverrides: {
323
152
  og_image: 'image'
324
153
  }
325
154
  },
326
- api_modules: {
327
- schema: 'services_public',
328
- table: 'api_modules'
329
- },
330
- api_extensions: {
331
- schema: 'services_public',
332
- table: 'api_extensions'
333
- },
334
- api_schemas: {
335
- schema: 'services_public',
336
- table: 'api_schemas'
337
- },
338
- database_settings: {
339
- schema: 'services_public',
340
- table: 'database_settings'
341
- },
342
- api_settings: {
343
- schema: 'services_public',
344
- table: 'api_settings'
345
- },
346
- rls_settings: {
347
- schema: 'services_public',
348
- table: 'rls_settings'
349
- },
350
- cors_settings: {
351
- schema: 'services_public',
352
- table: 'cors_settings'
353
- },
354
- pubkey_settings: {
355
- schema: 'services_public',
356
- table: 'pubkey_settings'
357
- },
358
- webauthn_settings: {
359
- schema: 'services_public',
360
- table: 'webauthn_settings'
361
- },
362
- // =============================================================================
363
- // metaschema_modules_public tables
364
- // =============================================================================
365
- rls_module: {
366
- schema: 'metaschema_modules_public',
367
- table: 'rls_module'
368
- },
369
- user_auth_module: {
370
- schema: 'metaschema_modules_public',
371
- table: 'user_auth_module'
372
- },
373
- memberships_module: {
374
- schema: 'metaschema_modules_public',
375
- table: 'memberships_module'
376
- },
377
- permissions_module: {
378
- schema: 'metaschema_modules_public',
379
- table: 'permissions_module'
380
- },
381
- limits_module: {
382
- schema: 'metaschema_modules_public',
383
- table: 'limits_module'
384
- },
385
- levels_module: {
386
- schema: 'metaschema_modules_public',
387
- table: 'levels_module'
388
- },
389
- events_module: {
390
- schema: 'metaschema_modules_public',
391
- table: 'events_module'
392
- },
393
- users_module: {
394
- schema: 'metaschema_modules_public',
395
- table: 'users_module'
396
- },
397
- hierarchy_module: {
398
- schema: 'metaschema_modules_public',
399
- table: 'hierarchy_module'
400
- },
401
- membership_types_module: {
402
- schema: 'metaschema_modules_public',
403
- table: 'membership_types_module'
404
- },
405
- invites_module: {
406
- schema: 'metaschema_modules_public',
407
- table: 'invites_module'
408
- },
409
- emails_module: {
410
- schema: 'metaschema_modules_public',
411
- table: 'emails_module'
412
- },
413
- sessions_module: {
414
- schema: 'metaschema_modules_public',
415
- table: 'sessions_module'
416
- },
417
- user_state_module: {
418
- schema: 'metaschema_modules_public',
419
- table: 'user_state_module'
420
- },
421
- profiles_module: {
422
- schema: 'metaschema_modules_public',
423
- table: 'profiles_module'
424
- },
425
- config_secrets_user_module: {
426
- schema: 'metaschema_modules_public',
427
- table: 'config_secrets_user_module'
428
- },
429
- user_credentials_module: {
430
- schema: 'metaschema_modules_public',
431
- table: 'user_credentials_module'
432
- },
433
- user_settings_module: {
434
- schema: 'metaschema_modules_public',
435
- table: 'user_settings_module'
436
- },
437
- connected_accounts_module: {
438
- schema: 'metaschema_modules_public',
439
- table: 'connected_accounts_module'
440
- },
441
- phone_numbers_module: {
442
- schema: 'metaschema_modules_public',
443
- table: 'phone_numbers_module'
444
- },
445
- crypto_addresses_module: {
446
- schema: 'metaschema_modules_public',
447
- table: 'crypto_addresses_module'
448
- },
449
- crypto_auth_module: {
450
- schema: 'metaschema_modules_public',
451
- table: 'crypto_auth_module'
452
- },
453
- field_module: {
454
- schema: 'metaschema_modules_public',
455
- table: 'field_module'
456
- },
457
- table_module: {
458
- schema: 'metaschema_modules_public',
459
- table: 'table_module'
460
- },
461
- // NOTE: table_template_module has been removed from pgpm-modules (superseded by blueprints)
462
- secure_table_provision: {
463
- schema: 'metaschema_modules_public',
464
- table: 'secure_table_provision'
465
- },
466
- uuid_module: {
467
- schema: 'metaschema_modules_public',
468
- table: 'uuid_module'
469
- },
470
- default_ids_module: {
471
- schema: 'metaschema_modules_public',
472
- table: 'default_ids_module'
473
- },
474
- denormalized_table_field: {
475
- schema: 'metaschema_modules_public',
476
- table: 'denormalized_table_field'
477
- },
478
- relation_provision: {
479
- schema: 'metaschema_modules_public',
480
- table: 'relation_provision'
481
- },
482
- entity_type_provision: {
483
- schema: 'metaschema_modules_public',
484
- table: 'entity_type_provision'
485
- },
486
- rate_limits_module: {
487
- schema: 'metaschema_modules_public',
488
- table: 'rate_limits_module'
489
- },
490
- storage_module: {
491
- schema: 'metaschema_modules_public',
492
- table: 'storage_module'
493
- },
494
- billing_module: {
495
- schema: 'metaschema_modules_public',
496
- table: 'billing_module'
497
- },
498
- billing_provider_module: {
499
- schema: 'metaschema_modules_public',
500
- table: 'billing_provider_module'
501
- },
502
- devices_module: {
503
- schema: 'metaschema_modules_public',
504
- table: 'devices_module'
505
- },
506
- identity_providers_module: {
507
- schema: 'metaschema_modules_public',
508
- table: 'identity_providers_module'
509
- },
510
- integration_providers_module: {
511
- schema: 'metaschema_modules_public',
512
- table: 'integration_providers_module'
513
- },
514
- notifications_module: {
515
- schema: 'metaschema_modules_public',
516
- table: 'notifications_module'
517
- },
518
- plans_module: {
519
- schema: 'metaschema_modules_public',
520
- table: 'plans_module'
521
- },
522
- realtime_module: {
523
- schema: 'metaschema_modules_public',
524
- table: 'realtime_module'
525
- },
526
- session_secrets_module: {
527
- schema: 'metaschema_modules_public',
528
- table: 'session_secrets_module'
529
- },
530
- infra_secrets_module: {
531
- schema: 'metaschema_modules_public',
532
- table: 'infra_secrets_module'
533
- },
534
- infra_config_module: {
535
- schema: 'metaschema_modules_public',
536
- table: 'infra_config_module'
537
- },
538
- internal_secrets_module: {
539
- schema: 'metaschema_modules_public',
540
- table: 'internal_secrets_module'
541
- },
542
155
  i18n_module: {
543
- schema: 'metaschema_modules_public',
544
- table: 'i18n_module',
545
156
  gqlTypeName: 'I18NModule' // i18n is a well-known abbreviation; PostGraphile inflector capitalizes the N
546
- },
547
- agent_module: {
548
- schema: 'metaschema_modules_public',
549
- table: 'agent_module'
550
- },
551
- function_module: {
552
- schema: 'metaschema_modules_public',
553
- table: 'function_module'
554
- },
555
- namespace_module: {
556
- schema: 'metaschema_modules_public',
557
- table: 'namespace_module'
558
- },
559
- merkle_store_module: {
560
- schema: 'metaschema_modules_public',
561
- table: 'merkle_store_module'
562
- },
563
- graph_module: {
564
- schema: 'metaschema_modules_public',
565
- table: 'graph_module'
566
- },
567
- graph_execution_module: {
568
- schema: 'metaschema_modules_public',
569
- table: 'graph_execution_module'
570
- },
571
- function_deployment_module: {
572
- schema: 'metaschema_modules_public',
573
- table: 'function_deployment_module'
574
- },
575
- function_invocation_module: {
576
- schema: 'metaschema_modules_public',
577
- table: 'function_invocation_module'
578
- },
579
- compute_log_module: {
580
- schema: 'metaschema_modules_public',
581
- table: 'compute_log_module'
582
- },
583
- db_usage_module: {
584
- schema: 'metaschema_modules_public',
585
- table: 'db_usage_module'
586
- },
587
- storage_log_module: {
588
- schema: 'metaschema_modules_public',
589
- table: 'storage_log_module'
590
- },
591
- transfer_log_module: {
592
- schema: 'metaschema_modules_public',
593
- table: 'transfer_log_module'
594
- },
595
- webauthn_auth_module: {
596
- schema: 'metaschema_modules_public',
597
- table: 'webauthn_auth_module'
598
- },
599
- webauthn_credentials_module: {
600
- schema: 'metaschema_modules_public',
601
- table: 'webauthn_credentials_module'
602
- },
603
- inference_log_module: {
604
- schema: 'metaschema_modules_public',
605
- table: 'inference_log_module',
606
- },
607
- rate_limit_meters_module: {
608
- schema: 'metaschema_modules_public',
609
- table: 'rate_limit_meters_module',
610
- },
611
- spatial_relation: {
612
- schema: 'metaschema_public',
613
- table: 'spatial_relation'
614
157
  }
615
158
  };
159
+ /**
160
+ * Ordered list of meta tables for export, derived from the generated
161
+ * manifest. Tables are already topologically sorted by foreign key
162
+ * dependencies at generation time.
163
+ */
164
+ export const META_TABLE_ORDER = metaExportTables.tables.map(t => t.key);
165
+ /**
166
+ * Shared metadata table configuration, built from the generated manifest
167
+ * plus META_TABLE_OVERRIDES.
168
+ *
169
+ * Fields are discovered dynamically at runtime via introspection:
170
+ * - SQL flow: uses information_schema.columns + mapPgTypeToFieldType()
171
+ * - GraphQL flow: uses __type introspection + mapGraphQLTypeToFieldType()
172
+ *
173
+ * Only `typeOverrides` are hardcoded for special types (image, upload, url)
174
+ * that cannot be inferred from database/GraphQL types alone.
175
+ */
176
+ export const META_TABLE_CONFIG = Object.fromEntries(metaExportTables.tables.map(t => [
177
+ t.key,
178
+ { schema: t.schema, table: t.table, ...META_TABLE_OVERRIDES[t.key] }
179
+ ]));
180
+ /**
181
+ * Per-table timestamptz columns whose DDL default is non-deterministic
182
+ * (now(), CURRENT_TIMESTAMP, clock_timestamp(), now() + interval).
183
+ * Generated by constructive-db and propagated here via constructive-hub.
184
+ * The GraphQL export path uses this to omit those columns without a DB pool.
185
+ */
186
+ export const META_TABLE_TIMESTAMP_DEFAULTS = metaExportTables.timestampDefaultColumns || {};
187
+ export const getTimestampDefaultColumnsForTable = (schema, table) => META_TABLE_TIMESTAMP_DEFAULTS[`${schema}.${table}`] || [];
616
188
  // =============================================================================
617
189
  // Shared functions
618
190
  // =============================================================================
package/esm/index.js CHANGED
@@ -4,6 +4,6 @@ export * from './export-graphql';
4
4
  export * from './export-graphql-meta';
5
5
  export { GraphQLClient } from './graphql-client';
6
6
  export { getGraphQLQueryName, getGraphQLTypeName, graphqlRowToPostgresRow, buildFieldsFragment, mapGraphQLTypeToFieldType, unwrapGraphQLType } from './graphql-naming';
7
- export { DB_REQUIRED_EXTENSIONS, SERVICE_REQUIRED_EXTENSIONS, META_COMMON_HEADER, META_COMMON_FOOTER, META_TABLE_ORDER, META_TABLE_CONFIG, mapPgTypeToFieldType, makeReplacer, preparePackage, normalizeOutdir, detectMissingModules, installMissingModules } from './export-utils';
7
+ export { DB_REQUIRED_EXTENSIONS, SERVICE_REQUIRED_EXTENSIONS, META_COMMON_HEADER, META_COMMON_FOOTER, META_TABLE_ORDER, META_TABLE_CONFIG, META_TABLE_OVERRIDES, mapPgTypeToFieldType, makeReplacer, preparePackage, normalizeOutdir, detectMissingModules, installMissingModules } from './export-utils';
8
8
  export { PG_TYPE_MAP, lookupByPgUdt, lookupByGqlType } from './type-map';
9
9
  export { intervalToPostgres, parsePgInterval } from './interval-utils';