pg-codegen 2.1.5

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/esm/query.js ADDED
@@ -0,0 +1,420 @@
1
+ /*
2
+ * IMPORTANT: when editing this file, ensure all operators (e.g. `@>`) are
3
+ * specified in the correct namespace (e.g. `operator(pg_catalog.@>)`). It looks
4
+ * weird, but it prevents clashes with extensions or user code that may
5
+ * overload operators, e.g. extension `intarray` overloads `@>`.
6
+ *
7
+ * NOTE: I'm not doing this with `=` because that way lies madness.
8
+ */
9
+ export const makeIntrospectionQuery = (serverVersionNum, options = {}) => {
10
+ const { pgLegacyFunctionsOnly = false, pgIgnoreRBAC = true } = options;
11
+ const unionRBAC = `
12
+ union all
13
+ select pg_roles.oid _oid, pg_roles.*
14
+ from pg_roles, accessible_roles, pg_auth_members
15
+ where pg_auth_members.roleid = pg_roles.oid
16
+ and pg_auth_members.member = accessible_roles._oid
17
+ `;
18
+ return `\
19
+ -- @see https://www.postgresql.org/docs/9.5/static/catalogs.html
20
+ -- @see https://github.com/graphile/graphile-engine/blob/master/packages/graphile-build-pg/src/plugins/introspectionQuery.js
21
+ --
22
+ -- ## Parameters
23
+ -- - \`$1\`: An array of strings that represent the namespaces we are introspecting.
24
+ -- - \`$2\`: set true to include functions/tables/etc that come from extensions
25
+ with
26
+ ${!pgIgnoreRBAC ? 'recursive' : ''} accessible_roles(_oid) as (
27
+ select oid _oid, pg_roles.*
28
+ from pg_roles
29
+ where rolname = current_user
30
+ ${!pgIgnoreRBAC ? unionRBAC : ''}
31
+ ),
32
+ -- @see https://www.postgresql.org/docs/9.5/static/catalog-pg-namespace.html
33
+ namespace as (
34
+ select
35
+ 'namespace' as "kind",
36
+ nsp.oid as "id",
37
+ nsp.nspname as "name",
38
+ dsc.description as "description"
39
+ from
40
+ pg_catalog.pg_namespace as nsp
41
+ left join pg_catalog.pg_description as dsc on dsc.objoid = nsp.oid and dsc.classoid = 'pg_catalog.pg_namespace'::regclass
42
+ where
43
+ nsp.nspname = any ($1)
44
+ order by
45
+ nsp.nspname
46
+ ),
47
+ -- Select all of the remote procedures we can use in this schema. This comes
48
+ -- first so that we can select types and classes using the information we get
49
+ -- from it.
50
+ --
51
+ -- @see https://www.postgresql.org/docs/9.6/static/catalog-pg-proc.html
52
+ procedure as (
53
+ select
54
+ 'procedure' as "kind",
55
+ pro.oid as "id",
56
+ pro.proname as "name",
57
+ dsc.description as "description",
58
+ pro.pronamespace as "namespaceId",
59
+ nsp.nspname as "namespaceName",
60
+ pro.proisstrict as "isStrict",
61
+ pro.proretset as "returnsSet",
62
+ case
63
+ when pro.provolatile = 'i' then true
64
+ when pro.provolatile = 's' then true
65
+ else false
66
+ end as "isStable",
67
+ pro.prorettype as "returnTypeId",
68
+ coalesce(pro.proallargtypes, pro.proargtypes) as "argTypeIds",
69
+ coalesce(pro.proargmodes, array[]::text[]) as "argModes",
70
+ coalesce(pro.proargnames, array[]::text[]) as "argNames",
71
+ pro.pronargs as "inputArgsCount",
72
+ pro.pronargdefaults as "argDefaultsNum",
73
+ pro.procost as "cost",
74
+ exists(select 1 from accessible_roles where has_function_privilege(accessible_roles.oid, pro.oid, 'EXECUTE')) as "aclExecutable",
75
+ (select lanname from pg_catalog.pg_language where pg_language.oid = pro.prolang) as "language"
76
+ from
77
+ pg_catalog.pg_proc as pro
78
+ left join pg_catalog.pg_description as dsc on dsc.objoid = pro.oid and dsc.classoid = 'pg_catalog.pg_proc'::regclass
79
+ left join pg_catalog.pg_namespace as nsp on nsp.oid = pro.pronamespace
80
+ where
81
+ pro.pronamespace in (select "id" from namespace) and
82
+ -- Currently we don’t support functions with variadic arguments. In the
83
+ -- future we may, but for now let’s just ignore functions with variadic
84
+ -- arguments.
85
+ -- TODO: Variadic arguments.
86
+ pro.provariadic = 0 and
87
+ -- Filter our aggregate functions and window functions.
88
+ ${serverVersionNum >= 110000
89
+ ? "pro.prokind = 'f'"
90
+ : 'pro.proisagg = false and pro.proiswindow = false'} and
91
+ ${pgLegacyFunctionsOnly
92
+ ? `\
93
+ -- We want to make sure the argument mode for all of our arguments is
94
+ -- \`IN\` which means \`proargmodes\` will be null.
95
+ pro.proargmodes is null and
96
+ -- Do not select procedures that return \`RECORD\` (oid 2249).
97
+ pro.prorettype operator(pg_catalog.<>) 2249 and`
98
+ : `\
99
+ -- We want to make sure the argument modes for all of our arguments are
100
+ -- \`IN\`, \`OUT\`, \`INOUT\`, or \`TABLE\` (not \`VARIADIC\`).
101
+ (pro.proargmodes is null or pro.proargmodes operator(pg_catalog.<@) array['i','o','b','t']::"char"[]) and
102
+ -- Do not select procedures that return \`RECORD\` (oid 2249) unless they
103
+ -- have \`OUT\`, \`INOUT\`, or \`TABLE\` arguments to define the return type.
104
+ (pro.prorettype operator(pg_catalog.<>) 2249 or pro.proargmodes && array['o','b','t']::"char"[]) and
105
+ -- Do not select procedures that have \`RECORD\` arguments.
106
+ (pro.proallargtypes is null or not (pro.proallargtypes operator(pg_catalog.@>) array[2249::oid])) and`}
107
+ -- Do not select procedures that create range types. These are utility
108
+ -- functions that really don’t need to be exposed in an API.
109
+ pro.proname not in (
110
+ select typ.typname
111
+ from pg_catalog.pg_type as typ
112
+ where typ.typtype = 'r'
113
+ and typ.typnamespace = pro.pronamespace
114
+ ) and
115
+ -- Do not expose trigger functions (type trigger has oid 2279)
116
+ pro.prorettype operator(pg_catalog.<>) 2279 and
117
+ -- We don't want functions that will clash with GraphQL (treat them as private)
118
+ pro.proname not like E'\\\\_\\\\_%' and
119
+ -- We also don’t want procedures that have been defined in our namespace
120
+ -- twice. This leads to duplicate fields in the API which throws an
121
+ -- error. In the future we may support this case. For now though, it is
122
+ -- too complex.
123
+ (
124
+ select count(pro2.*)
125
+ from pg_catalog.pg_proc as pro2
126
+ where pro2.pronamespace = pro.pronamespace
127
+ and pro2.proname = pro.proname
128
+ ) = 1 and
129
+ ($2 is true or not exists(
130
+ select 1
131
+ from pg_catalog.pg_depend
132
+ where pg_depend.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass
133
+ and pg_depend.deptype = 'e'
134
+ and pg_depend.classid = 'pg_catalog.pg_proc'::pg_catalog.regclass
135
+ and pg_depend.objid = pro.oid
136
+ ))
137
+ order by
138
+ pro.pronamespace, pro.proname
139
+ ),
140
+ -- @see https://www.postgresql.org/docs/9.5/static/catalog-pg-class.html
141
+ class as (
142
+ select
143
+ 'class' as "kind",
144
+ rel.oid as "id",
145
+ rel.relname as "name",
146
+ rel.relkind as "classKind",
147
+ dsc.description as "description",
148
+ rel.relnamespace as "namespaceId",
149
+ nsp.nspname as "namespaceName",
150
+ rel.reltype as "typeId",
151
+ -- Here we determine whether or not we can use this class in a
152
+ -- \`SELECT\`’s \`FROM\` clause. In order to determine this we look at them
153
+ -- \`relkind\` column, if it is \`i\` (index) or \`c\` (composite), we cannot
154
+ -- select this class. Otherwise we can.
155
+ rel.relkind not in ('i', 'c') as "isSelectable",
156
+ -- Here we are determining whether we can insert/update/delete a class.
157
+ -- This is helpful as it lets us detect non-updatable views and then
158
+ -- exclude them from being inserted/updated/deleted into. For more info
159
+ -- on how \`pg_catalog.pg_relation_is_updatable\` works:
160
+ --
161
+ -- - https://www.postgresql.org/message-id/CAEZATCV2_qN9P3zbvADwME_TkYf2gR_X2cLQR4R+pqkwxGxqJg@mail.gmail.com
162
+ -- - https://github.com/postgres/postgres/blob/2410a2543e77983dab1f63f48b2adcd23dba994e/src/backend/utils/adt/misc.c#L684
163
+ -- - https://github.com/postgres/postgres/blob/3aff33aa687e47d52f453892498b30ac98a296af/src/backend/rewrite/rewriteHandler.c#L2351
164
+ (pg_catalog.pg_relation_is_updatable(rel.oid, true)::bit(8) operator(pg_catalog.&) B'00010000') = B'00010000' as "isDeletable",
165
+ (pg_catalog.pg_relation_is_updatable(rel.oid, true)::bit(8) operator(pg_catalog.&) B'00001000') = B'00001000' as "isInsertable",
166
+ (pg_catalog.pg_relation_is_updatable(rel.oid, true)::bit(8) operator(pg_catalog.&) B'00000100') = B'00000100' as "isUpdatable",
167
+ exists(select 1 from accessible_roles where has_table_privilege(accessible_roles.oid, rel.oid, 'SELECT')) as "aclSelectable",
168
+ exists(select 1 from accessible_roles where has_table_privilege(accessible_roles.oid, rel.oid, 'INSERT')) as "aclInsertable",
169
+ exists(select 1 from accessible_roles where has_table_privilege(accessible_roles.oid, rel.oid, 'UPDATE')) as "aclUpdatable",
170
+ exists(select 1 from accessible_roles where has_table_privilege(accessible_roles.oid, rel.oid, 'DELETE')) as "aclDeletable"
171
+ from
172
+ pg_catalog.pg_class as rel
173
+ left join pg_catalog.pg_description as dsc on dsc.objoid = rel.oid and dsc.objsubid = 0 and dsc.classoid = 'pg_catalog.pg_class'::regclass
174
+ left join pg_catalog.pg_namespace as nsp on nsp.oid = rel.relnamespace
175
+ where
176
+ rel.relpersistence in ('p') and
177
+ -- We don't want classes that will clash with GraphQL (treat them as private)
178
+ rel.relname not like E'\\\\_\\\\_%' and
179
+ rel.relkind in ('r', 'v', 'm', 'c', 'f') and
180
+ ($2 is true or not exists(
181
+ select 1
182
+ from pg_catalog.pg_depend
183
+ where pg_depend.refclassid = 'pg_catalog.pg_extension'::pg_catalog.regclass
184
+ and pg_depend.deptype = 'e'
185
+ and pg_depend.classid = 'pg_catalog.pg_class'::pg_catalog.regclass
186
+ and pg_depend.objid = rel.oid
187
+ ))
188
+ order by
189
+ rel.relnamespace, rel.relname
190
+ ),
191
+ -- @see https://www.postgresql.org/docs/9.5/static/catalog-pg-attribute.html
192
+ attribute as (
193
+ select
194
+ 'attribute' as "kind",
195
+ att.attrelid as "classId",
196
+ att.attnum as "num",
197
+ att.attname as "name",
198
+ dsc.description as "description",
199
+ att.atttypid as "typeId",
200
+ nullif(att.atttypmod, -1) as "typeModifier",
201
+ att.attnotnull as "isNotNull",
202
+ att.atthasdef as "hasDefault",
203
+ ${serverVersionNum >= 100000 ? 'att.attidentity' : "''"} as "identity",
204
+ exists(select 1 from accessible_roles where has_column_privilege(accessible_roles.oid, att.attrelid, att.attname, 'SELECT')) as "aclSelectable",
205
+ exists(select 1 from accessible_roles where has_column_privilege(accessible_roles.oid, att.attrelid, att.attname, 'INSERT')) as "aclInsertable",
206
+ exists(select 1 from accessible_roles where has_column_privilege(accessible_roles.oid, att.attrelid, att.attname, 'UPDATE')) as "aclUpdatable",
207
+ -- https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit;h=c62dd80cdf149e2792b13c13777a539f5abb0370
208
+ att.attacl is not null and exists(select 1 from aclexplode(att.attacl) aclitem where aclitem.privilege_type = 'SELECT' and grantee in (select oid from accessible_roles)) as "columnLevelSelectGrant"
209
+ from
210
+ pg_catalog.pg_attribute as att
211
+ left join pg_catalog.pg_description as dsc on dsc.objoid = att.attrelid and dsc.objsubid = att.attnum and dsc.classoid = 'pg_catalog.pg_class'::regclass
212
+ where
213
+ att.attrelid in (select "id" from class) and
214
+ att.attnum > 0 and
215
+ -- We don't want attributes that will clash with GraphQL (treat them as private)
216
+ att.attname not like E'\\\\_\\\\_%' and
217
+ not att.attisdropped
218
+ order by
219
+ att.attrelid, att.attnum
220
+ ),
221
+ -- @see https://www.postgresql.org/docs/9.5/static/catalog-pg-type.html
222
+ type as (
223
+ -- Use another \`WITH\` statement here, because our \`WHERE\` clause will need
224
+ -- to use it.
225
+ with type_all as (
226
+ select
227
+ 'type' as "kind",
228
+ typ.oid as "id",
229
+ typ.typname as "name",
230
+ dsc.description as "description",
231
+ typ.typnamespace as "namespaceId",
232
+ -- We include the namespace name in types only because we select so
233
+ -- many types that are outside of our core set of namespaces. Having
234
+ -- the namespace name is super helpful when generating SQL, so
235
+ -- conditionally having namespace names for types is a pain.
236
+ nsp.nspname as "namespaceName",
237
+ typ.typtype as "type",
238
+ typ.typcategory as "category",
239
+ typ.typnotnull as "domainIsNotNull",
240
+ nullif(typ.typelem, 0) as "arrayItemTypeId",
241
+ typ.typlen as "typeLength",
242
+ (typ.typelem <> 0 and typ.typlen = -1) as "isPgArray",
243
+ nullif(typ.typrelid, 0) as "classId",
244
+ nullif(typ.typbasetype, 0) as "domainBaseTypeId",
245
+ nullif(typ.typtypmod, -1) as "domainTypeModifier",
246
+ typ.typdefaultbin is not null as "domainHasDefault",
247
+ -- If this type is an enum type, let’s select all of its enum variants.
248
+ --
249
+ -- @see https://www.postgresql.org/docs/9.5/static/catalog-pg-enum.html
250
+ case
251
+ when typ.typtype = 'e' then array(
252
+ select enm.enumlabel
253
+ from pg_catalog.pg_enum as enm
254
+ where enm.enumtypid = typ.oid
255
+ order by enm.enumsortorder
256
+ )
257
+ else null
258
+ end as "enumVariants",
259
+ -- If this type is a range type, we want to select the sub type of the
260
+ -- range.
261
+ --
262
+ -- @see https://www.postgresql.org/docs/9.6/static/catalog-pg-range.html
263
+ case
264
+ when typ.typtype = 'r' then (
265
+ select rng.rngsubtype
266
+ from pg_catalog.pg_range as rng
267
+ where rng.rngtypid = typ.oid
268
+ limit 1
269
+ )
270
+ else null
271
+ end as "rangeSubTypeId"
272
+ from
273
+ pg_catalog.pg_type as typ
274
+ left join pg_catalog.pg_description as dsc on dsc.objoid = typ.oid and dsc.classoid = 'pg_catalog.pg_type'::regclass
275
+ left join pg_catalog.pg_namespace as nsp on nsp.oid = typ.typnamespace
276
+ )
277
+ select
278
+ *
279
+ from
280
+ type_all as typ
281
+ where
282
+ typ.id in (
283
+ select "typeId" from class
284
+ union all
285
+ select "typeId" from attribute
286
+ union all
287
+ select "returnTypeId" from procedure
288
+ union all
289
+ select unnest("argTypeIds") from procedure
290
+ union all
291
+ -- If this type is a base type for *any* domain type, we will include it
292
+ -- in our selection. This may mean we fetch more types than we need, but
293
+ -- the alternative is to do some funky SQL recursion which would be hard
294
+ -- code to read. So we prefer code readability over selecting like 3 or
295
+ -- 4 less type rows.
296
+ --
297
+ -- We also do this for range sub types and array item types.
298
+ select "domainBaseTypeId" from type_all
299
+ union all
300
+ select "rangeSubTypeId" from type_all
301
+ union all
302
+ select "arrayItemTypeId" from type_all
303
+ )
304
+ order by
305
+ "namespaceId", "name"
306
+ ),
307
+ -- @see https://www.postgresql.org/docs/9.5/static/catalog-pg-constraint.html
308
+ "constraint" as (
309
+ select distinct on (con.conrelid, con.conkey, con.confrelid, con.confkey)
310
+ 'constraint' as "kind",
311
+ con.oid as "id",
312
+ con.conname as "name",
313
+ con.contype as "type",
314
+ con.conrelid as "classId",
315
+ nullif(con.confrelid, 0) as "foreignClassId",
316
+ dsc.description as "description",
317
+ con.conkey as "keyAttributeNums",
318
+ con.confkey as "foreignKeyAttributeNums"
319
+ from
320
+ pg_catalog.pg_constraint as con
321
+ inner join class on (con.conrelid = class.id)
322
+ left join pg_catalog.pg_description as dsc on dsc.objoid = con.oid and dsc.classoid = 'pg_catalog.pg_constraint'::regclass
323
+ where
324
+ -- Only get constraints for classes we have selected.
325
+ con.conrelid in (select "id" from class where "namespaceId" in (select "id" from namespace)) and
326
+ case
327
+ -- If this is a foreign key constraint, we want to ensure that the
328
+ -- foreign class is also in the list of classes we have already
329
+ -- selected.
330
+ when con.contype = 'f' then con.confrelid in (select "id" from class where "namespaceId" in (select "id" from namespace))
331
+ -- Otherwise, this should be true.
332
+ else true
333
+ end and
334
+ -- We only want foreign key, primary key, and unique constraints. We
335
+ -- made add support for more constraints in the future.
336
+ con.contype in ('f', 'p', 'u')
337
+ order by
338
+ con.conrelid, con.conkey, con.confrelid, con.confkey, con.conname
339
+ ),
340
+ -- @see https://www.postgresql.org/docs/9.5/static/catalog-pg-extension.html
341
+ "extension" as (
342
+ select
343
+ 'extension' as "kind",
344
+ ext.oid as "id",
345
+ ext.extname as "name",
346
+ ext.extnamespace as "namespaceId",
347
+ nsp.nspname as "namespaceName",
348
+ ext.extrelocatable as "relocatable",
349
+ ext.extversion as "version",
350
+ ext.extconfig as "configurationClassIds",
351
+ dsc.description as "description"
352
+ from
353
+ pg_catalog.pg_extension as ext
354
+ left join pg_catalog.pg_description as dsc on dsc.objoid = ext.oid and dsc.classoid = 'pg_catalog.pg_extension'::regclass
355
+ left join pg_catalog.pg_namespace as nsp on nsp.oid = ext.extnamespace
356
+ order by
357
+ ext.extname, ext.oid
358
+ ),
359
+ -- @see https://www.postgresql.org/docs/9.5/static/catalog-pg-index.html
360
+ "indexes" as (
361
+ select
362
+ 'index' as "kind",
363
+ idx.indexrelid as "id",
364
+ idx_more.relname as "name",
365
+ nsp.nspname as "namespaceName",
366
+ idx.indrelid as "classId",
367
+ idx.indnatts as "numberOfAttributes",
368
+ idx.indisunique as "isUnique",
369
+ idx.indisprimary as "isPrimary",
370
+ idx.indimmediate as "isImmediate", -- enforce uniqueness immediately on insert
371
+ idx.indisreplident as "isReplicaIdentity",
372
+ idx.indisvalid as "isValid", -- if false, don't use for queries
373
+ idx.indpred is not null as "isPartial", -- if true, index is not on on rows.
374
+ idx.indkey as "attributeNums",
375
+ am.amname as "indexType",
376
+ ${serverVersionNum >= 90600
377
+ ? `\
378
+ (
379
+ select array_agg(pg_index_column_has_property(idx.indexrelid,n::int2,'asc'))
380
+ from unnest(idx.indkey) with ordinality as ord(key,n)
381
+ ) as "attributePropertiesAsc",
382
+ (
383
+ select array_agg(pg_index_column_has_property(idx.indexrelid,n::int2,'nulls_first'))
384
+ from unnest(idx.indkey) with ordinality as ord(key,n)
385
+ ) as "attributePropertiesNullsFirst",`
386
+ : ''}
387
+ dsc.description as "description"
388
+ from
389
+ pg_catalog.pg_index as idx
390
+ inner join pg_catalog.pg_class idx_more on (idx.indexrelid = idx_more.oid)
391
+ inner join class on (idx.indrelid = class.id)
392
+ inner join pg_catalog.pg_namespace as nsp on (nsp.oid = idx_more.relnamespace)
393
+ inner join pg_catalog.pg_am as am on (am.oid = idx_more.relam)
394
+ left join pg_catalog.pg_description as dsc on dsc.objoid = idx.indexrelid and dsc.objsubid = 0 and dsc.classoid = 'pg_catalog.pg_class'::regclass
395
+ where
396
+ idx.indislive is not false and
397
+ idx.indisexclusion is not true and -- exclusion index
398
+ idx.indcheckxmin is not true and -- always valid?
399
+ idx.indpred is null -- no partial index predicate
400
+ order by
401
+ idx.indrelid, idx.indexrelid
402
+ )
403
+ select row_to_json(x) as object from namespace as x
404
+ union all
405
+ select row_to_json(x) as object from class as x
406
+ union all
407
+ select row_to_json(x) as object from attribute as x
408
+ union all
409
+ select row_to_json(x) as object from type as x
410
+ union all
411
+ select row_to_json(x) as object from "constraint" as x
412
+ union all
413
+ select row_to_json(x) as object from procedure as x
414
+ union all
415
+ select row_to_json(x) as object from extension as x
416
+ union all
417
+ select row_to_json(x) as object from indexes as x
418
+ ;
419
+ `;
420
+ };
package/esm/types.js ADDED
@@ -0,0 +1 @@
1
+ export {};
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/index.js ADDED
@@ -0,0 +1,74 @@
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
+ const fs_1 = require("fs");
7
+ const path_1 = __importDefault(require("path"));
8
+ const codegen_1 = require("./codegen/codegen");
9
+ const introspect_1 = __importDefault(require("./introspect"));
10
+ const server_utils_1 = require("@launchql/server-utils");
11
+ const types_1 = require("@launchql/types");
12
+ const server_utils_2 = require("@launchql/server-utils");
13
+ const log = new server_utils_1.Logger('codegen');
14
+ (async () => {
15
+ const env = (0, types_1.getPgEnvOptions)();
16
+ const pool = (0, server_utils_2.getRootPgPool)(env);
17
+ const options = {
18
+ // @ts-ignore
19
+ client: pool, // hope this is ok?
20
+ introspectionOptions: {
21
+ pgLegacyFunctionsOnly: false,
22
+ pgIgnoreRBAC: true,
23
+ },
24
+ namespacesToIntrospect: ['collections_public'],
25
+ includeExtensions: false,
26
+ };
27
+ const outputDirectory = path_1.default.join(__dirname, '../../../__fixtures__/output');
28
+ try {
29
+ // Clean the output directory
30
+ await fs_1.promises.rm(outputDirectory, { recursive: true, force: true });
31
+ log.info(`Cleaned output directory: ${outputDirectory}`);
32
+ // Fetch introspection rows
33
+ const rows = await (0, introspect_1.default)(options);
34
+ log.info('Introspection Rows Fetched:', rows);
35
+ // Generate TypeScript code
36
+ const codegenOptions = {
37
+ includeTimestamps: true,
38
+ includeUUID: true,
39
+ };
40
+ const generatedCode = (0, codegen_1.generateCodeTree)(rows, codegenOptions);
41
+ log.info('Generated TypeScript Code Tree:', generatedCode);
42
+ // Write the generated code to files
43
+ await writeGeneratedFiles(outputDirectory, generatedCode);
44
+ log.info(`Generated files written to ${outputDirectory}`);
45
+ }
46
+ catch (error) {
47
+ log.error('Failed to fetch introspection rows or generate code:', error);
48
+ }
49
+ })();
50
+ /**
51
+ * Writes the generated code files to the specified directory.
52
+ *
53
+ * @param outputDir The base directory to write the files.
54
+ * @param fileTree The file tree containing file paths and content.
55
+ */
56
+ const writeGeneratedFiles = async (outputDir, fileTree) => {
57
+ try {
58
+ // Ensure the output directory exists
59
+ await fs_1.promises.mkdir(outputDir, { recursive: true });
60
+ // Write each file to its corresponding path
61
+ for (const [filePath, content] of Object.entries(fileTree)) {
62
+ const fullPath = path_1.default.join(outputDir, filePath);
63
+ // Ensure the directory for the file exists
64
+ const dirName = path_1.default.dirname(fullPath);
65
+ await fs_1.promises.mkdir(dirName, { recursive: true });
66
+ // Write the file content
67
+ await fs_1.promises.writeFile(fullPath, content, 'utf8');
68
+ }
69
+ }
70
+ catch (error) {
71
+ log.error(`Failed to write files to ${outputDir}:`, error);
72
+ throw error;
73
+ }
74
+ };
@@ -0,0 +1,11 @@
1
+ import { Client } from 'pg';
2
+ import { IntrospectionOptions } from './query';
3
+ import { DatabaseObject } from './types';
4
+ export interface GetIntrospectionRowsOptions {
5
+ client: Client;
6
+ introspectionOptions: IntrospectionOptions;
7
+ namespacesToIntrospect: string[];
8
+ includeExtensions?: boolean;
9
+ }
10
+ export declare const getIntrospectionRows: (options: GetIntrospectionRowsOptions) => Promise<DatabaseObject[]>;
11
+ export default getIntrospectionRows;
package/introspect.js ADDED
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getIntrospectionRows = void 0;
4
+ const query_1 = require("./query");
5
+ const server_utils_1 = require("@launchql/server-utils");
6
+ const log = new server_utils_1.Logger('codegen');
7
+ const getIntrospectionRows = async (options) => {
8
+ const { client, introspectionOptions, namespacesToIntrospect, includeExtensions = false, } = options;
9
+ try {
10
+ // Query for server version in integer format
11
+ const res = await client.query('SHOW server_version_num');
12
+ const serverVersionNum = parseInt(res.rows[0].server_version_num, 10);
13
+ // Generate the introspection query
14
+ const introspectionQuery = (0, query_1.makeIntrospectionQuery)(serverVersionNum, introspectionOptions);
15
+ // Execute the introspection query
16
+ const queryResult = await client.query(introspectionQuery, [
17
+ namespacesToIntrospect,
18
+ includeExtensions,
19
+ ]);
20
+ // Map the result rows to the `DatabaseObject` type
21
+ const rows = queryResult.rows.map((result) => result.object);
22
+ return rows;
23
+ }
24
+ catch (error) {
25
+ log.error('Error during introspection:', error);
26
+ throw error;
27
+ }
28
+ };
29
+ exports.getIntrospectionRows = getIntrospectionRows;
30
+ exports.default = exports.getIntrospectionRows;
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "pg-codegen",
3
+ "version": "2.1.5",
4
+ "author": "Dan Lynch <pyramation@gmail.com>",
5
+ "description": "PostgreSQL Codegen",
6
+ "main": "index.js",
7
+ "module": "esm/index.js",
8
+ "types": "index.d.ts",
9
+ "homepage": "https://github.com/launchql/launchql",
10
+ "license": "MIT",
11
+ "publishConfig": {
12
+ "access": "public",
13
+ "directory": "dist"
14
+ },
15
+ "repository": {
16
+ "type": "git",
17
+ "url": "https://github.com/launchql/launchql"
18
+ },
19
+ "bugs": {
20
+ "url": "https://github.com/launchql/launchql/issues"
21
+ },
22
+ "scripts": {
23
+ "copy": "copyfiles -f ../../LICENSE README.md package.json dist",
24
+ "clean": "rimraf dist/**",
25
+ "prepare": "npm run build",
26
+ "build": "npm run clean; tsc; tsc -p tsconfig.esm.json; npm run copy",
27
+ "build:dev": "npm run clean; tsc --declarationMap; tsc -p tsconfig.esm.json; npm run copy",
28
+ "dev": "ts-node ./src/index.ts",
29
+ "lint": "eslint . --fix",
30
+ "test": "jest",
31
+ "test:watch": "jest --watch"
32
+ },
33
+ "keywords": [
34
+ "codegen",
35
+ "graphql",
36
+ "typescript",
37
+ "launchql",
38
+ "generator"
39
+ ],
40
+ "dependencies": {
41
+ "@babel/generator": "^7.26.3",
42
+ "@babel/types": "^7.26.3",
43
+ "@launchql/types": "^2.1.6",
44
+ "@launchql/server-utils": "^2.1.8",
45
+ "pg": "^8.16.0",
46
+ "pgsql-test": "^2.1.14"
47
+ },
48
+ "devDependencies": {
49
+ "@types/pg": "^8.15.2"
50
+ }
51
+ }
package/query.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ export interface IntrospectionOptions {
2
+ pgLegacyFunctionsOnly: boolean;
3
+ pgIgnoreRBAC: boolean;
4
+ }
5
+ export declare const makeIntrospectionQuery: (serverVersionNum: number, options?: IntrospectionOptions) => string;