greenseed 0.1.5 → 0.1.6
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/dist/cli.cjs +125 -30
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +125 -30
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.cjs
CHANGED
|
@@ -35,6 +35,12 @@ var path2__default = /*#__PURE__*/_interopDefault(path2);
|
|
|
35
35
|
var postgres__default = /*#__PURE__*/_interopDefault(postgres);
|
|
36
36
|
var fs__default = /*#__PURE__*/_interopDefault(fs);
|
|
37
37
|
|
|
38
|
+
var C = {
|
|
39
|
+
reset: "\x1B[0m",
|
|
40
|
+
dim: "\x1B[2m",
|
|
41
|
+
red: "\x1B[31m"
|
|
42
|
+
};
|
|
43
|
+
var logError = (msg) => console.error(`${C.red}\u2717${C.reset} ${C.dim}${msg}${C.reset}`);
|
|
38
44
|
function connect(url) {
|
|
39
45
|
return postgres__default.default(url, { max: 10, idle_timeout: 20 });
|
|
40
46
|
}
|
|
@@ -42,7 +48,8 @@ async function check(sql) {
|
|
|
42
48
|
try {
|
|
43
49
|
await sql`select 1`;
|
|
44
50
|
} catch (err) {
|
|
45
|
-
|
|
51
|
+
logError("Database connection failed");
|
|
52
|
+
throw new Error("Failed to connect to database");
|
|
46
53
|
}
|
|
47
54
|
}
|
|
48
55
|
async function tableExists(sql, schema, table) {
|
|
@@ -50,7 +57,8 @@ async function tableExists(sql, schema, table) {
|
|
|
50
57
|
select to_regclass(${'"' + schema + '"."' + table + '"'}) is not null as exists
|
|
51
58
|
`;
|
|
52
59
|
if (!res[0]) {
|
|
53
|
-
|
|
60
|
+
logError(`Failed to check table: ${schema}.${table}`);
|
|
61
|
+
throw new Error(`Table check failed: ${schema}.${table}`);
|
|
54
62
|
}
|
|
55
63
|
return res[0].exists;
|
|
56
64
|
}
|
|
@@ -58,7 +66,8 @@ async function validateTables(sql, tables) {
|
|
|
58
66
|
for (const t of tables) {
|
|
59
67
|
const ok = await tableExists(sql, t.schema, t.table);
|
|
60
68
|
if (!ok) {
|
|
61
|
-
|
|
69
|
+
logError(`Table does not exist: ${t.schema}.${t.table}`);
|
|
70
|
+
throw new Error(`Missing table: ${t.schema}.${t.table}`);
|
|
62
71
|
}
|
|
63
72
|
}
|
|
64
73
|
}
|
|
@@ -72,18 +81,42 @@ async function metadata(sql, schema, table) {
|
|
|
72
81
|
`;
|
|
73
82
|
return { columns: cols.map((c) => c.column_name) };
|
|
74
83
|
}
|
|
75
|
-
async function insert(sql, schema, table, rows, columns, primaryKeys) {
|
|
84
|
+
async function insert(sql, schema, table, rows, columns, primaryKeys, updateOnConflict) {
|
|
76
85
|
if (primaryKeys.length === 0) {
|
|
77
|
-
|
|
86
|
+
logError(`No primary keys defined for ${schema}.${table}`);
|
|
87
|
+
throw new Error(`No primary keys for ${schema}.${table}`);
|
|
88
|
+
}
|
|
89
|
+
const updateCols = updateOnConflict.filter(
|
|
90
|
+
(col) => !primaryKeys.includes(col)
|
|
91
|
+
);
|
|
92
|
+
let q;
|
|
93
|
+
if (updateCols.length > 0) {
|
|
94
|
+
const updates = Object.fromEntries(
|
|
95
|
+
updateCols.map((col) => [col, sql`excluded.${sql(col)}`])
|
|
96
|
+
);
|
|
97
|
+
q = await sql`
|
|
98
|
+
insert into ${sql(schema + "." + table)}
|
|
99
|
+
${sql(rows, columns)}
|
|
100
|
+
on conflict (${sql(primaryKeys)}) do update set
|
|
101
|
+
${sql(updates)}
|
|
102
|
+
returning ${sql(primaryKeys[0] ?? [])}
|
|
103
|
+
`;
|
|
104
|
+
} else {
|
|
105
|
+
q = await sql`
|
|
106
|
+
insert into ${sql(schema + "." + table)}
|
|
107
|
+
${sql(rows, columns)}
|
|
108
|
+
on conflict (${sql(primaryKeys)}) do nothing
|
|
109
|
+
returning ${sql(primaryKeys[0] ?? [])}
|
|
110
|
+
`;
|
|
78
111
|
}
|
|
79
|
-
const q = await sql`
|
|
80
|
-
insert into ${sql(schema + "." + table)}
|
|
81
|
-
${sql(rows, columns)}
|
|
82
|
-
on conflict (${sql(primaryKeys)}) do nothing
|
|
83
|
-
returning ${sql(primaryKeys[0] ?? [])}
|
|
84
|
-
`;
|
|
85
112
|
return q.length;
|
|
86
113
|
}
|
|
114
|
+
var C2 = {
|
|
115
|
+
reset: "\x1B[0m",
|
|
116
|
+
dim: "\x1B[2m",
|
|
117
|
+
red: "\x1B[31m"
|
|
118
|
+
};
|
|
119
|
+
var logError2 = (msg) => console.error(`${C2.red}\u2717${C2.reset} ${C2.dim}${msg}${C2.reset}`);
|
|
87
120
|
function exists(p) {
|
|
88
121
|
return fs__default.default.existsSync(p);
|
|
89
122
|
}
|
|
@@ -91,7 +124,8 @@ function readFile(p) {
|
|
|
91
124
|
try {
|
|
92
125
|
return fs__default.default.readFileSync(p, "utf8");
|
|
93
126
|
} catch (err) {
|
|
94
|
-
|
|
127
|
+
logError2(`Failed to read file: ${p}`);
|
|
128
|
+
throw new Error(`File read error: ${p}`);
|
|
95
129
|
}
|
|
96
130
|
}
|
|
97
131
|
function resolvePath(p) {
|
|
@@ -102,17 +136,20 @@ function loadJsonArray(p, onMissing) {
|
|
|
102
136
|
if (onMissing === "skip") {
|
|
103
137
|
return [];
|
|
104
138
|
}
|
|
105
|
-
|
|
139
|
+
logError2(`Source file not found: ${p}`);
|
|
140
|
+
throw new Error(`Missing file: ${p}`);
|
|
106
141
|
}
|
|
107
142
|
const raw = readFile(p);
|
|
108
143
|
let parsed;
|
|
109
144
|
try {
|
|
110
145
|
parsed = JSON.parse(raw);
|
|
111
146
|
} catch {
|
|
112
|
-
|
|
147
|
+
logError2(`Invalid JSON in file: ${p}`);
|
|
148
|
+
throw new Error(`JSON parse error: ${p}`);
|
|
113
149
|
}
|
|
114
150
|
if (!Array.isArray(parsed)) {
|
|
115
|
-
|
|
151
|
+
logError2(`Expected array but got ${typeof parsed}: ${p}`);
|
|
152
|
+
throw new Error(`Invalid data format: ${p}`);
|
|
116
153
|
}
|
|
117
154
|
return parsed;
|
|
118
155
|
}
|
|
@@ -125,11 +162,33 @@ function chunk(data, size) {
|
|
|
125
162
|
}
|
|
126
163
|
|
|
127
164
|
// src/app.ts
|
|
128
|
-
var
|
|
165
|
+
var C3 = {
|
|
129
166
|
reset: "\x1B[0m",
|
|
167
|
+
bold: "\x1B[1m",
|
|
168
|
+
dim: "\x1B[2m",
|
|
130
169
|
green: "\x1B[32m",
|
|
131
170
|
yellow: "\x1B[33m",
|
|
132
|
-
|
|
171
|
+
blue: "\x1B[34m",
|
|
172
|
+
cyan: "\x1B[36m",
|
|
173
|
+
red: "\x1B[31m"};
|
|
174
|
+
var log = {
|
|
175
|
+
info: (msg) => console.log(`${C3.blue}\u2139${C3.reset} ${msg}`),
|
|
176
|
+
success: (msg) => console.log(`${C3.green}\u2713${C3.reset} ${msg}`),
|
|
177
|
+
warn: (msg) => console.log(`${C3.yellow}\u26A0${C3.reset} ${msg}`),
|
|
178
|
+
error: (msg) => console.log(`${C3.red}\u2717${C3.reset} ${msg}`),
|
|
179
|
+
dim: (msg) => console.log(`${C3.dim}${msg}${C3.reset}`),
|
|
180
|
+
header: (msg) => console.log(`
|
|
181
|
+
${C3.bold}${C3.cyan}${msg}${C3.reset}
|
|
182
|
+
`),
|
|
183
|
+
table: (label, value) => console.log(` ${C3.dim}${label}:${C3.reset} ${C3.bold}${value}${C3.reset}`),
|
|
184
|
+
progress: (current, total, label) => {
|
|
185
|
+
const percentage = Math.round(current / total * 100);
|
|
186
|
+
const bar = "\u2588".repeat(Math.floor(percentage / 5));
|
|
187
|
+
const empty = "\u2591".repeat(20 - Math.floor(percentage / 5));
|
|
188
|
+
console.log(
|
|
189
|
+
` ${C3.cyan}${bar}${empty}${C3.reset} ${C3.bold}${percentage}%${C3.reset} ${C3.dim}(${current}/${total})${C3.reset} ${label}`
|
|
190
|
+
);
|
|
191
|
+
}
|
|
133
192
|
};
|
|
134
193
|
var ConfigSchema = v__namespace.object({
|
|
135
194
|
seedFileExtensions: v__namespace.array(v__namespace.picklist([".json"])),
|
|
@@ -140,13 +199,15 @@ var ConfigSchema = v__namespace.object({
|
|
|
140
199
|
table: v__namespace.string(),
|
|
141
200
|
schema: v__namespace.optional(v__namespace.string(), "public"),
|
|
142
201
|
primaryKeys: v__namespace.array(v__namespace.string()),
|
|
143
|
-
source: v__namespace.string()
|
|
202
|
+
source: v__namespace.string(),
|
|
203
|
+
updateOnConflict: v__namespace.optional(v__namespace.nullable(v__namespace.array(v__namespace.string())), null)
|
|
144
204
|
})
|
|
145
205
|
)
|
|
146
206
|
});
|
|
147
207
|
async function run(opts) {
|
|
148
208
|
dotenv__default.default.config();
|
|
149
209
|
const configPath = resolvePath(opts.config || "seed.config.json");
|
|
210
|
+
log.header("\u{1F331} GreenSeed");
|
|
150
211
|
let raw;
|
|
151
212
|
try {
|
|
152
213
|
raw = JSON.parse(readFile(configPath));
|
|
@@ -165,6 +226,8 @@ async function run(opts) {
|
|
|
165
226
|
if (!dbUrl) {
|
|
166
227
|
throw new Error("Missing DB env var: " + dbVar);
|
|
167
228
|
}
|
|
229
|
+
log.info(`Using config: ${C3.dim}${configPath}${C3.reset}`);
|
|
230
|
+
log.info(`Database: ${C3.dim}${dbVar}${C3.reset}`);
|
|
168
231
|
const sql = connect(dbUrl);
|
|
169
232
|
await check(sql);
|
|
170
233
|
await validateTables(
|
|
@@ -174,18 +237,20 @@ async function run(opts) {
|
|
|
174
237
|
schema: t.schema
|
|
175
238
|
}))
|
|
176
239
|
);
|
|
177
|
-
|
|
240
|
+
log.success("Database connection established");
|
|
241
|
+
log.header("Seeding Tables");
|
|
178
242
|
for (const t of config.tables) {
|
|
179
243
|
const source = path2__default.default.resolve(path2__default.default.dirname(configPath), t.source);
|
|
180
244
|
const rows = loadJsonArray(source, config.onMissingFile || "error");
|
|
181
245
|
if (rows.length === 0) {
|
|
182
|
-
|
|
246
|
+
log.warn(`Skipped ${C3.bold}${t.schema}.${t.table}${C3.reset} (no data)`);
|
|
183
247
|
continue;
|
|
184
248
|
}
|
|
185
249
|
const meta = await metadata(sql, t.schema, t.table);
|
|
186
250
|
const validCols = Object.keys(rows[0]).filter(
|
|
187
251
|
(c) => meta.columns.includes(c)
|
|
188
252
|
);
|
|
253
|
+
log.info(`Seeding ${C3.bold}${t.schema}.${t.table}${C3.reset}`);
|
|
189
254
|
let inserted = 0;
|
|
190
255
|
for (const part of chunk(rows, 1e3)) {
|
|
191
256
|
inserted += await insert(
|
|
@@ -194,16 +259,35 @@ async function run(opts) {
|
|
|
194
259
|
t.table,
|
|
195
260
|
part,
|
|
196
261
|
validCols,
|
|
197
|
-
t.primaryKeys
|
|
262
|
+
t.primaryKeys,
|
|
263
|
+
t.updateOnConflict || []
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
const hasConflict = t.updateOnConflict && t.updateOnConflict.length > 0;
|
|
267
|
+
const action = hasConflict ? "upserted" : "inserted";
|
|
268
|
+
const skipped = rows.length - inserted;
|
|
269
|
+
if (inserted === rows.length) {
|
|
270
|
+
log.success(
|
|
271
|
+
`${C3.bold}${t.schema}.${t.table}${C3.reset} ${C3.green}${action} ${inserted} rows${C3.reset}`
|
|
272
|
+
);
|
|
273
|
+
} else {
|
|
274
|
+
log.success(
|
|
275
|
+
`${C3.bold}${t.schema}.${t.table}${C3.reset} ${C3.green}${action} ${inserted}${C3.reset}${C3.dim}, skipped ${skipped}${C3.reset}`
|
|
198
276
|
);
|
|
199
277
|
}
|
|
200
|
-
console.log(
|
|
201
|
-
C.green + t.schema + "." + t.table + ": " + inserted + "/" + rows.length + C.reset
|
|
202
|
-
);
|
|
203
278
|
}
|
|
204
279
|
await sql.end();
|
|
205
|
-
|
|
280
|
+
log.header("Summary");
|
|
281
|
+
log.success(
|
|
282
|
+
`Seeded ${C3.bold}${config.tables.length}${C3.reset} table${config.tables.length === 1 ? "" : "s"}`
|
|
283
|
+
);
|
|
206
284
|
}
|
|
285
|
+
var C4 = {
|
|
286
|
+
reset: "\x1B[0m",
|
|
287
|
+
dim: "\x1B[2m",
|
|
288
|
+
red: "\x1B[31m"
|
|
289
|
+
};
|
|
290
|
+
var logError3 = (msg) => console.error(`${C4.red}\u2717${C4.reset} ${C4.dim}${msg}${C4.reset}`);
|
|
207
291
|
var DEFAULT_CONFIG = {
|
|
208
292
|
seedFileExtensions: [".json"],
|
|
209
293
|
databaseUrlEnvVar: "DATABASE_URL",
|
|
@@ -213,14 +297,16 @@ var DEFAULT_CONFIG = {
|
|
|
213
297
|
table: "example_table",
|
|
214
298
|
schema: "public",
|
|
215
299
|
primaryKeys: ["id"],
|
|
216
|
-
source: "./data/example_table.json"
|
|
300
|
+
source: "./data/example_table.json",
|
|
301
|
+
updateOnConflict: []
|
|
217
302
|
}
|
|
218
303
|
]
|
|
219
304
|
};
|
|
220
305
|
function init(configPath) {
|
|
221
306
|
const resolved = path2__default.default.resolve(process.cwd(), configPath);
|
|
222
307
|
if (fs__default.default.existsSync(resolved)) {
|
|
223
|
-
|
|
308
|
+
logError3(`Config file already exists: ${resolved}`);
|
|
309
|
+
throw new Error(`Config already exists: ${resolved}`);
|
|
224
310
|
}
|
|
225
311
|
fs__default.default.writeFileSync(
|
|
226
312
|
resolved,
|
|
@@ -230,21 +316,30 @@ function init(configPath) {
|
|
|
230
316
|
}
|
|
231
317
|
|
|
232
318
|
// src/cli.ts
|
|
319
|
+
var C5 = {
|
|
320
|
+
reset: "\x1B[0m",
|
|
321
|
+
bold: "\x1B[1m",
|
|
322
|
+
green: "\x1B[32m",
|
|
323
|
+
red: "\x1B[31m"
|
|
324
|
+
};
|
|
325
|
+
var logError4 = (err) => console.error(`${C5.red}${C5.bold}Error:${C5.reset} ${String(err)}`);
|
|
233
326
|
var prog = sade__default.default("greenseed");
|
|
234
327
|
prog.command("push").option("-c, --config <file>", "Config file", "seed.config.json").option("-d, --dbvar <name>", "DB env var override").action(async (opts) => {
|
|
235
328
|
try {
|
|
236
329
|
await run(opts);
|
|
237
330
|
} catch (err) {
|
|
238
|
-
|
|
331
|
+
logError4(err);
|
|
239
332
|
process.exit(1);
|
|
240
333
|
}
|
|
241
334
|
});
|
|
242
335
|
prog.command("init").option("-c, --config <file>", "Config file", "seed.config.json").action((opts) => {
|
|
243
336
|
try {
|
|
244
337
|
init(opts.config);
|
|
245
|
-
console.log(
|
|
338
|
+
console.log(
|
|
339
|
+
`${C5.green}\u2713${C5.reset} Config created: ${C5.bold}${opts.config}${C5.reset}`
|
|
340
|
+
);
|
|
246
341
|
} catch (err) {
|
|
247
|
-
|
|
342
|
+
logError4(err);
|
|
248
343
|
process.exit(1);
|
|
249
344
|
}
|
|
250
345
|
});
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/init.ts","../src/cli.ts"],"names":["postgres","fs","path","v","dotenv","sade"],"mappings":"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","file":"cli.cjs","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${\"\\\"\" + schema + \"\\\".\\\"\" + table + \"\\\"\"}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst DEFAULT_CONFIG = {\n\tseedFileExtensions: [\".json\"],\n\tdatabaseUrlEnvVar: \"DATABASE_URL\",\n\tonMissingFile: \"error\",\n\ttables: [\n\t\t{\n\t\t\ttable: \"example_table\",\n\t\t\tschema: \"public\",\n\t\t\tprimaryKeys: [\"id\"],\n\t\t\tsource: \"./data/example_table.json\",\n\t\t},\n\t],\n}\n\nexport function init(configPath: string): void {\n\tconst resolved = path.resolve(process.cwd(), configPath)\n\n\tif (fs.existsSync(resolved)) {\n\t\tthrow new Error(\"Config already exists: \" + resolved)\n\t}\n\n\tfs.writeFileSync(\n\t\tresolved,\n\t\tJSON.stringify(DEFAULT_CONFIG, null, 2) + \"\\n\",\n\t\t\"utf8\",\n\t)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\nimport { init } from \"./init\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog\n\t.command(\"init\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.action((opts) => {\n\t\ttry {\n\t\t\tinit(opts.config)\n\t\t\tconsole.log(\"\\x1b[32mConfig created: \" + opts.config + \"\\x1b[0m\")\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/init.ts","../src/cli.ts"],"names":["postgres","C","logError","fs","path","v","dotenv","sade"],"mappings":"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","file":"cli.cjs","sourcesContent":["import postgres from \"postgres\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tlogError(\"Database connection failed\")\n\t\tthrow new Error(\"Failed to connect to database\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${'\"' + schema + '\".\"' + table + '\"'}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tlogError(`Failed to check table: ${schema}.${table}`)\n\t\tthrow new Error(`Table check failed: ${schema}.${table}`)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tlogError(`Table does not exist: ${t.schema}.${t.table}`)\n\t\t\tthrow new Error(`Missing table: ${t.schema}.${t.table}`)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n\tupdateOnConflict: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tlogError(`No primary keys defined for ${schema}.${table}`)\n\t\tthrow new Error(`No primary keys for ${schema}.${table}`)\n\t}\n\n\tconst updateCols = updateOnConflict.filter(\n\t\t(col) => !primaryKeys.includes(col),\n\t)\n\n\tlet q\n\tif (updateCols.length > 0) {\n\t\tconst updates = Object.fromEntries(\n\t\t\tupdateCols.map((col) => [col, sql`excluded.${sql(col)}`]),\n\t\t)\n\n\t\tq = await sql`\n\t\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t\t${sql(rows, columns)}\n\t\t\ton conflict (${sql(primaryKeys)}) do update set\n\t\t\t${sql(updates)}\n\t\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t\t`\n\t} else {\n\t\tq = await sql`\n\t\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t\t${sql(rows, columns)}\n\t\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t\t`\n\t}\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tlogError(`Failed to read file: ${p}`)\n\t\tthrow new Error(`File read error: ${p}`)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tlogError(`Source file not found: ${p}`)\n\t\tthrow new Error(`Missing file: ${p}`)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tlogError(`Invalid JSON in file: ${p}`)\n\t\tthrow new Error(`JSON parse error: ${p}`)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tlogError(`Expected array but got ${typeof parsed}: ${p}`)\n\t\tthrow new Error(`Invalid data format: ${p}`)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tbold: \"\\x1b[1m\",\n\tdim: \"\\x1b[2m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tblue: \"\\x1b[34m\",\n\tcyan: \"\\x1b[36m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst log = {\n\tinfo: (msg: string) => console.log(`${C.blue}ℹ${C.reset} ${msg}`),\n\tsuccess: (msg: string) => console.log(`${C.green}✓${C.reset} ${msg}`),\n\twarn: (msg: string) => console.log(`${C.yellow}⚠${C.reset} ${msg}`),\n\terror: (msg: string) => console.log(`${C.red}✗${C.reset} ${msg}`),\n\tdim: (msg: string) => console.log(`${C.dim}${msg}${C.reset}`),\n\theader: (msg: string) =>\n\t\tconsole.log(`\\n${C.bold}${C.cyan}${msg}${C.reset}\\n`),\n\ttable: (label: string, value: string | number) =>\n\t\tconsole.log(` ${C.dim}${label}:${C.reset} ${C.bold}${value}${C.reset}`),\n\tprogress: (current: number, total: number, label: string) => {\n\t\tconst percentage = Math.round((current / total) * 100)\n\t\tconst bar = \"█\".repeat(Math.floor(percentage / 5))\n\t\tconst empty = \"░\".repeat(20 - Math.floor(percentage / 5))\n\t\tconsole.log(\n\t\t\t` ${C.cyan}${bar}${empty}${C.reset} ${C.bold}${percentage}%${C.reset} ${C.dim}(${current}/${total})${C.reset} ${label}`,\n\t\t)\n\t},\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t\tupdateOnConflict: v.optional(v.nullable(v.array(v.string())), null),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlog.header(\"🌱 GreenSeed\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tlog.info(`Using config: ${C.dim}${configPath}${C.reset}`)\n\tlog.info(`Database: ${C.dim}${dbVar}${C.reset}`)\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tlog.success(\"Database connection established\")\n\tlog.header(\"Seeding Tables\")\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tlog.warn(`Skipped ${C.bold}${t.schema}.${t.table}${C.reset} (no data)`)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlog.info(`Seeding ${C.bold}${t.schema}.${t.table}${C.reset}`)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t\tt.updateOnConflict || [],\n\t\t\t)\n\t\t}\n\n\t\tconst hasConflict = t.updateOnConflict && t.updateOnConflict.length > 0\n\t\tconst action = hasConflict ? \"upserted\" : \"inserted\"\n\t\tconst skipped = rows.length - inserted\n\n\t\tif (inserted === rows.length) {\n\t\t\tlog.success(\n\t\t\t\t`${C.bold}${t.schema}.${t.table}${C.reset} ${C.green}${action} ${inserted} rows${C.reset}`,\n\t\t\t)\n\t\t} else {\n\t\t\tlog.success(\n\t\t\t\t`${C.bold}${t.schema}.${t.table}${C.reset} ${C.green}${action} ${inserted}${C.reset}${C.dim}, skipped ${skipped}${C.reset}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tawait sql.end()\n\n\tlog.header(\"Summary\")\n\tlog.success(\n\t\t`Seeded ${C.bold}${config.tables.length}${C.reset} table${config.tables.length === 1 ? \"\" : \"s\"}`,\n\t)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\n\nconst DEFAULT_CONFIG = {\n\tseedFileExtensions: [\".json\"],\n\tdatabaseUrlEnvVar: \"DATABASE_URL\",\n\tonMissingFile: \"error\",\n\ttables: [\n\t\t{\n\t\t\ttable: \"example_table\",\n\t\t\tschema: \"public\",\n\t\t\tprimaryKeys: [\"id\"],\n\t\t\tsource: \"./data/example_table.json\",\n\t\t\tupdateOnConflict: [],\n\t\t},\n\t],\n}\n\nexport function init(configPath: string): void {\n\tconst resolved = path.resolve(process.cwd(), configPath)\n\n\tif (fs.existsSync(resolved)) {\n\t\tlogError(`Config file already exists: ${resolved}`)\n\t\tthrow new Error(`Config already exists: ${resolved}`)\n\t}\n\n\tfs.writeFileSync(\n\t\tresolved,\n\t\tJSON.stringify(DEFAULT_CONFIG, null, 2) + \"\\n\",\n\t\t\"utf8\",\n\t)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\nimport { init } from \"./init\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tbold: \"\\x1b[1m\",\n\tgreen: \"\\x1b[32m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (err: unknown) =>\n\tconsole.error(`${C.red}${C.bold}Error:${C.reset} ${String(err)}`)\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tlogError(err)\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog\n\t.command(\"init\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.action((opts) => {\n\t\ttry {\n\t\t\tinit(opts.config)\n\t\t\tconsole.log(\n\t\t\t\t`${C.green}✓${C.reset} Config created: ${C.bold}${opts.config}${C.reset}`,\n\t\t\t)\n\t\t} catch (err) {\n\t\t\tlogError(err)\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
|
package/dist/cli.js
CHANGED
|
@@ -6,6 +6,12 @@ import path2 from 'path';
|
|
|
6
6
|
import postgres from 'postgres';
|
|
7
7
|
import fs from 'fs';
|
|
8
8
|
|
|
9
|
+
var C = {
|
|
10
|
+
reset: "\x1B[0m",
|
|
11
|
+
dim: "\x1B[2m",
|
|
12
|
+
red: "\x1B[31m"
|
|
13
|
+
};
|
|
14
|
+
var logError = (msg) => console.error(`${C.red}\u2717${C.reset} ${C.dim}${msg}${C.reset}`);
|
|
9
15
|
function connect(url) {
|
|
10
16
|
return postgres(url, { max: 10, idle_timeout: 20 });
|
|
11
17
|
}
|
|
@@ -13,7 +19,8 @@ async function check(sql) {
|
|
|
13
19
|
try {
|
|
14
20
|
await sql`select 1`;
|
|
15
21
|
} catch (err) {
|
|
16
|
-
|
|
22
|
+
logError("Database connection failed");
|
|
23
|
+
throw new Error("Failed to connect to database");
|
|
17
24
|
}
|
|
18
25
|
}
|
|
19
26
|
async function tableExists(sql, schema, table) {
|
|
@@ -21,7 +28,8 @@ async function tableExists(sql, schema, table) {
|
|
|
21
28
|
select to_regclass(${'"' + schema + '"."' + table + '"'}) is not null as exists
|
|
22
29
|
`;
|
|
23
30
|
if (!res[0]) {
|
|
24
|
-
|
|
31
|
+
logError(`Failed to check table: ${schema}.${table}`);
|
|
32
|
+
throw new Error(`Table check failed: ${schema}.${table}`);
|
|
25
33
|
}
|
|
26
34
|
return res[0].exists;
|
|
27
35
|
}
|
|
@@ -29,7 +37,8 @@ async function validateTables(sql, tables) {
|
|
|
29
37
|
for (const t of tables) {
|
|
30
38
|
const ok = await tableExists(sql, t.schema, t.table);
|
|
31
39
|
if (!ok) {
|
|
32
|
-
|
|
40
|
+
logError(`Table does not exist: ${t.schema}.${t.table}`);
|
|
41
|
+
throw new Error(`Missing table: ${t.schema}.${t.table}`);
|
|
33
42
|
}
|
|
34
43
|
}
|
|
35
44
|
}
|
|
@@ -43,18 +52,42 @@ async function metadata(sql, schema, table) {
|
|
|
43
52
|
`;
|
|
44
53
|
return { columns: cols.map((c) => c.column_name) };
|
|
45
54
|
}
|
|
46
|
-
async function insert(sql, schema, table, rows, columns, primaryKeys) {
|
|
55
|
+
async function insert(sql, schema, table, rows, columns, primaryKeys, updateOnConflict) {
|
|
47
56
|
if (primaryKeys.length === 0) {
|
|
48
|
-
|
|
57
|
+
logError(`No primary keys defined for ${schema}.${table}`);
|
|
58
|
+
throw new Error(`No primary keys for ${schema}.${table}`);
|
|
59
|
+
}
|
|
60
|
+
const updateCols = updateOnConflict.filter(
|
|
61
|
+
(col) => !primaryKeys.includes(col)
|
|
62
|
+
);
|
|
63
|
+
let q;
|
|
64
|
+
if (updateCols.length > 0) {
|
|
65
|
+
const updates = Object.fromEntries(
|
|
66
|
+
updateCols.map((col) => [col, sql`excluded.${sql(col)}`])
|
|
67
|
+
);
|
|
68
|
+
q = await sql`
|
|
69
|
+
insert into ${sql(schema + "." + table)}
|
|
70
|
+
${sql(rows, columns)}
|
|
71
|
+
on conflict (${sql(primaryKeys)}) do update set
|
|
72
|
+
${sql(updates)}
|
|
73
|
+
returning ${sql(primaryKeys[0] ?? [])}
|
|
74
|
+
`;
|
|
75
|
+
} else {
|
|
76
|
+
q = await sql`
|
|
77
|
+
insert into ${sql(schema + "." + table)}
|
|
78
|
+
${sql(rows, columns)}
|
|
79
|
+
on conflict (${sql(primaryKeys)}) do nothing
|
|
80
|
+
returning ${sql(primaryKeys[0] ?? [])}
|
|
81
|
+
`;
|
|
49
82
|
}
|
|
50
|
-
const q = await sql`
|
|
51
|
-
insert into ${sql(schema + "." + table)}
|
|
52
|
-
${sql(rows, columns)}
|
|
53
|
-
on conflict (${sql(primaryKeys)}) do nothing
|
|
54
|
-
returning ${sql(primaryKeys[0] ?? [])}
|
|
55
|
-
`;
|
|
56
83
|
return q.length;
|
|
57
84
|
}
|
|
85
|
+
var C2 = {
|
|
86
|
+
reset: "\x1B[0m",
|
|
87
|
+
dim: "\x1B[2m",
|
|
88
|
+
red: "\x1B[31m"
|
|
89
|
+
};
|
|
90
|
+
var logError2 = (msg) => console.error(`${C2.red}\u2717${C2.reset} ${C2.dim}${msg}${C2.reset}`);
|
|
58
91
|
function exists(p) {
|
|
59
92
|
return fs.existsSync(p);
|
|
60
93
|
}
|
|
@@ -62,7 +95,8 @@ function readFile(p) {
|
|
|
62
95
|
try {
|
|
63
96
|
return fs.readFileSync(p, "utf8");
|
|
64
97
|
} catch (err) {
|
|
65
|
-
|
|
98
|
+
logError2(`Failed to read file: ${p}`);
|
|
99
|
+
throw new Error(`File read error: ${p}`);
|
|
66
100
|
}
|
|
67
101
|
}
|
|
68
102
|
function resolvePath(p) {
|
|
@@ -73,17 +107,20 @@ function loadJsonArray(p, onMissing) {
|
|
|
73
107
|
if (onMissing === "skip") {
|
|
74
108
|
return [];
|
|
75
109
|
}
|
|
76
|
-
|
|
110
|
+
logError2(`Source file not found: ${p}`);
|
|
111
|
+
throw new Error(`Missing file: ${p}`);
|
|
77
112
|
}
|
|
78
113
|
const raw = readFile(p);
|
|
79
114
|
let parsed;
|
|
80
115
|
try {
|
|
81
116
|
parsed = JSON.parse(raw);
|
|
82
117
|
} catch {
|
|
83
|
-
|
|
118
|
+
logError2(`Invalid JSON in file: ${p}`);
|
|
119
|
+
throw new Error(`JSON parse error: ${p}`);
|
|
84
120
|
}
|
|
85
121
|
if (!Array.isArray(parsed)) {
|
|
86
|
-
|
|
122
|
+
logError2(`Expected array but got ${typeof parsed}: ${p}`);
|
|
123
|
+
throw new Error(`Invalid data format: ${p}`);
|
|
87
124
|
}
|
|
88
125
|
return parsed;
|
|
89
126
|
}
|
|
@@ -96,11 +133,33 @@ function chunk(data, size) {
|
|
|
96
133
|
}
|
|
97
134
|
|
|
98
135
|
// src/app.ts
|
|
99
|
-
var
|
|
136
|
+
var C3 = {
|
|
100
137
|
reset: "\x1B[0m",
|
|
138
|
+
bold: "\x1B[1m",
|
|
139
|
+
dim: "\x1B[2m",
|
|
101
140
|
green: "\x1B[32m",
|
|
102
141
|
yellow: "\x1B[33m",
|
|
103
|
-
|
|
142
|
+
blue: "\x1B[34m",
|
|
143
|
+
cyan: "\x1B[36m",
|
|
144
|
+
red: "\x1B[31m"};
|
|
145
|
+
var log = {
|
|
146
|
+
info: (msg) => console.log(`${C3.blue}\u2139${C3.reset} ${msg}`),
|
|
147
|
+
success: (msg) => console.log(`${C3.green}\u2713${C3.reset} ${msg}`),
|
|
148
|
+
warn: (msg) => console.log(`${C3.yellow}\u26A0${C3.reset} ${msg}`),
|
|
149
|
+
error: (msg) => console.log(`${C3.red}\u2717${C3.reset} ${msg}`),
|
|
150
|
+
dim: (msg) => console.log(`${C3.dim}${msg}${C3.reset}`),
|
|
151
|
+
header: (msg) => console.log(`
|
|
152
|
+
${C3.bold}${C3.cyan}${msg}${C3.reset}
|
|
153
|
+
`),
|
|
154
|
+
table: (label, value) => console.log(` ${C3.dim}${label}:${C3.reset} ${C3.bold}${value}${C3.reset}`),
|
|
155
|
+
progress: (current, total, label) => {
|
|
156
|
+
const percentage = Math.round(current / total * 100);
|
|
157
|
+
const bar = "\u2588".repeat(Math.floor(percentage / 5));
|
|
158
|
+
const empty = "\u2591".repeat(20 - Math.floor(percentage / 5));
|
|
159
|
+
console.log(
|
|
160
|
+
` ${C3.cyan}${bar}${empty}${C3.reset} ${C3.bold}${percentage}%${C3.reset} ${C3.dim}(${current}/${total})${C3.reset} ${label}`
|
|
161
|
+
);
|
|
162
|
+
}
|
|
104
163
|
};
|
|
105
164
|
var ConfigSchema = v.object({
|
|
106
165
|
seedFileExtensions: v.array(v.picklist([".json"])),
|
|
@@ -111,13 +170,15 @@ var ConfigSchema = v.object({
|
|
|
111
170
|
table: v.string(),
|
|
112
171
|
schema: v.optional(v.string(), "public"),
|
|
113
172
|
primaryKeys: v.array(v.string()),
|
|
114
|
-
source: v.string()
|
|
173
|
+
source: v.string(),
|
|
174
|
+
updateOnConflict: v.optional(v.nullable(v.array(v.string())), null)
|
|
115
175
|
})
|
|
116
176
|
)
|
|
117
177
|
});
|
|
118
178
|
async function run(opts) {
|
|
119
179
|
dotenv.config();
|
|
120
180
|
const configPath = resolvePath(opts.config || "seed.config.json");
|
|
181
|
+
log.header("\u{1F331} GreenSeed");
|
|
121
182
|
let raw;
|
|
122
183
|
try {
|
|
123
184
|
raw = JSON.parse(readFile(configPath));
|
|
@@ -136,6 +197,8 @@ async function run(opts) {
|
|
|
136
197
|
if (!dbUrl) {
|
|
137
198
|
throw new Error("Missing DB env var: " + dbVar);
|
|
138
199
|
}
|
|
200
|
+
log.info(`Using config: ${C3.dim}${configPath}${C3.reset}`);
|
|
201
|
+
log.info(`Database: ${C3.dim}${dbVar}${C3.reset}`);
|
|
139
202
|
const sql = connect(dbUrl);
|
|
140
203
|
await check(sql);
|
|
141
204
|
await validateTables(
|
|
@@ -145,18 +208,20 @@ async function run(opts) {
|
|
|
145
208
|
schema: t.schema
|
|
146
209
|
}))
|
|
147
210
|
);
|
|
148
|
-
|
|
211
|
+
log.success("Database connection established");
|
|
212
|
+
log.header("Seeding Tables");
|
|
149
213
|
for (const t of config.tables) {
|
|
150
214
|
const source = path2.resolve(path2.dirname(configPath), t.source);
|
|
151
215
|
const rows = loadJsonArray(source, config.onMissingFile || "error");
|
|
152
216
|
if (rows.length === 0) {
|
|
153
|
-
|
|
217
|
+
log.warn(`Skipped ${C3.bold}${t.schema}.${t.table}${C3.reset} (no data)`);
|
|
154
218
|
continue;
|
|
155
219
|
}
|
|
156
220
|
const meta = await metadata(sql, t.schema, t.table);
|
|
157
221
|
const validCols = Object.keys(rows[0]).filter(
|
|
158
222
|
(c) => meta.columns.includes(c)
|
|
159
223
|
);
|
|
224
|
+
log.info(`Seeding ${C3.bold}${t.schema}.${t.table}${C3.reset}`);
|
|
160
225
|
let inserted = 0;
|
|
161
226
|
for (const part of chunk(rows, 1e3)) {
|
|
162
227
|
inserted += await insert(
|
|
@@ -165,16 +230,35 @@ async function run(opts) {
|
|
|
165
230
|
t.table,
|
|
166
231
|
part,
|
|
167
232
|
validCols,
|
|
168
|
-
t.primaryKeys
|
|
233
|
+
t.primaryKeys,
|
|
234
|
+
t.updateOnConflict || []
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
const hasConflict = t.updateOnConflict && t.updateOnConflict.length > 0;
|
|
238
|
+
const action = hasConflict ? "upserted" : "inserted";
|
|
239
|
+
const skipped = rows.length - inserted;
|
|
240
|
+
if (inserted === rows.length) {
|
|
241
|
+
log.success(
|
|
242
|
+
`${C3.bold}${t.schema}.${t.table}${C3.reset} ${C3.green}${action} ${inserted} rows${C3.reset}`
|
|
243
|
+
);
|
|
244
|
+
} else {
|
|
245
|
+
log.success(
|
|
246
|
+
`${C3.bold}${t.schema}.${t.table}${C3.reset} ${C3.green}${action} ${inserted}${C3.reset}${C3.dim}, skipped ${skipped}${C3.reset}`
|
|
169
247
|
);
|
|
170
248
|
}
|
|
171
|
-
console.log(
|
|
172
|
-
C.green + t.schema + "." + t.table + ": " + inserted + "/" + rows.length + C.reset
|
|
173
|
-
);
|
|
174
249
|
}
|
|
175
250
|
await sql.end();
|
|
176
|
-
|
|
251
|
+
log.header("Summary");
|
|
252
|
+
log.success(
|
|
253
|
+
`Seeded ${C3.bold}${config.tables.length}${C3.reset} table${config.tables.length === 1 ? "" : "s"}`
|
|
254
|
+
);
|
|
177
255
|
}
|
|
256
|
+
var C4 = {
|
|
257
|
+
reset: "\x1B[0m",
|
|
258
|
+
dim: "\x1B[2m",
|
|
259
|
+
red: "\x1B[31m"
|
|
260
|
+
};
|
|
261
|
+
var logError3 = (msg) => console.error(`${C4.red}\u2717${C4.reset} ${C4.dim}${msg}${C4.reset}`);
|
|
178
262
|
var DEFAULT_CONFIG = {
|
|
179
263
|
seedFileExtensions: [".json"],
|
|
180
264
|
databaseUrlEnvVar: "DATABASE_URL",
|
|
@@ -184,14 +268,16 @@ var DEFAULT_CONFIG = {
|
|
|
184
268
|
table: "example_table",
|
|
185
269
|
schema: "public",
|
|
186
270
|
primaryKeys: ["id"],
|
|
187
|
-
source: "./data/example_table.json"
|
|
271
|
+
source: "./data/example_table.json",
|
|
272
|
+
updateOnConflict: []
|
|
188
273
|
}
|
|
189
274
|
]
|
|
190
275
|
};
|
|
191
276
|
function init(configPath) {
|
|
192
277
|
const resolved = path2.resolve(process.cwd(), configPath);
|
|
193
278
|
if (fs.existsSync(resolved)) {
|
|
194
|
-
|
|
279
|
+
logError3(`Config file already exists: ${resolved}`);
|
|
280
|
+
throw new Error(`Config already exists: ${resolved}`);
|
|
195
281
|
}
|
|
196
282
|
fs.writeFileSync(
|
|
197
283
|
resolved,
|
|
@@ -201,21 +287,30 @@ function init(configPath) {
|
|
|
201
287
|
}
|
|
202
288
|
|
|
203
289
|
// src/cli.ts
|
|
290
|
+
var C5 = {
|
|
291
|
+
reset: "\x1B[0m",
|
|
292
|
+
bold: "\x1B[1m",
|
|
293
|
+
green: "\x1B[32m",
|
|
294
|
+
red: "\x1B[31m"
|
|
295
|
+
};
|
|
296
|
+
var logError4 = (err) => console.error(`${C5.red}${C5.bold}Error:${C5.reset} ${String(err)}`);
|
|
204
297
|
var prog = sade("greenseed");
|
|
205
298
|
prog.command("push").option("-c, --config <file>", "Config file", "seed.config.json").option("-d, --dbvar <name>", "DB env var override").action(async (opts) => {
|
|
206
299
|
try {
|
|
207
300
|
await run(opts);
|
|
208
301
|
} catch (err) {
|
|
209
|
-
|
|
302
|
+
logError4(err);
|
|
210
303
|
process.exit(1);
|
|
211
304
|
}
|
|
212
305
|
});
|
|
213
306
|
prog.command("init").option("-c, --config <file>", "Config file", "seed.config.json").action((opts) => {
|
|
214
307
|
try {
|
|
215
308
|
init(opts.config);
|
|
216
|
-
console.log(
|
|
309
|
+
console.log(
|
|
310
|
+
`${C5.green}\u2713${C5.reset} Config created: ${C5.bold}${opts.config}${C5.reset}`
|
|
311
|
+
);
|
|
217
312
|
} catch (err) {
|
|
218
|
-
|
|
313
|
+
logError4(err);
|
|
219
314
|
process.exit(1);
|
|
220
315
|
}
|
|
221
316
|
});
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/init.ts","../src/cli.ts"],"names":["path","fs"],"mappings":"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","file":"cli.js","sourcesContent":["import postgres from \"postgres\"\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tthrow new Error(\"Database connection failed\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${\"\\\"\" + schema + \"\\\".\\\"\" + table + \"\\\"\"}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tthrow new Error(\"Failed checking table: \" + schema + \".\" + table)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tthrow new Error(\"Missing table: \" + t.schema + \".\" + t.table)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tthrow new Error(\"No primary keys for \" + schema + \".\" + table)\n\t}\n\n\tconst q = await sql`\n\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t${sql(rows, columns)}\n\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t`\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tthrow new Error(\"Failed to read file: \" + p)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tthrow new Error(\"Source file not found: \" + p)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tthrow new Error(\"Invalid JSON: \" + p)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tthrow new Error(\"Data is not an array: \" + p)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tconsole.log(C.gray + \"Starting seed process\" + C.reset)\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tconsole.log(C.yellow + \"Skipped \" + t.table + C.reset)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t)\n\t\t}\n\n\t\tconsole.log(\n\t\t\tC.green +\n\t\t\t\tt.schema +\n\t\t\t\t\".\" +\n\t\t\t\tt.table +\n\t\t\t\t\": \" +\n\t\t\t\tinserted +\n\t\t\t\t\"/\" +\n\t\t\t\trows.length +\n\t\t\t\tC.reset,\n\t\t)\n\t}\n\n\tawait sql.end()\n\tconsole.log(C.green + \"Seed completed\" + C.reset)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst DEFAULT_CONFIG = {\n\tseedFileExtensions: [\".json\"],\n\tdatabaseUrlEnvVar: \"DATABASE_URL\",\n\tonMissingFile: \"error\",\n\ttables: [\n\t\t{\n\t\t\ttable: \"example_table\",\n\t\t\tschema: \"public\",\n\t\t\tprimaryKeys: [\"id\"],\n\t\t\tsource: \"./data/example_table.json\",\n\t\t},\n\t],\n}\n\nexport function init(configPath: string): void {\n\tconst resolved = path.resolve(process.cwd(), configPath)\n\n\tif (fs.existsSync(resolved)) {\n\t\tthrow new Error(\"Config already exists: \" + resolved)\n\t}\n\n\tfs.writeFileSync(\n\t\tresolved,\n\t\tJSON.stringify(DEFAULT_CONFIG, null, 2) + \"\\n\",\n\t\t\"utf8\",\n\t)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\nimport { init } from \"./init\"\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog\n\t.command(\"init\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.action((opts) => {\n\t\ttry {\n\t\t\tinit(opts.config)\n\t\t\tconsole.log(\"\\x1b[32mConfig created: \" + opts.config + \"\\x1b[0m\")\n\t\t} catch (err) {\n\t\t\tconsole.error(\"\\x1b[31m\" + String(err) + \"\\x1b[0m\")\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/db.ts","../src/fs.ts","../src/app.ts","../src/init.ts","../src/cli.ts"],"names":["C","logError","path","fs"],"mappings":"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","file":"cli.js","sourcesContent":["import postgres from \"postgres\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\n\nexport function connect(url: string): postgres.Sql {\n\treturn postgres(url, { max: 10, idle_timeout: 20 })\n}\n\nexport async function check(sql: postgres.Sql): Promise<void> {\n\ttry {\n\t\tawait sql`select 1`\n\t} catch (err) {\n\t\tlogError(\"Database connection failed\")\n\t\tthrow new Error(\"Failed to connect to database\")\n\t}\n}\n\nexport async function tableExists(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<boolean> {\n\tconst res = await sql<{ exists: boolean }[]>`\n\t\tselect to_regclass(${'\"' + schema + '\".\"' + table + '\"'}) is not null as exists\n\t`\n\tif (!res[0]) {\n\t\tlogError(`Failed to check table: ${schema}.${table}`)\n\t\tthrow new Error(`Table check failed: ${schema}.${table}`)\n\t}\n\treturn res[0].exists\n}\n\nexport async function validateTables(\n\tsql: postgres.Sql,\n\ttables: { schema: string; table: string }[],\n): Promise<void> {\n\tfor (const t of tables) {\n\t\tconst ok = await tableExists(sql, t.schema, t.table)\n\t\tif (!ok) {\n\t\t\tlogError(`Table does not exist: ${t.schema}.${t.table}`)\n\t\t\tthrow new Error(`Missing table: ${t.schema}.${t.table}`)\n\t\t}\n\t}\n}\n\nexport async function metadata(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n): Promise<{ columns: string[] }> {\n\tconst cols = await sql<{ column_name: string }[]>`\n\t\tselect column_name\n\t\tfrom information_schema.columns\n\t\twhere table_schema = ${schema}\n\t\tand table_name = ${table}\n\t\torder by ordinal_position\n\t`\n\treturn { columns: cols.map((c) => c.column_name) }\n}\n\nexport async function insert(\n\tsql: postgres.Sql,\n\tschema: string,\n\ttable: string,\n\trows: Record<string, any>[],\n\tcolumns: string[],\n\tprimaryKeys: string[],\n\tupdateOnConflict: string[],\n): Promise<number> {\n\tif (primaryKeys.length === 0) {\n\t\tlogError(`No primary keys defined for ${schema}.${table}`)\n\t\tthrow new Error(`No primary keys for ${schema}.${table}`)\n\t}\n\n\tconst updateCols = updateOnConflict.filter(\n\t\t(col) => !primaryKeys.includes(col),\n\t)\n\n\tlet q\n\tif (updateCols.length > 0) {\n\t\tconst updates = Object.fromEntries(\n\t\t\tupdateCols.map((col) => [col, sql`excluded.${sql(col)}`]),\n\t\t)\n\n\t\tq = await sql`\n\t\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t\t${sql(rows, columns)}\n\t\t\ton conflict (${sql(primaryKeys)}) do update set\n\t\t\t${sql(updates)}\n\t\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t\t`\n\t} else {\n\t\tq = await sql`\n\t\t\tinsert into ${sql(schema + \".\" + table)}\n\t\t\t${sql(rows, columns)}\n\t\t\ton conflict (${sql(primaryKeys)}) do nothing\n\t\t\treturning ${sql(primaryKeys[0] ?? [])}\n\t\t`\n\t}\n\treturn q.length\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\n\nexport function exists(p: string): boolean {\n\treturn fs.existsSync(p)\n}\n\nexport function readFile(p: string): string {\n\ttry {\n\t\treturn fs.readFileSync(p, \"utf8\")\n\t} catch (err) {\n\t\tlogError(`Failed to read file: ${p}`)\n\t\tthrow new Error(`File read error: ${p}`)\n\t}\n}\n\nexport function resolvePath(p: string): string {\n\treturn path.resolve(process.cwd(), p)\n}\n\nexport function ext(p: string): string {\n\treturn path.extname(p).toLowerCase()\n}\n\nexport function loadJsonArray(p: string, onMissing: \"skip\" | \"error\"): any[] {\n\tif (!exists(p)) {\n\t\tif (onMissing === \"skip\") {\n\t\t\treturn []\n\t\t}\n\t\tlogError(`Source file not found: ${p}`)\n\t\tthrow new Error(`Missing file: ${p}`)\n\t}\n\n\tconst raw = readFile(p)\n\n\tlet parsed: unknown\n\ttry {\n\t\tparsed = JSON.parse(raw)\n\t} catch {\n\t\tlogError(`Invalid JSON in file: ${p}`)\n\t\tthrow new Error(`JSON parse error: ${p}`)\n\t}\n\n\tif (!Array.isArray(parsed)) {\n\t\tlogError(`Expected array but got ${typeof parsed}: ${p}`)\n\t\tthrow new Error(`Invalid data format: ${p}`)\n\t}\n\n\treturn parsed\n}\n\nexport function chunk<T>(data: T[], size: number): T[][] {\n\tconst out: T[][] = []\n\tfor (let i = 0; i < data.length; i += size) {\n\t\tout.push(data.slice(i, i + size))\n\t}\n\treturn out\n}\n","import * as v from \"valibot\"\nimport dotenv from \"dotenv\"\nimport path from \"path\"\nimport { connect, check, validateTables, metadata, insert } from \"./db\"\nimport { resolvePath, loadJsonArray, chunk, readFile } from \"./fs\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tbold: \"\\x1b[1m\",\n\tdim: \"\\x1b[2m\",\n\tgreen: \"\\x1b[32m\",\n\tyellow: \"\\x1b[33m\",\n\tblue: \"\\x1b[34m\",\n\tcyan: \"\\x1b[36m\",\n\tred: \"\\x1b[31m\",\n\tgray: \"\\x1b[90m\",\n}\n\nconst log = {\n\tinfo: (msg: string) => console.log(`${C.blue}ℹ${C.reset} ${msg}`),\n\tsuccess: (msg: string) => console.log(`${C.green}✓${C.reset} ${msg}`),\n\twarn: (msg: string) => console.log(`${C.yellow}⚠${C.reset} ${msg}`),\n\terror: (msg: string) => console.log(`${C.red}✗${C.reset} ${msg}`),\n\tdim: (msg: string) => console.log(`${C.dim}${msg}${C.reset}`),\n\theader: (msg: string) =>\n\t\tconsole.log(`\\n${C.bold}${C.cyan}${msg}${C.reset}\\n`),\n\ttable: (label: string, value: string | number) =>\n\t\tconsole.log(` ${C.dim}${label}:${C.reset} ${C.bold}${value}${C.reset}`),\n\tprogress: (current: number, total: number, label: string) => {\n\t\tconst percentage = Math.round((current / total) * 100)\n\t\tconst bar = \"█\".repeat(Math.floor(percentage / 5))\n\t\tconst empty = \"░\".repeat(20 - Math.floor(percentage / 5))\n\t\tconsole.log(\n\t\t\t` ${C.cyan}${bar}${empty}${C.reset} ${C.bold}${percentage}%${C.reset} ${C.dim}(${current}/${total})${C.reset} ${label}`,\n\t\t)\n\t},\n}\n\nconst ConfigSchema = v.object({\n\tseedFileExtensions: v.array(v.picklist([\".json\"])),\n\tdatabaseUrlEnvVar: v.string(),\n\tonMissingFile: v.optional(v.picklist([\"skip\", \"error\"]), \"error\"),\n\ttables: v.array(\n\t\tv.object({\n\t\t\ttable: v.string(),\n\t\t\tschema: v.optional(v.string(), \"public\"),\n\t\t\tprimaryKeys: v.array(v.string()),\n\t\t\tsource: v.string(),\n\t\t\tupdateOnConflict: v.optional(v.nullable(v.array(v.string())), null),\n\t\t}),\n\t),\n})\n\ntype Opts = { config: string; dbvar?: string }\n\nexport async function run(opts: Opts): Promise<void> {\n\tdotenv.config()\n\n\tconst configPath = resolvePath(opts.config || \"seed.config.json\")\n\n\tlog.header(\"🌱 GreenSeed\")\n\n\tlet raw: unknown\n\ttry {\n\t\traw = JSON.parse(readFile(configPath))\n\t} catch (e) {\n\t\tthrow new Error(\n\t\t\t`Failed to load config at ${configPath}: ${e instanceof Error ? e.message : String(e)}`,\n\t\t)\n\t}\n\n\tconst parsed = v.safeParse(ConfigSchema, raw)\n\n\tif (!parsed.success) {\n\t\tthrow new Error(\"Invalid configuration\")\n\t}\n\n\tconst config = parsed.output\n\tconst dbVar =\n\t\topts.dbvar && opts.dbvar.length > 0 ? opts.dbvar : config.databaseUrlEnvVar\n\n\tconst dbUrl = process.env[dbVar]\n\tif (!dbUrl) {\n\t\tthrow new Error(\"Missing DB env var: \" + dbVar)\n\t}\n\n\tlog.info(`Using config: ${C.dim}${configPath}${C.reset}`)\n\tlog.info(`Database: ${C.dim}${dbVar}${C.reset}`)\n\n\tconst sql = connect(dbUrl)\n\tawait check(sql)\n\n\tawait validateTables(\n\t\tsql,\n\t\tconfig.tables.map((t) => ({\n\t\t\ttable: t.table,\n\t\t\tschema: t.schema,\n\t\t})),\n\t)\n\n\tlog.success(\"Database connection established\")\n\tlog.header(\"Seeding Tables\")\n\n\tfor (const t of config.tables) {\n\t\tconst source = path.resolve(path.dirname(configPath), t.source)\n\t\tconst rows = loadJsonArray(source, config.onMissingFile || \"error\")\n\n\t\tif (rows.length === 0) {\n\t\t\tlog.warn(`Skipped ${C.bold}${t.schema}.${t.table}${C.reset} (no data)`)\n\t\t\tcontinue\n\t\t}\n\n\t\tconst meta = await metadata(sql, t.schema, t.table)\n\t\tconst validCols = Object.keys(rows[0]).filter((c) =>\n\t\t\tmeta.columns.includes(c),\n\t\t)\n\n\t\tlog.info(`Seeding ${C.bold}${t.schema}.${t.table}${C.reset}`)\n\n\t\tlet inserted = 0\n\t\tfor (const part of chunk(rows, 1000)) {\n\t\t\tinserted += await insert(\n\t\t\t\tsql,\n\t\t\t\tt.schema,\n\t\t\t\tt.table,\n\t\t\t\tpart,\n\t\t\t\tvalidCols,\n\t\t\t\tt.primaryKeys,\n\t\t\t\tt.updateOnConflict || [],\n\t\t\t)\n\t\t}\n\n\t\tconst hasConflict = t.updateOnConflict && t.updateOnConflict.length > 0\n\t\tconst action = hasConflict ? \"upserted\" : \"inserted\"\n\t\tconst skipped = rows.length - inserted\n\n\t\tif (inserted === rows.length) {\n\t\t\tlog.success(\n\t\t\t\t`${C.bold}${t.schema}.${t.table}${C.reset} ${C.green}${action} ${inserted} rows${C.reset}`,\n\t\t\t)\n\t\t} else {\n\t\t\tlog.success(\n\t\t\t\t`${C.bold}${t.schema}.${t.table}${C.reset} ${C.green}${action} ${inserted}${C.reset}${C.dim}, skipped ${skipped}${C.reset}`,\n\t\t\t)\n\t\t}\n\t}\n\n\tawait sql.end()\n\n\tlog.header(\"Summary\")\n\tlog.success(\n\t\t`Seeded ${C.bold}${config.tables.length}${C.reset} table${config.tables.length === 1 ? \"\" : \"s\"}`,\n\t)\n}\n","import fs from \"fs\"\nimport path from \"path\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tdim: \"\\x1b[2m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (msg: string) =>\n\tconsole.error(`${C.red}✗${C.reset} ${C.dim}${msg}${C.reset}`)\n\nconst DEFAULT_CONFIG = {\n\tseedFileExtensions: [\".json\"],\n\tdatabaseUrlEnvVar: \"DATABASE_URL\",\n\tonMissingFile: \"error\",\n\ttables: [\n\t\t{\n\t\t\ttable: \"example_table\",\n\t\t\tschema: \"public\",\n\t\t\tprimaryKeys: [\"id\"],\n\t\t\tsource: \"./data/example_table.json\",\n\t\t\tupdateOnConflict: [],\n\t\t},\n\t],\n}\n\nexport function init(configPath: string): void {\n\tconst resolved = path.resolve(process.cwd(), configPath)\n\n\tif (fs.existsSync(resolved)) {\n\t\tlogError(`Config file already exists: ${resolved}`)\n\t\tthrow new Error(`Config already exists: ${resolved}`)\n\t}\n\n\tfs.writeFileSync(\n\t\tresolved,\n\t\tJSON.stringify(DEFAULT_CONFIG, null, 2) + \"\\n\",\n\t\t\"utf8\",\n\t)\n}\n","#!/usr/bin/env node\nimport sade from \"sade\"\nimport { run } from \"./app\"\nimport { init } from \"./init\"\n\nconst C = {\n\treset: \"\\x1b[0m\",\n\tbold: \"\\x1b[1m\",\n\tgreen: \"\\x1b[32m\",\n\tred: \"\\x1b[31m\",\n}\n\nconst logError = (err: unknown) =>\n\tconsole.error(`${C.red}${C.bold}Error:${C.reset} ${String(err)}`)\n\nconst prog = sade(\"greenseed\")\n\nprog\n\t.command(\"push\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.option(\"-d, --dbvar <name>\", \"DB env var override\")\n\t.action(async (opts) => {\n\t\ttry {\n\t\t\tawait run(opts)\n\t\t} catch (err) {\n\t\t\tlogError(err)\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog\n\t.command(\"init\")\n\t.option(\"-c, --config <file>\", \"Config file\", \"seed.config.json\")\n\t.action((opts) => {\n\t\ttry {\n\t\t\tinit(opts.config)\n\t\t\tconsole.log(\n\t\t\t\t`${C.green}✓${C.reset} Config created: ${C.bold}${opts.config}${C.reset}`,\n\t\t\t)\n\t\t} catch (err) {\n\t\t\tlogError(err)\n\t\t\tprocess.exit(1)\n\t\t}\n\t})\n\nprog.parse(process.argv)\n"]}
|