nomi-relay-cli 0.0.0-stage → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -2
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -0
- package/dist/lint.js +83 -0
- package/dist/lint.js.map +1 -0
- package/dist/migrations.js +53 -0
- package/dist/migrations.js.map +1 -0
- package/dist/run.js +164 -0
- package/dist/run.js.map +1 -0
- package/dist/sql.js +92 -0
- package/dist/sql.js.map +1 -0
- package/package.json +34 -4
package/README.md
CHANGED
|
@@ -1,3 +1,40 @@
|
|
|
1
|
-
#
|
|
1
|
+
# nomi-relay-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The command line for Nomi Relay. It installs a `nomi-relay` command.
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
npx nomi-relay-cli --help
|
|
7
|
+
# or
|
|
8
|
+
npm install -D nomi-relay-cli && npx nomi-relay --help
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Commands
|
|
12
|
+
|
|
13
|
+
| Command | What it does |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `nomi-relay init` | Creates `nomi/backend.json`, `nomi/migrations/0001_init.sql` and `nomi/functions/` |
|
|
16
|
+
| `nomi-relay migrate new <name>` | Writes the next numbered migration, e.g. `0003_add_orders.sql` |
|
|
17
|
+
| `nomi-relay migrate lint` | Checks migrations before they're applied (exits 1 on problems, so it fits CI) |
|
|
18
|
+
| `nomi-relay migrate status` | Lists local migrations with their checksums |
|
|
19
|
+
| `nomi-relay functions new <name>` | Writes `nomi/functions/<name>.ts` from a starter |
|
|
20
|
+
|
|
21
|
+
`--dir <path>` points any command at another project folder.
|
|
22
|
+
|
|
23
|
+
## What `migrate lint` checks
|
|
24
|
+
|
|
25
|
+
- **Row-level security.** Every `create table` needs `alter table … enable row level security`
|
|
26
|
+
in some migration, unless it's marked `-- nomi: public` on the line above.
|
|
27
|
+
- **Drops that lose data.** `drop table` and `alter table … drop column` need
|
|
28
|
+
`-- nomi: destructive` on the line above.
|
|
29
|
+
- **Staying inside the project.** No objects in other schemas (such as `nomi.*`), no schema,
|
|
30
|
+
role or database changes, no `set role` or `search_path`.
|
|
31
|
+
|
|
32
|
+
Relay runs the same checks, authoritatively, when it applies migrations; the CLI gives you
|
|
33
|
+
the answer before that.
|
|
34
|
+
|
|
35
|
+
## Coming with the hosted Relay
|
|
36
|
+
|
|
37
|
+
`login`, `link`, `migrate apply`, `types`, `functions serve`, `functions deploy` and
|
|
38
|
+
`secrets set` are reserved and say so when called.
|
|
39
|
+
|
|
40
|
+
The app-side client is [`nomi-relay`](https://www.npmjs.com/package/nomi-relay).
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { EXIT_PROBLEMS, run } from './run.js';
|
|
3
|
+
run(process.argv.slice(2), { cwd: process.cwd(), out: console.log, err: console.error }).then((code) => process.exit(code), (error) => {
|
|
4
|
+
console.error(error);
|
|
5
|
+
process.exit(EXIT_PROBLEMS);
|
|
6
|
+
});
|
|
7
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE9C,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,EAAE,GAAG,EAAE,OAAO,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC,IAAI,CAC3F,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAC5B,CAAC,KAAK,EAAE,EAAE;IACR,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;AAC9B,CAAC,CACF,CAAC"}
|
package/dist/lint.js
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { splitStatements, unquote } from './sql.js';
|
|
2
|
+
/** Marks a table that's meant to be readable without row-level security. */
|
|
3
|
+
export const PUBLIC_MARKER = 'nomi: public';
|
|
4
|
+
/** Marks a drop that's meant to lose data. */
|
|
5
|
+
export const DESTRUCTIVE_MARKER = 'nomi: destructive';
|
|
6
|
+
/** The only schema a project's migrations may create things in by name; unqualified names land there too. */
|
|
7
|
+
const OWN_SCHEMA = 'public';
|
|
8
|
+
const IDENT = String.raw `(?:"[^"]+"|[a-z_][a-z0-9_$]*)`;
|
|
9
|
+
const QUALIFIED = String.raw `(?:(${IDENT})\.)?(${IDENT})`;
|
|
10
|
+
const CREATE_TABLE = new RegExp(String.raw `^create (?:unlogged )?table (?:if not exists )?${QUALIFIED}`);
|
|
11
|
+
const ENABLE_RLS = new RegExp(String.raw `^alter table (?:if exists )?(?:only )?${QUALIFIED} enable row level security`);
|
|
12
|
+
const DROP_TABLE = /^drop table /;
|
|
13
|
+
const DROP_COLUMN = /^alter table .* drop (?:column )?(?!constraint\b|default\b|not null\b|identity\b|expression\b)/;
|
|
14
|
+
const QUALIFIED_DDL = new RegExp(String.raw `^(?:create|alter|drop) (?:or replace )?(?:unique )?(?:unlogged )?(?:materialized )?` +
|
|
15
|
+
String.raw `(?:table|view|function|procedure|index|type|trigger|sequence|domain|aggregate) ` +
|
|
16
|
+
String.raw `(?:if (?:not )?exists )?(?:concurrently )?(?:only )?(${IDENT})\.`);
|
|
17
|
+
const QUALIFIED_ON = new RegExp(String.raw `^create (?:policy|trigger|(?:unique )?index) .*? on (?:only )?(${IDENT})\.${IDENT}`);
|
|
18
|
+
const FORBIDDEN = [
|
|
19
|
+
{ pattern: /^(?:create|alter|drop) schema\b/, what: 'creating, changing or dropping a schema' },
|
|
20
|
+
{ pattern: /^(?:create|alter|drop) (?:role|user|group)\b/, what: 'managing database roles' },
|
|
21
|
+
{ pattern: /^(?:create|alter|drop) database\b/, what: 'managing databases' },
|
|
22
|
+
{ pattern: /^(?:set|reset) (?:local )?(?:role|session authorization|search_path)\b/, what: 'switching role or search_path' },
|
|
23
|
+
{ pattern: /^alter system\b/, what: 'changing server settings' },
|
|
24
|
+
{ pattern: /^(?:create|alter|drop) event trigger\b/, what: 'event triggers' },
|
|
25
|
+
{ pattern: /^copy .* (?:from|to) program\b/, what: 'running programs on the server' }
|
|
26
|
+
];
|
|
27
|
+
function hasMarker(statement, marker) {
|
|
28
|
+
return statement.comments.toLowerCase().includes(marker);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Checks migrations the way Relay does before applying them. This is an early check for the
|
|
32
|
+
* editor and CI; Relay runs the authoritative one.
|
|
33
|
+
*/
|
|
34
|
+
export function lintMigrations(files) {
|
|
35
|
+
const issues = [];
|
|
36
|
+
const parsed = files.map((file) => ({ file, statements: splitStatements(file.sql) }));
|
|
37
|
+
const rlsEnabled = new Set();
|
|
38
|
+
for (const { statements } of parsed) {
|
|
39
|
+
for (const statement of statements) {
|
|
40
|
+
const match = ENABLE_RLS.exec(statement.code);
|
|
41
|
+
if (match)
|
|
42
|
+
rlsEnabled.add(unquote(match[2]));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
for (const { file, statements } of parsed) {
|
|
46
|
+
for (const statement of statements) {
|
|
47
|
+
const at = { file: file.name, line: statement.line };
|
|
48
|
+
const table = CREATE_TABLE.exec(statement.code);
|
|
49
|
+
if (table) {
|
|
50
|
+
const name = unquote(table[2]);
|
|
51
|
+
if (!rlsEnabled.has(name) && !hasMarker(statement, PUBLIC_MARKER)) {
|
|
52
|
+
issues.push({
|
|
53
|
+
...at,
|
|
54
|
+
rule: 'row-level-security',
|
|
55
|
+
message: `table "${name}" has no row-level security: add "alter table ${name} enable row level security;" and policies, or mark it "-- ${PUBLIC_MARKER}"`
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if ((DROP_TABLE.test(statement.code) || DROP_COLUMN.test(statement.code)) && !hasMarker(statement, DESTRUCTIVE_MARKER)) {
|
|
60
|
+
issues.push({
|
|
61
|
+
...at,
|
|
62
|
+
rule: 'destructive',
|
|
63
|
+
message: `this drops data: mark it "-- ${DESTRUCTIVE_MARKER}" if that's intended`
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
const schema = QUALIFIED_DDL.exec(statement.code)?.[1] ?? QUALIFIED_ON.exec(statement.code)?.[1];
|
|
67
|
+
if (schema && unquote(schema) !== OWN_SCHEMA) {
|
|
68
|
+
issues.push({
|
|
69
|
+
...at,
|
|
70
|
+
rule: 'outside-project',
|
|
71
|
+
message: `migrations may only define things in the project's own schema, not "${unquote(schema)}"`
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
for (const { pattern, what } of FORBIDDEN) {
|
|
75
|
+
if (pattern.test(statement.code)) {
|
|
76
|
+
issues.push({ ...at, rule: 'outside-project', message: `migrations can't do ${what}` });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return issues;
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=lint.js.map
|
package/dist/lint.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lint.js","sourceRoot":"","sources":["../src/lint.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,OAAO,EAAkB,MAAM,UAAU,CAAC;AAgBpE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,aAAa,GAAG,cAAc,CAAC;AAC5C,8CAA8C;AAC9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,mBAAmB,CAAC;AAEtD,6GAA6G;AAC7G,MAAM,UAAU,GAAG,QAAQ,CAAC;AAE5B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAA,+BAA+B,CAAC;AACxD,MAAM,SAAS,GAAG,MAAM,CAAC,GAAG,CAAA,OAAO,KAAK,SAAS,KAAK,GAAG,CAAC;AAE1D,MAAM,YAAY,GAAG,IAAI,MAAM,CAC7B,MAAM,CAAC,GAAG,CAAA,kDAAkD,SAAS,EAAE,CACxE,CAAC;AACF,MAAM,UAAU,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,yCAAyC,SAAS,4BAA4B,CAAC,CAAC;AACxH,MAAM,UAAU,GAAG,cAAc,CAAC;AAClC,MAAM,WAAW,GAAG,gGAAgG,CAAC;AACrH,MAAM,aAAa,GAAG,IAAI,MAAM,CAC9B,MAAM,CAAC,GAAG,CAAA,qFAAqF;IAC7F,MAAM,CAAC,GAAG,CAAA,iFAAiF;IAC3F,MAAM,CAAC,GAAG,CAAA,wDAAwD,KAAK,KAAK,CAC/E,CAAC;AACF,MAAM,YAAY,GAAG,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAA,kEAAkE,KAAK,MAAM,KAAK,EAAE,CAAC,CAAC;AAChI,MAAM,SAAS,GAAwC;IACrD,EAAE,OAAO,EAAE,iCAAiC,EAAE,IAAI,EAAE,yCAAyC,EAAE;IAC/F,EAAE,OAAO,EAAE,8CAA8C,EAAE,IAAI,EAAE,yBAAyB,EAAE;IAC5F,EAAE,OAAO,EAAE,mCAAmC,EAAE,IAAI,EAAE,oBAAoB,EAAE;IAC5E,EAAE,OAAO,EAAE,wEAAwE,EAAE,IAAI,EAAE,+BAA+B,EAAE;IAC5H,EAAE,OAAO,EAAE,iBAAiB,EAAE,IAAI,EAAE,0BAA0B,EAAE;IAChE,EAAE,OAAO,EAAE,wCAAwC,EAAE,IAAI,EAAE,gBAAgB,EAAE;IAC7E,EAAE,OAAO,EAAE,gCAAgC,EAAE,IAAI,EAAE,gCAAgC,EAAE;CACtF,CAAC;AAEF,SAAS,SAAS,CAAC,SAAoB,EAAE,MAAc;IACrD,OAAO,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AAC3D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,KAAsB;IACnD,MAAM,MAAM,GAAY,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,EAAE,eAAe,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;IAEtF,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;IACrC,KAAK,MAAM,EAAE,UAAU,EAAE,IAAI,MAAM,EAAE,CAAC;QACpC,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC9C,IAAI,KAAK;gBAAE,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,KAAK,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,MAAM,EAAE,CAAC;QAC1C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACnC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,SAAS,CAAC,IAAI,EAAE,CAAC;YAErD,MAAM,KAAK,GAAG,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAChD,IAAI,KAAK,EAAE,CAAC;gBACV,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAE,CAAC,CAAC;gBAChC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,aAAa,CAAC,EAAE,CAAC;oBAClE,MAAM,CAAC,IAAI,CAAC;wBACV,GAAG,EAAE;wBACL,IAAI,EAAE,oBAAoB;wBAC1B,OAAO,EAAE,UAAU,IAAI,iDAAiD,IAAI,6DAA6D,aAAa,GAAG;qBAC1J,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;YAED,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,WAAW,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,SAAS,EAAE,kBAAkB,CAAC,EAAE,CAAC;gBACvH,MAAM,CAAC,IAAI,CAAC;oBACV,GAAG,EAAE;oBACL,IAAI,EAAE,aAAa;oBACnB,OAAO,EAAE,gCAAgC,kBAAkB,sBAAsB;iBAClF,CAAC,CAAC;YACL,CAAC;YAED,MAAM,MAAM,GAAG,aAAa,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACjG,IAAI,MAAM,IAAI,OAAO,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC;gBAC7C,MAAM,CAAC,IAAI,CAAC;oBACV,GAAG,EAAE;oBACL,IAAI,EAAE,iBAAiB;oBACvB,OAAO,EAAE,uEAAuE,OAAO,CAAC,MAAM,CAAC,GAAG;iBACnG,CAAC,CAAC;YACL,CAAC;YACD,KAAK,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,SAAS,EAAE,CAAC;gBAC1C,IAAI,OAAO,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;oBACjC,MAAM,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,uBAAuB,IAAI,EAAE,EAAE,CAAC,CAAC;gBAC1F,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
/** Migrations are `0001_init.sql`, `0002_orders.sql`…: a zero-padded number, then a name. */
|
|
5
|
+
export const MIGRATION_NAME = /^(\d{4,})_([a-z0-9_]+)\.sql$/;
|
|
6
|
+
const NUMBER_WIDTH = 4;
|
|
7
|
+
export function checksum(sql) {
|
|
8
|
+
return createHash('sha256').update(sql).digest('hex');
|
|
9
|
+
}
|
|
10
|
+
export async function readMigrations(dir) {
|
|
11
|
+
let entries;
|
|
12
|
+
try {
|
|
13
|
+
entries = await readdir(dir);
|
|
14
|
+
}
|
|
15
|
+
catch (error) {
|
|
16
|
+
if (error.code === 'ENOENT')
|
|
17
|
+
return { migrations: [], problems: [] };
|
|
18
|
+
throw error;
|
|
19
|
+
}
|
|
20
|
+
const migrations = [];
|
|
21
|
+
const problems = [];
|
|
22
|
+
for (const file of entries.sort()) {
|
|
23
|
+
if (file.startsWith('.'))
|
|
24
|
+
continue;
|
|
25
|
+
const match = MIGRATION_NAME.exec(file);
|
|
26
|
+
if (!match) {
|
|
27
|
+
problems.push({ file, message: 'not a migration name: use 0001_name.sql (lowercase letters, digits, underscores)' });
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const sql = await readFile(join(dir, file), 'utf8');
|
|
31
|
+
migrations.push({ number: Number(match[1]), name: match[2], file, sql, checksum: checksum(sql) });
|
|
32
|
+
}
|
|
33
|
+
migrations.sort((a, b) => a.number - b.number);
|
|
34
|
+
for (let i = 1; i < migrations.length; i++) {
|
|
35
|
+
if (migrations[i].number === migrations[i - 1].number) {
|
|
36
|
+
problems.push({ file: migrations[i].file, message: `has the same number as ${migrations[i - 1].file}` });
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
return { migrations, problems };
|
|
40
|
+
}
|
|
41
|
+
/** `Add Orders!` → `add_orders`. */
|
|
42
|
+
export function slug(name) {
|
|
43
|
+
return name
|
|
44
|
+
.trim()
|
|
45
|
+
.toLowerCase()
|
|
46
|
+
.replace(/[^a-z0-9]+/g, '_')
|
|
47
|
+
.replace(/^_+|_+$/g, '');
|
|
48
|
+
}
|
|
49
|
+
export function nextMigrationFile(existing, name) {
|
|
50
|
+
const number = (existing.at(-1)?.number ?? 0) + 1;
|
|
51
|
+
return `${String(number).padStart(NUMBER_WIDTH, '0')}_${slug(name)}.sql`;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=migrations.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"migrations.js","sourceRoot":"","sources":["../src/migrations.ts"],"names":[],"mappings":"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"}
|
package/dist/run.js
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { access, mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join, relative, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
import { parseArgs } from 'node:util';
|
|
5
|
+
import { lintMigrations } from './lint.js';
|
|
6
|
+
import { nextMigrationFile, readMigrations, slug } from './migrations.js';
|
|
7
|
+
const EXIT_OK = 0;
|
|
8
|
+
export const EXIT_PROBLEMS = 1;
|
|
9
|
+
const EXIT_USAGE = 2;
|
|
10
|
+
/** Shown checksums are cut to this many characters; the full one is what Relay compares. */
|
|
11
|
+
const SHORT_CHECKSUM_LENGTH = 12;
|
|
12
|
+
const HELP = `nomi-relay: command line for Nomi Relay
|
|
13
|
+
|
|
14
|
+
Usage: nomi-relay <command> [options]
|
|
15
|
+
|
|
16
|
+
Commands
|
|
17
|
+
init Create nomi/backend.json, nomi/migrations and nomi/functions
|
|
18
|
+
migrate new <name> Write the next numbered migration, e.g. 0003_add_orders.sql
|
|
19
|
+
migrate lint Check migrations for missing row-level security, unmarked drops
|
|
20
|
+
and statements that reach outside the project
|
|
21
|
+
migrate status List local migrations with their checksums
|
|
22
|
+
functions new <name> Write nomi/functions/<name>.ts from a starter
|
|
23
|
+
|
|
24
|
+
Coming with the hosted Relay
|
|
25
|
+
login, link, migrate apply, types, functions serve, functions deploy, secrets set
|
|
26
|
+
|
|
27
|
+
Options
|
|
28
|
+
--dir <path> Project folder (default: the current folder)
|
|
29
|
+
-h, --help Show this help
|
|
30
|
+
-v, --version Show the version`;
|
|
31
|
+
const COMING_SOON = new Set(['login', 'link', 'types', 'secrets', 'migrate apply', 'functions serve', 'functions deploy']);
|
|
32
|
+
const BACKEND_JSON = {
|
|
33
|
+
auth: { methods: ['email_code'], redirect_urls: [] },
|
|
34
|
+
storage: { buckets: [] },
|
|
35
|
+
realtime: [],
|
|
36
|
+
functions: {}
|
|
37
|
+
};
|
|
38
|
+
const FIRST_MIGRATION = `-- Plain SQL, applied once, in order. Never edit a migration after it's applied: add a new one.
|
|
39
|
+
-- Every table needs row-level security (or a "-- nomi: public" marker).
|
|
40
|
+
`;
|
|
41
|
+
function functionStarter(name) {
|
|
42
|
+
return `import { defineFunction } from 'nomi-relay/functions';
|
|
43
|
+
|
|
44
|
+
// Called at /functions/${name}, or nomi.functions.invoke('${name}', { body }).
|
|
45
|
+
export default defineFunction(async (req, ctx) => {
|
|
46
|
+
if (!ctx.user) return new Response('sign in first', { status: 401 });
|
|
47
|
+
const body = req.method === 'GET' ? null : await req.json();
|
|
48
|
+
return Response.json({ ok: true, user: ctx.user.id, body });
|
|
49
|
+
});
|
|
50
|
+
`;
|
|
51
|
+
}
|
|
52
|
+
async function exists(path) {
|
|
53
|
+
try {
|
|
54
|
+
await access(path);
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
async function writeNew(path, content, io) {
|
|
62
|
+
if (await exists(path)) {
|
|
63
|
+
io.err(`${relative(io.cwd, path)} already exists; leaving it as it is`);
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
await mkdir(dirname(path), { recursive: true });
|
|
67
|
+
await writeFile(path, content);
|
|
68
|
+
io.out(`created ${relative(io.cwd, path)}`);
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
async function version() {
|
|
72
|
+
const pkg = JSON.parse(await readFile(join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json'), 'utf8'));
|
|
73
|
+
return String(pkg.version);
|
|
74
|
+
}
|
|
75
|
+
export async function run(argv, io) {
|
|
76
|
+
let parsed;
|
|
77
|
+
try {
|
|
78
|
+
parsed = parseArgs({
|
|
79
|
+
args: argv,
|
|
80
|
+
allowPositionals: true,
|
|
81
|
+
options: {
|
|
82
|
+
dir: { type: 'string' },
|
|
83
|
+
help: { type: 'boolean', short: 'h' },
|
|
84
|
+
version: { type: 'boolean', short: 'v' }
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
io.err(error.message);
|
|
90
|
+
return EXIT_USAGE;
|
|
91
|
+
}
|
|
92
|
+
const { values, positionals } = parsed;
|
|
93
|
+
if (values.version) {
|
|
94
|
+
io.out(await version());
|
|
95
|
+
return EXIT_OK;
|
|
96
|
+
}
|
|
97
|
+
const [command, sub, ...rest] = positionals;
|
|
98
|
+
if (values.help || !command || command === 'help') {
|
|
99
|
+
io.out(HELP);
|
|
100
|
+
return command || values.help ? EXIT_OK : EXIT_USAGE;
|
|
101
|
+
}
|
|
102
|
+
const root = resolve(io.cwd, values.dir ?? '.');
|
|
103
|
+
const nomiDir = join(root, 'nomi');
|
|
104
|
+
const migrationsDir = join(nomiDir, 'migrations');
|
|
105
|
+
const functionsDir = join(nomiDir, 'functions');
|
|
106
|
+
if (COMING_SOON.has(command) || COMING_SOON.has(`${command} ${sub}`)) {
|
|
107
|
+
io.err(`"${[command, sub].filter(Boolean).join(' ')}" needs the hosted Nomi Relay and isn't available in this release yet.`);
|
|
108
|
+
return EXIT_USAGE;
|
|
109
|
+
}
|
|
110
|
+
switch (`${command} ${sub ?? ''}`.trim()) {
|
|
111
|
+
case 'init': {
|
|
112
|
+
await writeNew(join(nomiDir, 'backend.json'), JSON.stringify(BACKEND_JSON, null, 2) + '\n', io);
|
|
113
|
+
const { migrations } = await readMigrations(migrationsDir);
|
|
114
|
+
if (migrations.length === 0)
|
|
115
|
+
await writeNew(join(migrationsDir, '0001_init.sql'), FIRST_MIGRATION, io);
|
|
116
|
+
await mkdir(functionsDir, { recursive: true });
|
|
117
|
+
return EXIT_OK;
|
|
118
|
+
}
|
|
119
|
+
case 'migrate new': {
|
|
120
|
+
const name = rest.join(' ');
|
|
121
|
+
if (!slug(name)) {
|
|
122
|
+
io.err('Usage: nomi-relay migrate new <name>');
|
|
123
|
+
return EXIT_USAGE;
|
|
124
|
+
}
|
|
125
|
+
const { migrations } = await readMigrations(migrationsDir);
|
|
126
|
+
await writeNew(join(migrationsDir, nextMigrationFile(migrations, name)), '', io);
|
|
127
|
+
return EXIT_OK;
|
|
128
|
+
}
|
|
129
|
+
case 'migrate lint': {
|
|
130
|
+
const { migrations, problems } = await readMigrations(migrationsDir);
|
|
131
|
+
for (const problem of problems)
|
|
132
|
+
io.err(`${problem.file}: ${problem.message}`);
|
|
133
|
+
const issues = lintMigrations(migrations.map((migration) => ({ name: migration.file, sql: migration.sql })));
|
|
134
|
+
for (const issue of issues)
|
|
135
|
+
io.err(`${issue.file}:${issue.line} ${issue.rule} ${issue.message}`);
|
|
136
|
+
const count = problems.length + issues.length;
|
|
137
|
+
io.out(count === 0 ? `${migrations.length} migration(s) look good` : `${count} problem(s) found`);
|
|
138
|
+
return count === 0 ? EXIT_OK : EXIT_PROBLEMS;
|
|
139
|
+
}
|
|
140
|
+
case 'migrate status': {
|
|
141
|
+
const { migrations, problems } = await readMigrations(migrationsDir);
|
|
142
|
+
if (migrations.length === 0)
|
|
143
|
+
io.out('no migrations yet: run "nomi-relay migrate new <name>"');
|
|
144
|
+
for (const migration of migrations)
|
|
145
|
+
io.out(`${migration.file} ${migration.checksum.slice(0, SHORT_CHECKSUM_LENGTH)}`);
|
|
146
|
+
for (const problem of problems)
|
|
147
|
+
io.err(`${problem.file}: ${problem.message}`);
|
|
148
|
+
return problems.length === 0 ? EXIT_OK : EXIT_PROBLEMS;
|
|
149
|
+
}
|
|
150
|
+
case 'functions new': {
|
|
151
|
+
const name = slug(rest.join(' ')).replace(/_/g, '-');
|
|
152
|
+
if (!name) {
|
|
153
|
+
io.err('Usage: nomi-relay functions new <name>');
|
|
154
|
+
return EXIT_USAGE;
|
|
155
|
+
}
|
|
156
|
+
const created = await writeNew(join(functionsDir, `${name}.ts`), functionStarter(name), io);
|
|
157
|
+
return created ? EXIT_OK : EXIT_PROBLEMS;
|
|
158
|
+
}
|
|
159
|
+
default:
|
|
160
|
+
io.err(`Unknown command "${positionals.join(' ')}". Run "nomi-relay --help".`);
|
|
161
|
+
return EXIT_USAGE;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
//# sourceMappingURL=run.js.map
|
package/dist/run.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../src/run.ts"],"names":[],"mappings":"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"}
|
package/dist/sql.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Splits SQL into statements on `;`, skipping semicolons inside strings, quoted identifiers,
|
|
3
|
+
* dollar-quoted bodies (`$$ … $$`, `$fn$ … $fn$`) and comments.
|
|
4
|
+
*/
|
|
5
|
+
export function splitStatements(sql) {
|
|
6
|
+
const statements = [];
|
|
7
|
+
let code = '';
|
|
8
|
+
let comments = '';
|
|
9
|
+
let line = 1;
|
|
10
|
+
let startLine = 0;
|
|
11
|
+
let i = 0;
|
|
12
|
+
const flush = () => {
|
|
13
|
+
const normalized = code.replace(/\s+/g, ' ').trim().toLowerCase();
|
|
14
|
+
if (normalized)
|
|
15
|
+
statements.push({ code: normalized, comments, line: startLine || line });
|
|
16
|
+
code = '';
|
|
17
|
+
comments = '';
|
|
18
|
+
startLine = 0;
|
|
19
|
+
};
|
|
20
|
+
const markStart = () => {
|
|
21
|
+
if (!startLine)
|
|
22
|
+
startLine = line;
|
|
23
|
+
};
|
|
24
|
+
while (i < sql.length) {
|
|
25
|
+
const char = sql[i];
|
|
26
|
+
const next = sql[i + 1];
|
|
27
|
+
if (char === '\n')
|
|
28
|
+
line += 1;
|
|
29
|
+
if (char === '-' && next === '-') {
|
|
30
|
+
const end = sql.indexOf('\n', i);
|
|
31
|
+
const stop = end === -1 ? sql.length : end;
|
|
32
|
+
comments += sql.slice(i, stop) + '\n';
|
|
33
|
+
i = stop;
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
if (char === '/' && next === '*') {
|
|
37
|
+
const end = sql.indexOf('*/', i + 2);
|
|
38
|
+
const stop = end === -1 ? sql.length : end + 2;
|
|
39
|
+
const block = sql.slice(i, stop);
|
|
40
|
+
comments += block + '\n';
|
|
41
|
+
line += block.split('\n').length - 1;
|
|
42
|
+
i = stop;
|
|
43
|
+
code += ' ';
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (char === "'" || char === '"') {
|
|
47
|
+
markStart();
|
|
48
|
+
let j = i + 1;
|
|
49
|
+
while (j < sql.length) {
|
|
50
|
+
if (sql[j] === char && sql[j + 1] === char)
|
|
51
|
+
j += 2;
|
|
52
|
+
else if (sql[j] === char)
|
|
53
|
+
break;
|
|
54
|
+
else
|
|
55
|
+
j += 1;
|
|
56
|
+
}
|
|
57
|
+
const quoted = sql.slice(i, j + 1);
|
|
58
|
+
line += quoted.split('\n').length - 1;
|
|
59
|
+
// Keep quoted identifiers (they matter for names); blank out string contents.
|
|
60
|
+
code += char === '"' ? quoted : "''";
|
|
61
|
+
i = j + 1;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
if (char === '$') {
|
|
65
|
+
const tag = /^\$[A-Za-z_]*\$/.exec(sql.slice(i))?.[0];
|
|
66
|
+
if (tag) {
|
|
67
|
+
markStart();
|
|
68
|
+
const end = sql.indexOf(tag, i + tag.length);
|
|
69
|
+
const stop = end === -1 ? sql.length : end + tag.length;
|
|
70
|
+
line += sql.slice(i, stop).split('\n').length - 1;
|
|
71
|
+
code += ' $body$ ';
|
|
72
|
+
i = stop;
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (char === ';') {
|
|
77
|
+
flush();
|
|
78
|
+
i += 1;
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
if (!/\s/.test(char))
|
|
82
|
+
markStart();
|
|
83
|
+
code += char;
|
|
84
|
+
i += 1;
|
|
85
|
+
}
|
|
86
|
+
flush();
|
|
87
|
+
return statements;
|
|
88
|
+
}
|
|
89
|
+
export function unquote(identifier) {
|
|
90
|
+
return identifier.replace(/^"|"$/g, '').toLowerCase();
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=sql.js.map
|
package/dist/sql.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,36 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nomi-relay-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Command line for Nomi Relay: migrations, linting and functions.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/triandamai/nomi-v2.git",
|
|
10
|
+
"directory": "relay/cli"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"nomi",
|
|
14
|
+
"baas",
|
|
15
|
+
"migrations",
|
|
16
|
+
"cli"
|
|
17
|
+
],
|
|
18
|
+
"bin": {
|
|
19
|
+
"nomi-relay": "dist/cli.js"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "rm -rf dist && tsc -p tsconfig.build.json && chmod +x dist/cli.js",
|
|
27
|
+
"check": "tsc -p tsconfig.json --noEmit",
|
|
28
|
+
"test": "vitest run"
|
|
29
|
+
},
|
|
30
|
+
"engines": {
|
|
31
|
+
"node": ">=20"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
}
|
|
36
|
+
}
|