neon 8.4.5 → 8.5.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.
- package/README.md +7 -7
- package/dist/_chunks/{env-4WGRW2nw.js → env-BNm69Qh8.js} +2 -5
- package/dist/commands/config.js +11 -28
- package/dist/commands/dev.js +1 -1
- package/dist/commands/env.js +2 -3
- package/dist/commands/init.js +1 -2
- package/dist/commands/manifest.js +1 -3
- package/dist/commands/subscribe.js +16 -16
- package/dist/credential_io.js +1 -2
- package/dist/dev/env.js +1 -2
- package/dist/index.js +1 -1
- package/dist/init/run.js +1 -2
- package/dist/plugins/run.js +2 -1
- package/dist/psql/command/cmd_io.js +1 -2
- package/dist/psql/command/cmd_pipeline.js +1 -2
- package/dist/psql/core/common.js +1 -2
- package/dist/psql/core/startup.js +1 -2
- package/dist/psql/index.js +1 -2
- package/dist/psql/types/index.js +1 -2
- package/dist/skills/catalog.js +5 -0
- package/dist/utils/hooks.js +0 -2
- package/dist/utils/staging.js +1 -14
- package/{LICENSE → dist/vendor/postgres-meta/LICENSE} +0 -1
- package/dist/vendor/postgres-meta/constants.js +14 -0
- package/dist/vendor/postgres-meta/lib/PostgresMeta.js +49 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaColumnPrivileges.js +85 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaColumns.js +235 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaConfig.js +16 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaExtensions.js +79 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaForeignTables.js +81 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaFunctions.js +173 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaIndexes.js +42 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaMaterializedViews.js +84 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaPolicies.js +120 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaPublications.js +193 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaRelationships.js +87 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaRoles.js +203 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaSchemas.js +105 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTablePrivileges.js +91 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTables.js +183 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTriggers.js +156 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaTypes.js +22 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaVersion.js +20 -0
- package/dist/vendor/postgres-meta/lib/PostgresMetaViews.js +83 -0
- package/dist/vendor/postgres-meta/lib/constants.js +8 -0
- package/dist/vendor/postgres-meta/lib/db.js +147 -0
- package/dist/vendor/postgres-meta/lib/generators.js +106 -0
- package/dist/vendor/postgres-meta/lib/helpers.js +26 -0
- package/dist/vendor/postgres-meta/lib/index.js +1 -0
- package/dist/vendor/postgres-meta/lib/sql/column_privileges.sql.js +154 -0
- package/dist/vendor/postgres-meta/lib/sql/columns.sql.js +123 -0
- package/dist/vendor/postgres-meta/lib/sql/common.js +1 -0
- package/dist/vendor/postgres-meta/lib/sql/config.sql.js +32 -0
- package/dist/vendor/postgres-meta/lib/sql/extensions.sql.js +20 -0
- package/dist/vendor/postgres-meta/lib/sql/foreign_tables.sql.js +20 -0
- package/dist/vendor/postgres-meta/lib/sql/functions.sql.js +141 -0
- package/dist/vendor/postgres-meta/lib/sql/indexes.sql.js +51 -0
- package/dist/vendor/postgres-meta/lib/sql/materialized_views.sql.js +21 -0
- package/dist/vendor/postgres-meta/lib/sql/policies.sql.js +53 -0
- package/dist/vendor/postgres-meta/lib/sql/publications.sql.js +46 -0
- package/dist/vendor/postgres-meta/lib/sql/roles.sql.js +40 -0
- package/dist/vendor/postgres-meta/lib/sql/schemas.sql.js +26 -0
- package/dist/vendor/postgres-meta/lib/sql/table.sql.js +109 -0
- package/dist/vendor/postgres-meta/lib/sql/table_privileges.sql.js +85 -0
- package/dist/vendor/postgres-meta/lib/sql/table_relationships.sql.js +52 -0
- package/dist/vendor/postgres-meta/lib/sql/triggers.sql.js +64 -0
- package/dist/vendor/postgres-meta/lib/sql/types.sql.js +65 -0
- package/dist/vendor/postgres-meta/lib/sql/version.sql.js +15 -0
- package/dist/vendor/postgres-meta/lib/sql/views.sql.js +22 -0
- package/dist/vendor/postgres-meta/lib/sql/views_key_dependencies.sql.js +198 -0
- package/dist/vendor/postgres-meta/lib/types.js +1 -0
- package/dist/vendor/postgres-meta/templates/typescript.js +608 -0
- package/package.json +13 -6
|
@@ -0,0 +1,608 @@
|
|
|
1
|
+
import { GENERATE_TYPES_DEFAULT_SCHEMA, VALID_FUNCTION_ARGS_MODE, VALID_UNNAMED_FUNCTION_ARG_TYPES } from "../constants.js";
|
|
2
|
+
import prettier from "prettier";
|
|
3
|
+
//#region src/vendor/postgres-meta/templates/typescript.ts
|
|
4
|
+
const apply = async ({ schemas, tables, foreignTables, views, materializedViews, columns, relationships, functions, types, detectOneToOneRelationships, postgrestVersion }) => {
|
|
5
|
+
schemas.sort((a, b) => a.name.localeCompare(b.name));
|
|
6
|
+
relationships.sort((a, b) => a.foreign_key_name.localeCompare(b.foreign_key_name) || a.referenced_relation.localeCompare(b.referenced_relation) || JSON.stringify(a.referenced_columns).localeCompare(JSON.stringify(b.referenced_columns)));
|
|
7
|
+
const introspectionBySchema = Object.fromEntries(schemas.map((s) => [s.name, {
|
|
8
|
+
tables: [],
|
|
9
|
+
views: [],
|
|
10
|
+
functions: [],
|
|
11
|
+
enums: [],
|
|
12
|
+
compositeTypes: []
|
|
13
|
+
}]));
|
|
14
|
+
const columnsByTableId = {};
|
|
15
|
+
const tablesNamesByTableId = {};
|
|
16
|
+
const relationTypeByIds = /* @__PURE__ */ new Map();
|
|
17
|
+
const typesById = /* @__PURE__ */ new Map();
|
|
18
|
+
const tablesLike = [
|
|
19
|
+
...tables,
|
|
20
|
+
...foreignTables,
|
|
21
|
+
...views,
|
|
22
|
+
...materializedViews
|
|
23
|
+
];
|
|
24
|
+
for (const tableLike of tablesLike) {
|
|
25
|
+
columnsByTableId[tableLike.id] = [];
|
|
26
|
+
tablesNamesByTableId[tableLike.id] = tableLike.name;
|
|
27
|
+
}
|
|
28
|
+
for (const column of columns) if (column.table_id in columnsByTableId) columnsByTableId[column.table_id].push(column);
|
|
29
|
+
for (const tableId in columnsByTableId) columnsByTableId[tableId].sort((a, b) => a.name.localeCompare(b.name));
|
|
30
|
+
for (const type of types) {
|
|
31
|
+
typesById.set(type.id, type);
|
|
32
|
+
if (type.type_relation_id) relationTypeByIds.set(type.id, type);
|
|
33
|
+
if (type.schema in introspectionBySchema) {
|
|
34
|
+
if (type.enums.length > 0) introspectionBySchema[type.schema].enums.push(type);
|
|
35
|
+
if (type.attributes.length > 0) introspectionBySchema[type.schema].compositeTypes.push(type);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function getRelationships(object, relationships) {
|
|
39
|
+
return relationships.filter((relationship) => relationship.schema === object.schema && relationship.referenced_schema === object.schema && relationship.relation === object.name);
|
|
40
|
+
}
|
|
41
|
+
function generateRelationshiptTsDefinition(relationship) {
|
|
42
|
+
return `{
|
|
43
|
+
foreignKeyName: ${JSON.stringify(relationship.foreign_key_name)}
|
|
44
|
+
columns: ${JSON.stringify(relationship.columns)}${detectOneToOneRelationships ? `\nisOneToOne: ${relationship.is_one_to_one}` : ""}
|
|
45
|
+
referencedRelation: ${JSON.stringify(relationship.referenced_relation)}
|
|
46
|
+
referencedColumns: ${JSON.stringify(relationship.referenced_columns)}
|
|
47
|
+
}`;
|
|
48
|
+
}
|
|
49
|
+
for (const table of tables) if (table.schema in introspectionBySchema) introspectionBySchema[table.schema].tables.push({
|
|
50
|
+
table,
|
|
51
|
+
relationships: getRelationships(table, relationships)
|
|
52
|
+
});
|
|
53
|
+
for (const table of foreignTables) if (table.schema in introspectionBySchema) introspectionBySchema[table.schema].tables.push({
|
|
54
|
+
table,
|
|
55
|
+
relationships: getRelationships(table, relationships)
|
|
56
|
+
});
|
|
57
|
+
for (const view of views) if (view.schema in introspectionBySchema) introspectionBySchema[view.schema].views.push({
|
|
58
|
+
view,
|
|
59
|
+
relationships: getRelationships(view, relationships)
|
|
60
|
+
});
|
|
61
|
+
for (const materializedView of materializedViews) if (materializedView.schema in introspectionBySchema) introspectionBySchema[materializedView.schema].views.push({
|
|
62
|
+
view: {
|
|
63
|
+
...materializedView,
|
|
64
|
+
is_updatable: false
|
|
65
|
+
},
|
|
66
|
+
relationships: getRelationships(materializedView, relationships)
|
|
67
|
+
});
|
|
68
|
+
const getTableNameFromRelationId = (relationId, returnTypeId) => {
|
|
69
|
+
if (!relationId) return null;
|
|
70
|
+
if (tablesNamesByTableId[relationId]) return tablesNamesByTableId[relationId];
|
|
71
|
+
const reltype = returnTypeId ? relationTypeByIds.get(returnTypeId) : null;
|
|
72
|
+
return reltype ? reltype.name : null;
|
|
73
|
+
};
|
|
74
|
+
for (const func of functions) if (func.schema in introspectionBySchema) {
|
|
75
|
+
func.args.sort((a, b) => a.name.localeCompare(b.name));
|
|
76
|
+
const inArgs = func.args.filter(({ mode }) => VALID_FUNCTION_ARGS_MODE.has(mode));
|
|
77
|
+
if (inArgs.length === 0 || !inArgs.some(({ name }) => name === "") || inArgs.every((arg) => {
|
|
78
|
+
if (arg.name === "") return arg.has_default && VALID_UNNAMED_FUNCTION_ARG_TYPES.has(arg.type_id);
|
|
79
|
+
return true;
|
|
80
|
+
}) || inArgs.length === 1 && inArgs[0].name === "" && (VALID_UNNAMED_FUNCTION_ARG_TYPES.has(inArgs[0].type_id) || relationTypeByIds.get(inArgs[0].type_id) && getTableNameFromRelationId(func.return_type_relation_id, func.return_type_id) || relationTypeByIds.get(inArgs[0].type_id) && !getTableNameFromRelationId(func.return_type_relation_id, func.return_type_id))) introspectionBySchema[func.schema].functions.push({
|
|
81
|
+
fn: func,
|
|
82
|
+
inArgs
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
for (const schema in introspectionBySchema) {
|
|
86
|
+
introspectionBySchema[schema].tables.sort((a, b) => a.table.name.localeCompare(b.table.name));
|
|
87
|
+
introspectionBySchema[schema].views.sort((a, b) => a.view.name.localeCompare(b.view.name));
|
|
88
|
+
introspectionBySchema[schema].functions.sort((a, b) => a.fn.name.localeCompare(b.fn.name));
|
|
89
|
+
introspectionBySchema[schema].enums.sort((a, b) => a.name.localeCompare(b.name));
|
|
90
|
+
introspectionBySchema[schema].compositeTypes.sort((a, b) => a.name.localeCompare(b.name));
|
|
91
|
+
}
|
|
92
|
+
const getFunctionTsReturnType = (fn, returnType) => {
|
|
93
|
+
let setofOptionsInfo = "";
|
|
94
|
+
const returnTableName = getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id);
|
|
95
|
+
const returnsSetOfTable = fn.is_set_returning_function && fn.return_type_relation_id !== null;
|
|
96
|
+
const returnsMultipleRows = fn.prorows !== null && fn.prorows > 1;
|
|
97
|
+
if (returnTableName) setofOptionsInfo = `SetofOptions: {
|
|
98
|
+
from: "*"
|
|
99
|
+
to: ${JSON.stringify(returnTableName)}
|
|
100
|
+
isOneToOne: ${Boolean(!returnsMultipleRows)}
|
|
101
|
+
isSetofReturn: ${fn.is_set_returning_function}
|
|
102
|
+
}`;
|
|
103
|
+
if (fn.args.length === 1) {
|
|
104
|
+
const relationType = relationTypeByIds.get(fn.args[0].type_id);
|
|
105
|
+
if (relationType) {
|
|
106
|
+
const sourceTable = relationType.format;
|
|
107
|
+
if (returnsSetOfTable && returnTableName) setofOptionsInfo = `SetofOptions: {
|
|
108
|
+
from: ${JSON.stringify(sourceTable)}
|
|
109
|
+
to: ${JSON.stringify(returnTableName)}
|
|
110
|
+
isOneToOne: ${Boolean(!returnsMultipleRows)}
|
|
111
|
+
isSetofReturn: true
|
|
112
|
+
}`;
|
|
113
|
+
else if (returnTableName && !returnsSetOfTable) {
|
|
114
|
+
const targetTable = returnTableName;
|
|
115
|
+
setofOptionsInfo = `SetofOptions: {
|
|
116
|
+
from: ${JSON.stringify(sourceTable)}
|
|
117
|
+
to: ${JSON.stringify(targetTable)}
|
|
118
|
+
isOneToOne: true
|
|
119
|
+
isSetofReturn: false
|
|
120
|
+
}`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return `${returnType}${fn.is_set_returning_function && returnsMultipleRows ? "[]" : ""}
|
|
125
|
+
${setofOptionsInfo ? `${setofOptionsInfo}` : ""}`;
|
|
126
|
+
};
|
|
127
|
+
const getFunctionReturnType = (schema, fn) => {
|
|
128
|
+
const tableArgs = fn.args.filter(({ mode }) => mode === "table");
|
|
129
|
+
if (tableArgs.length > 0) return `{
|
|
130
|
+
${tableArgs.map(({ name, type_id }) => {
|
|
131
|
+
const type = typesById.get(type_id);
|
|
132
|
+
let tsType = "unknown";
|
|
133
|
+
if (type) tsType = pgTypeToTsType(schema, type.name, {
|
|
134
|
+
types,
|
|
135
|
+
schemas,
|
|
136
|
+
tables,
|
|
137
|
+
views
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
name,
|
|
141
|
+
type: tsType
|
|
142
|
+
};
|
|
143
|
+
}).map(({ name, type }) => `${JSON.stringify(name)}: ${type}`)}
|
|
144
|
+
}`;
|
|
145
|
+
const relation = introspectionBySchema[schema.name]?.tables.find(({ table: { id } }) => id === fn.return_type_relation_id)?.table || introspectionBySchema[schema.name]?.views.find(({ view: { id } }) => id === fn.return_type_relation_id)?.view;
|
|
146
|
+
if (relation) return `{
|
|
147
|
+
${columnsByTableId[relation.id].map((column) => generateColumnTsDefinition(schema, {
|
|
148
|
+
name: column.name,
|
|
149
|
+
format: column.format,
|
|
150
|
+
is_nullable: column.is_nullable,
|
|
151
|
+
is_optional: false
|
|
152
|
+
}, {
|
|
153
|
+
types,
|
|
154
|
+
schemas,
|
|
155
|
+
tables,
|
|
156
|
+
views
|
|
157
|
+
})).join(",\n")}
|
|
158
|
+
}`;
|
|
159
|
+
const type = typesById.get(fn.return_type_id);
|
|
160
|
+
if (type) return pgTypeToTsType(schema, type.name, {
|
|
161
|
+
types,
|
|
162
|
+
schemas,
|
|
163
|
+
tables,
|
|
164
|
+
views
|
|
165
|
+
});
|
|
166
|
+
return "unknown";
|
|
167
|
+
};
|
|
168
|
+
const hasTableRowError = (fn, inArgs) => {
|
|
169
|
+
if (inArgs.length === 1 && inArgs[0].name === "" && relationTypeByIds.get(inArgs[0].type_id) && !getTableNameFromRelationId(fn.return_type_relation_id, fn.return_type_id)) return true;
|
|
170
|
+
return false;
|
|
171
|
+
};
|
|
172
|
+
const getConflictError = (schema, fns, fn, inArgs) => {
|
|
173
|
+
if (fns.length <= 1) return null;
|
|
174
|
+
if (inArgs.length === 0) {
|
|
175
|
+
const conflictingFns = fns.filter(({ fn: otherFn, inArgs: otherInArgs }) => {
|
|
176
|
+
if (otherFn === fn) return false;
|
|
177
|
+
return otherInArgs.length === 1 && otherInArgs[0].name === "" && otherInArgs[0].has_default;
|
|
178
|
+
});
|
|
179
|
+
if (conflictingFns.length > 0) {
|
|
180
|
+
const conflictingFn = conflictingFns[0];
|
|
181
|
+
const returnTypeName = typesById.get(conflictingFn.fn.return_type_id)?.name || "unknown";
|
|
182
|
+
return `Could not choose the best candidate function between: ${schema.name}.${fn.name}(), ${schema.name}.${fn.name}( => ${returnTypeName}). Try renaming the parameters or the function itself in the database so function overloading can be resolved`;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (inArgs.length === 1 && inArgs[0].name !== "") {
|
|
186
|
+
const conflictingFns = fns.filter(({ fn: otherFn, inArgs: otherInArgs }) => {
|
|
187
|
+
if (otherFn === fn) return false;
|
|
188
|
+
return otherInArgs.length === 1 && otherInArgs[0].name === inArgs[0].name && otherInArgs[0].type_id !== inArgs[0].type_id;
|
|
189
|
+
});
|
|
190
|
+
if (conflictingFns.length > 0) return `Could not choose the best candidate function between: ${[{
|
|
191
|
+
fn,
|
|
192
|
+
inArgs
|
|
193
|
+
}, ...conflictingFns].sort((a, b) => {
|
|
194
|
+
const aArgs = a.inArgs;
|
|
195
|
+
const bArgs = b.inArgs;
|
|
196
|
+
return (aArgs[0]?.type_id || 0) - (bArgs[0]?.type_id || 0);
|
|
197
|
+
}).map((f) => {
|
|
198
|
+
const args = f.inArgs;
|
|
199
|
+
return `${schema.name}.${fn.name}(${args.map((a) => `${a.name || ""} => ${typesById.get(a.type_id)?.name || "unknown"}`).join(", ")})`;
|
|
200
|
+
}).join(", ")}. Try renaming the parameters or the function itself in the database so function overloading can be resolved`;
|
|
201
|
+
}
|
|
202
|
+
return null;
|
|
203
|
+
};
|
|
204
|
+
const getFunctionSignatures = (schema, fns) => {
|
|
205
|
+
return fns.map(({ fn, inArgs }) => {
|
|
206
|
+
let argsType = "never";
|
|
207
|
+
let returnType = getFunctionReturnType(schema, fn);
|
|
208
|
+
const conflictError = getConflictError(schema, fns, fn, inArgs);
|
|
209
|
+
if (conflictError) {
|
|
210
|
+
if (inArgs.length > 0) argsType = `{ ${inArgs.map(({ name, type_id, has_default }) => {
|
|
211
|
+
const type = typesById.get(type_id);
|
|
212
|
+
let tsType = "unknown";
|
|
213
|
+
if (type) tsType = pgTypeToTsType(schema, type.name, {
|
|
214
|
+
types,
|
|
215
|
+
schemas,
|
|
216
|
+
tables,
|
|
217
|
+
views
|
|
218
|
+
});
|
|
219
|
+
return {
|
|
220
|
+
name,
|
|
221
|
+
type: tsType,
|
|
222
|
+
has_default
|
|
223
|
+
};
|
|
224
|
+
}).map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? "?" : ""}: ${type}`)} }`;
|
|
225
|
+
returnType = `{ error: true } & ${JSON.stringify(conflictError)}`;
|
|
226
|
+
} else if (hasTableRowError(fn, inArgs)) {
|
|
227
|
+
if (inArgs.length > 0) argsType = `{ ${inArgs.map(({ name, type_id, has_default }) => {
|
|
228
|
+
const type = typesById.get(type_id);
|
|
229
|
+
let tsType = "unknown";
|
|
230
|
+
if (type) tsType = pgTypeToTsType(schema, type.name, {
|
|
231
|
+
types,
|
|
232
|
+
schemas,
|
|
233
|
+
tables,
|
|
234
|
+
views
|
|
235
|
+
});
|
|
236
|
+
return {
|
|
237
|
+
name,
|
|
238
|
+
type: tsType,
|
|
239
|
+
has_default
|
|
240
|
+
};
|
|
241
|
+
}).map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? "?" : ""}: ${type}`)} }`;
|
|
242
|
+
returnType = `{ error: true } & ${JSON.stringify(`the function ${schema.name}.${fn.name} with parameter or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache`)}`;
|
|
243
|
+
} else if (inArgs.length > 0) argsType = `{ ${inArgs.map(({ name, type_id, has_default }) => {
|
|
244
|
+
const type = typesById.get(type_id);
|
|
245
|
+
let tsType = "unknown";
|
|
246
|
+
if (type) tsType = pgTypeToTsType(schema, type.name, {
|
|
247
|
+
types,
|
|
248
|
+
schemas,
|
|
249
|
+
tables,
|
|
250
|
+
views
|
|
251
|
+
});
|
|
252
|
+
return {
|
|
253
|
+
name,
|
|
254
|
+
type: tsType,
|
|
255
|
+
has_default
|
|
256
|
+
};
|
|
257
|
+
}).map(({ name, type, has_default }) => `${JSON.stringify(name)}${has_default ? "?" : ""}: ${type}`)} }`;
|
|
258
|
+
return `{ Args: ${argsType}; Returns: ${getFunctionTsReturnType(fn, returnType)} }`;
|
|
259
|
+
}).join(" |\n");
|
|
260
|
+
};
|
|
261
|
+
const internal_supabase_schema = postgrestVersion ? `// Allows to automatically instantiate createClient with right options
|
|
262
|
+
// instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
|
|
263
|
+
__InternalSupabase: {
|
|
264
|
+
PostgrestVersion: '${postgrestVersion}'
|
|
265
|
+
}` : "";
|
|
266
|
+
function generateNullableUnionTsType(tsType, isNullable) {
|
|
267
|
+
if (tsType === "unknown" || tsType === "any" || !isNullable) return tsType;
|
|
268
|
+
return `${tsType} | null`;
|
|
269
|
+
}
|
|
270
|
+
function generateColumnTsDefinition(schema, column, context) {
|
|
271
|
+
return `${JSON.stringify(column.name)}${column.is_optional ? "?" : ""}: ${generateNullableUnionTsType(pgTypeToTsType(schema, column.format, context), column.is_nullable)}`;
|
|
272
|
+
}
|
|
273
|
+
let output = `
|
|
274
|
+
export type Json = string | number | boolean | null | { [key: string]: Json | undefined } | Json[]
|
|
275
|
+
|
|
276
|
+
export type Database = {
|
|
277
|
+
${internal_supabase_schema}
|
|
278
|
+
${schemas.map((schema) => {
|
|
279
|
+
const { tables: schemaTables, views: schemaViews, functions: schemaFunctions, enums: schemaEnums, compositeTypes: schemaCompositeTypes } = introspectionBySchema[schema.name];
|
|
280
|
+
return `${JSON.stringify(schema.name)}: {
|
|
281
|
+
Tables: {
|
|
282
|
+
${schemaTables.length === 0 ? "[_ in never]: never" : schemaTables.map(({ table, relationships }) => `${JSON.stringify(table.name)}: {
|
|
283
|
+
Row: {
|
|
284
|
+
${[...columnsByTableId[table.id].map((column) => generateColumnTsDefinition(schema, {
|
|
285
|
+
name: column.name,
|
|
286
|
+
format: column.format,
|
|
287
|
+
is_nullable: column.is_nullable,
|
|
288
|
+
is_optional: false
|
|
289
|
+
}, {
|
|
290
|
+
types,
|
|
291
|
+
schemas,
|
|
292
|
+
tables,
|
|
293
|
+
views
|
|
294
|
+
})), ...schemaFunctions.filter(({ fn }) => fn.argument_types === table.name).map(({ fn }) => {
|
|
295
|
+
return `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}`;
|
|
296
|
+
})]}
|
|
297
|
+
}
|
|
298
|
+
Insert: {
|
|
299
|
+
${columnsByTableId[table.id].map((column) => {
|
|
300
|
+
if (column.identity_generation === "ALWAYS") return `${JSON.stringify(column.name)}?: never`;
|
|
301
|
+
return generateColumnTsDefinition(schema, {
|
|
302
|
+
name: column.name,
|
|
303
|
+
format: column.format,
|
|
304
|
+
is_nullable: column.is_nullable,
|
|
305
|
+
is_optional: column.is_nullable || column.is_identity || column.default_value !== null
|
|
306
|
+
}, {
|
|
307
|
+
types,
|
|
308
|
+
schemas,
|
|
309
|
+
tables,
|
|
310
|
+
views
|
|
311
|
+
});
|
|
312
|
+
})}
|
|
313
|
+
}
|
|
314
|
+
Update: {
|
|
315
|
+
${columnsByTableId[table.id].map((column) => {
|
|
316
|
+
if (column.identity_generation === "ALWAYS") return `${JSON.stringify(column.name)}?: never`;
|
|
317
|
+
return generateColumnTsDefinition(schema, {
|
|
318
|
+
name: column.name,
|
|
319
|
+
format: column.format,
|
|
320
|
+
is_nullable: column.is_nullable,
|
|
321
|
+
is_optional: true
|
|
322
|
+
}, {
|
|
323
|
+
types,
|
|
324
|
+
schemas,
|
|
325
|
+
tables,
|
|
326
|
+
views
|
|
327
|
+
});
|
|
328
|
+
})}
|
|
329
|
+
}
|
|
330
|
+
Relationships: [
|
|
331
|
+
${relationships.map(generateRelationshiptTsDefinition)}
|
|
332
|
+
]
|
|
333
|
+
}`)}
|
|
334
|
+
}
|
|
335
|
+
Views: {
|
|
336
|
+
${schemaViews.length === 0 ? "[_ in never]: never" : schemaViews.map(({ view, relationships }) => `${JSON.stringify(view.name)}: {
|
|
337
|
+
Row: {
|
|
338
|
+
${[...columnsByTableId[view.id].map((column) => generateColumnTsDefinition(schema, {
|
|
339
|
+
name: column.name,
|
|
340
|
+
format: column.format,
|
|
341
|
+
is_nullable: column.is_nullable,
|
|
342
|
+
is_optional: false
|
|
343
|
+
}, {
|
|
344
|
+
types,
|
|
345
|
+
schemas,
|
|
346
|
+
tables,
|
|
347
|
+
views
|
|
348
|
+
})), ...schemaFunctions.filter(({ fn }) => fn.argument_types === view.name).map(({ fn }) => `${JSON.stringify(fn.name)}: ${generateNullableUnionTsType(getFunctionReturnType(schema, fn), true)}`)]}
|
|
349
|
+
}
|
|
350
|
+
${view.is_updatable ? `Insert: {
|
|
351
|
+
${columnsByTableId[view.id].map((column) => {
|
|
352
|
+
if (!column.is_updatable) return `${JSON.stringify(column.name)}?: never`;
|
|
353
|
+
return generateColumnTsDefinition(schema, {
|
|
354
|
+
name: column.name,
|
|
355
|
+
format: column.format,
|
|
356
|
+
is_nullable: true,
|
|
357
|
+
is_optional: true
|
|
358
|
+
}, {
|
|
359
|
+
types,
|
|
360
|
+
schemas,
|
|
361
|
+
tables,
|
|
362
|
+
views
|
|
363
|
+
});
|
|
364
|
+
})}
|
|
365
|
+
}
|
|
366
|
+
Update: {
|
|
367
|
+
${columnsByTableId[view.id].map((column) => {
|
|
368
|
+
if (!column.is_updatable) return `${JSON.stringify(column.name)}?: never`;
|
|
369
|
+
return generateColumnTsDefinition(schema, {
|
|
370
|
+
name: column.name,
|
|
371
|
+
format: column.format,
|
|
372
|
+
is_nullable: true,
|
|
373
|
+
is_optional: true
|
|
374
|
+
}, {
|
|
375
|
+
types,
|
|
376
|
+
schemas,
|
|
377
|
+
tables,
|
|
378
|
+
views
|
|
379
|
+
});
|
|
380
|
+
})}
|
|
381
|
+
}
|
|
382
|
+
` : ""}Relationships: [
|
|
383
|
+
${relationships.map(generateRelationshiptTsDefinition)}
|
|
384
|
+
]
|
|
385
|
+
}`)}
|
|
386
|
+
}
|
|
387
|
+
Functions: {
|
|
388
|
+
${(() => {
|
|
389
|
+
if (schemaFunctions.length === 0) return "[_ in never]: never";
|
|
390
|
+
const schemaFunctionsGroupedByName = schemaFunctions.reduce((acc, curr) => {
|
|
391
|
+
acc[curr.fn.name] ??= [];
|
|
392
|
+
acc[curr.fn.name].push(curr);
|
|
393
|
+
return acc;
|
|
394
|
+
}, {});
|
|
395
|
+
for (const fnName in schemaFunctionsGroupedByName) schemaFunctionsGroupedByName[fnName].sort((a, b) => b.fn.definition.localeCompare(a.fn.definition));
|
|
396
|
+
return Object.entries(schemaFunctionsGroupedByName).map(([fnName, fns]) => {
|
|
397
|
+
const functionSignatures = getFunctionSignatures(schema, fns);
|
|
398
|
+
return `${JSON.stringify(fnName)}:\n${functionSignatures}`;
|
|
399
|
+
}).join(",\n");
|
|
400
|
+
})()}
|
|
401
|
+
}
|
|
402
|
+
Enums: {
|
|
403
|
+
${schemaEnums.length === 0 ? "[_ in never]: never" : schemaEnums.map((enum_) => `${JSON.stringify(enum_.name)}: ${enum_.enums.map((variant) => JSON.stringify(variant)).join("|")}`)}
|
|
404
|
+
}
|
|
405
|
+
CompositeTypes: {
|
|
406
|
+
${schemaCompositeTypes.length === 0 ? "[_ in never]: never" : schemaCompositeTypes.map(({ name, attributes }) => `${JSON.stringify(name)}: {
|
|
407
|
+
${attributes.map(({ name, type_id }) => {
|
|
408
|
+
const type = typesById.get(type_id);
|
|
409
|
+
let tsType = "unknown";
|
|
410
|
+
if (type) tsType = `${generateNullableUnionTsType(pgTypeToTsType(schema, type.name, {
|
|
411
|
+
types,
|
|
412
|
+
schemas,
|
|
413
|
+
tables,
|
|
414
|
+
views
|
|
415
|
+
}), true)}`;
|
|
416
|
+
return `${JSON.stringify(name)}: ${tsType}`;
|
|
417
|
+
})}
|
|
418
|
+
}`)}
|
|
419
|
+
}
|
|
420
|
+
}`;
|
|
421
|
+
})}
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
type DatabaseWithoutInternals = Omit<Database, '__InternalSupabase'>
|
|
425
|
+
|
|
426
|
+
type DefaultSchema = DatabaseWithoutInternals[Extract<keyof Database, ${JSON.stringify(GENERATE_TYPES_DEFAULT_SCHEMA)}>]
|
|
427
|
+
|
|
428
|
+
export type Tables<
|
|
429
|
+
DefaultSchemaTableNameOrOptions extends
|
|
430
|
+
| keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
|
|
431
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
432
|
+
TableName extends DefaultSchemaTableNameOrOptions extends {
|
|
433
|
+
schema: keyof DatabaseWithoutInternals
|
|
434
|
+
}
|
|
435
|
+
? keyof (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
|
436
|
+
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])
|
|
437
|
+
: never = never
|
|
438
|
+
> = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
439
|
+
? (DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"] &
|
|
440
|
+
DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Views"])[TableName] extends {
|
|
441
|
+
Row: infer R
|
|
442
|
+
}
|
|
443
|
+
? R
|
|
444
|
+
: never
|
|
445
|
+
: DefaultSchemaTableNameOrOptions extends keyof (DefaultSchema["Tables"] & DefaultSchema["Views"])
|
|
446
|
+
? (DefaultSchema["Tables"] & DefaultSchema["Views"])[DefaultSchemaTableNameOrOptions] extends {
|
|
447
|
+
Row: infer R
|
|
448
|
+
}
|
|
449
|
+
? R
|
|
450
|
+
: never
|
|
451
|
+
: never
|
|
452
|
+
|
|
453
|
+
export type TablesInsert<
|
|
454
|
+
DefaultSchemaTableNameOrOptions extends
|
|
455
|
+
| keyof DefaultSchema["Tables"]
|
|
456
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
457
|
+
TableName extends DefaultSchemaTableNameOrOptions extends {
|
|
458
|
+
schema: keyof DatabaseWithoutInternals
|
|
459
|
+
}
|
|
460
|
+
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
|
461
|
+
: never = never
|
|
462
|
+
> = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
463
|
+
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
|
464
|
+
Insert: infer I
|
|
465
|
+
}
|
|
466
|
+
? I
|
|
467
|
+
: never
|
|
468
|
+
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
|
469
|
+
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
|
470
|
+
Insert: infer I
|
|
471
|
+
}
|
|
472
|
+
? I
|
|
473
|
+
: never
|
|
474
|
+
: never
|
|
475
|
+
|
|
476
|
+
export type TablesUpdate<
|
|
477
|
+
DefaultSchemaTableNameOrOptions extends
|
|
478
|
+
| keyof DefaultSchema["Tables"]
|
|
479
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
480
|
+
TableName extends DefaultSchemaTableNameOrOptions extends {
|
|
481
|
+
schema: keyof DatabaseWithoutInternals
|
|
482
|
+
}
|
|
483
|
+
? keyof DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"]
|
|
484
|
+
: never = never
|
|
485
|
+
> = DefaultSchemaTableNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
486
|
+
? DatabaseWithoutInternals[DefaultSchemaTableNameOrOptions["schema"]]["Tables"][TableName] extends {
|
|
487
|
+
Update: infer U
|
|
488
|
+
}
|
|
489
|
+
? U
|
|
490
|
+
: never
|
|
491
|
+
: DefaultSchemaTableNameOrOptions extends keyof DefaultSchema["Tables"]
|
|
492
|
+
? DefaultSchema["Tables"][DefaultSchemaTableNameOrOptions] extends {
|
|
493
|
+
Update: infer U
|
|
494
|
+
}
|
|
495
|
+
? U
|
|
496
|
+
: never
|
|
497
|
+
: never
|
|
498
|
+
|
|
499
|
+
export type Enums<
|
|
500
|
+
DefaultSchemaEnumNameOrOptions extends
|
|
501
|
+
| keyof DefaultSchema["Enums"]
|
|
502
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
503
|
+
EnumName extends DefaultSchemaEnumNameOrOptions extends {
|
|
504
|
+
schema: keyof DatabaseWithoutInternals
|
|
505
|
+
}
|
|
506
|
+
? keyof DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"]
|
|
507
|
+
: never = never
|
|
508
|
+
> = DefaultSchemaEnumNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
509
|
+
? DatabaseWithoutInternals[DefaultSchemaEnumNameOrOptions["schema"]]["Enums"][EnumName]
|
|
510
|
+
: DefaultSchemaEnumNameOrOptions extends keyof DefaultSchema["Enums"]
|
|
511
|
+
? DefaultSchema["Enums"][DefaultSchemaEnumNameOrOptions]
|
|
512
|
+
: never
|
|
513
|
+
|
|
514
|
+
export type CompositeTypes<
|
|
515
|
+
PublicCompositeTypeNameOrOptions extends
|
|
516
|
+
| keyof DefaultSchema["CompositeTypes"]
|
|
517
|
+
| { schema: keyof DatabaseWithoutInternals },
|
|
518
|
+
CompositeTypeName extends PublicCompositeTypeNameOrOptions extends {
|
|
519
|
+
schema: keyof DatabaseWithoutInternals
|
|
520
|
+
}
|
|
521
|
+
? keyof DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"]
|
|
522
|
+
: never = never
|
|
523
|
+
> = PublicCompositeTypeNameOrOptions extends { schema: keyof DatabaseWithoutInternals }
|
|
524
|
+
? DatabaseWithoutInternals[PublicCompositeTypeNameOrOptions["schema"]]["CompositeTypes"][CompositeTypeName]
|
|
525
|
+
: PublicCompositeTypeNameOrOptions extends keyof DefaultSchema["CompositeTypes"]
|
|
526
|
+
? DefaultSchema["CompositeTypes"][PublicCompositeTypeNameOrOptions]
|
|
527
|
+
: never
|
|
528
|
+
|
|
529
|
+
export const Constants = {
|
|
530
|
+
${schemas.map((schema) => {
|
|
531
|
+
const schemaEnums = introspectionBySchema[schema.name].enums;
|
|
532
|
+
return `${JSON.stringify(schema.name)}: {
|
|
533
|
+
Enums: {
|
|
534
|
+
${schemaEnums.map((enum_) => `${JSON.stringify(enum_.name)}: [${enum_.enums.map((variant) => JSON.stringify(variant)).join(", ")}]`)}
|
|
535
|
+
}
|
|
536
|
+
}`;
|
|
537
|
+
})}
|
|
538
|
+
} as const
|
|
539
|
+
`;
|
|
540
|
+
output = await prettier.format(output, {
|
|
541
|
+
parser: "typescript",
|
|
542
|
+
semi: false
|
|
543
|
+
});
|
|
544
|
+
return output;
|
|
545
|
+
};
|
|
546
|
+
const pgTypeToTsType = (schema, pgType, { types, schemas, tables, views }) => {
|
|
547
|
+
if (pgType === "bool") return "boolean";
|
|
548
|
+
else if ([
|
|
549
|
+
"int2",
|
|
550
|
+
"int4",
|
|
551
|
+
"int8",
|
|
552
|
+
"float4",
|
|
553
|
+
"float8",
|
|
554
|
+
"numeric"
|
|
555
|
+
].includes(pgType)) return "number";
|
|
556
|
+
else if ([
|
|
557
|
+
"bytea",
|
|
558
|
+
"bpchar",
|
|
559
|
+
"varchar",
|
|
560
|
+
"date",
|
|
561
|
+
"text",
|
|
562
|
+
"citext",
|
|
563
|
+
"time",
|
|
564
|
+
"timetz",
|
|
565
|
+
"timestamp",
|
|
566
|
+
"timestamptz",
|
|
567
|
+
"uuid",
|
|
568
|
+
"vector"
|
|
569
|
+
].includes(pgType)) return "string";
|
|
570
|
+
else if (["json", "jsonb"].includes(pgType)) return "Json";
|
|
571
|
+
else if (pgType === "void") return "undefined";
|
|
572
|
+
else if (pgType === "record") return "Record<string, unknown>";
|
|
573
|
+
else if (pgType.startsWith("_")) return `(${pgTypeToTsType(schema, pgType.substring(1), {
|
|
574
|
+
types,
|
|
575
|
+
schemas,
|
|
576
|
+
tables,
|
|
577
|
+
views
|
|
578
|
+
})})[]`;
|
|
579
|
+
else {
|
|
580
|
+
const enumTypes = types.filter((type) => type.name === pgType && type.enums.length > 0);
|
|
581
|
+
if (enumTypes.length > 0) {
|
|
582
|
+
const enumType = enumTypes.find((type) => type.schema === schema.name) || enumTypes[0];
|
|
583
|
+
if (schemas.some(({ name }) => name === enumType.schema)) return `Database[${JSON.stringify(enumType.schema)}]['Enums'][${JSON.stringify(enumType.name)}]`;
|
|
584
|
+
return enumType.enums.map((variant) => JSON.stringify(variant)).join("|");
|
|
585
|
+
}
|
|
586
|
+
const compositeTypes = types.filter((type) => type.name === pgType && type.attributes.length > 0);
|
|
587
|
+
if (compositeTypes.length > 0) {
|
|
588
|
+
const compositeType = compositeTypes.find((type) => type.schema === schema.name) || compositeTypes[0];
|
|
589
|
+
if (schemas.some(({ name }) => name === compositeType.schema)) return `Database[${JSON.stringify(compositeType.schema)}]['CompositeTypes'][${JSON.stringify(compositeType.name)}]`;
|
|
590
|
+
return "unknown";
|
|
591
|
+
}
|
|
592
|
+
const tableRowTypes = tables.filter((table) => table.name === pgType);
|
|
593
|
+
if (tableRowTypes.length > 0) {
|
|
594
|
+
const tableRowType = tableRowTypes.find((type) => type.schema === schema.name) || tableRowTypes[0];
|
|
595
|
+
if (schemas.some(({ name }) => name === tableRowType.schema)) return `Database[${JSON.stringify(tableRowType.schema)}]['Tables'][${JSON.stringify(tableRowType.name)}]['Row']`;
|
|
596
|
+
return "unknown";
|
|
597
|
+
}
|
|
598
|
+
const viewRowTypes = views.filter((view) => view.name === pgType);
|
|
599
|
+
if (viewRowTypes.length > 0) {
|
|
600
|
+
const viewRowType = viewRowTypes.find((type) => type.schema === schema.name) || viewRowTypes[0];
|
|
601
|
+
if (schemas.some(({ name }) => name === viewRowType.schema)) return `Database[${JSON.stringify(viewRowType.schema)}]['Views'][${JSON.stringify(viewRowType.name)}]['Row']`;
|
|
602
|
+
return "unknown";
|
|
603
|
+
}
|
|
604
|
+
return "unknown";
|
|
605
|
+
}
|
|
606
|
+
};
|
|
607
|
+
//#endregion
|
|
608
|
+
export { apply };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "neon",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.5.0",
|
|
4
4
|
"description": "CLI tool for Neon, the cloud backend primitives built around Lakebase Postgres",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"neon",
|
|
@@ -55,9 +55,13 @@
|
|
|
55
55
|
"fflate": "^0.8.3",
|
|
56
56
|
"open": "^10.2.0",
|
|
57
57
|
"openid-client": "6.8.1",
|
|
58
|
+
"pg": "^8.20.0",
|
|
58
59
|
"pg-connection-string": "^2.14.1",
|
|
60
|
+
"pg-format": "1.0.4",
|
|
59
61
|
"pg-protocol": "^1.14.0",
|
|
60
62
|
"picocolors": "^1.1.1",
|
|
63
|
+
"postgres-array": "^3.0.4",
|
|
64
|
+
"prettier": "3.9.8",
|
|
61
65
|
"prompts": "2.4.2",
|
|
62
66
|
"semver": "7.7.2",
|
|
63
67
|
"undici": "^7.28.0",
|
|
@@ -66,10 +70,10 @@
|
|
|
66
70
|
"yaml": "^2.9.0",
|
|
67
71
|
"yargs": "17.7.2",
|
|
68
72
|
"yoctocolors": "^2.1.2",
|
|
69
|
-
"@neon/
|
|
70
|
-
"@neon/config-runtime": "1.9.1",
|
|
73
|
+
"@neon/sdk": "7.2.0",
|
|
71
74
|
"@neon/realtime": "0.1.0",
|
|
72
|
-
"@neon/
|
|
75
|
+
"@neon/config-runtime": "1.10.0",
|
|
76
|
+
"@neon/config": "1.9.1"
|
|
73
77
|
},
|
|
74
78
|
"optionalDependencies": {
|
|
75
79
|
"@napi-rs/keyring": "1.3.0",
|
|
@@ -85,6 +89,8 @@
|
|
|
85
89
|
"@types/diff": "5.2.1",
|
|
86
90
|
"@types/express": "4.17.21",
|
|
87
91
|
"@types/node": "^20.19.0",
|
|
92
|
+
"@types/pg": "8.20.0",
|
|
93
|
+
"@types/pg-format": "1.0.5",
|
|
88
94
|
"@types/prompts": "2.4.9",
|
|
89
95
|
"@types/semver": "7.7.1",
|
|
90
96
|
"@types/which": "3.0.4",
|
|
@@ -103,8 +109,8 @@
|
|
|
103
109
|
"tsx": "4.22.3",
|
|
104
110
|
"typescript": "^5.9.0",
|
|
105
111
|
"vitest": "^3.0.9",
|
|
106
|
-
"@neon-internals/env-core": "0.0.32",
|
|
107
112
|
"@neon-internals/cli-core": "0.0.0",
|
|
113
|
+
"@neon-internals/env-core": "0.0.32",
|
|
108
114
|
"@neon/env": "1.6.2",
|
|
109
115
|
"@neon/e2e-harness": "0.0.0",
|
|
110
116
|
"@neon/functions": "0.11.0"
|
|
@@ -115,7 +121,8 @@
|
|
|
115
121
|
},
|
|
116
122
|
"pkg": {
|
|
117
123
|
"assets": [
|
|
118
|
-
"callback.html"
|
|
124
|
+
"callback.html",
|
|
125
|
+
"vendor/postgres-meta/LICENSE"
|
|
119
126
|
],
|
|
120
127
|
"scripts": [
|
|
121
128
|
"bundle/*.js"
|