@sequenceholdings/orm 0.1.1 → 0.1.3

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.
Files changed (81) hide show
  1. package/README.md +12 -2
  2. package/dist/authoring.cjs +1801 -0
  3. package/dist/authoring.cjs.map +1 -0
  4. package/dist/authoring.d.cts +539 -0
  5. package/dist/authoring.d.ts +539 -0
  6. package/dist/authoring.js +1759 -0
  7. package/dist/authoring.js.map +1 -0
  8. package/dist/canonical.cjs +552 -0
  9. package/dist/canonical.cjs.map +1 -0
  10. package/dist/canonical.d.cts +603 -0
  11. package/dist/canonical.d.ts +603 -0
  12. package/dist/canonical.js +532 -0
  13. package/dist/canonical.js.map +1 -0
  14. package/dist/cli.cjs +7843 -1815
  15. package/dist/cli.cjs.map +1 -1
  16. package/dist/cli.d.cts +8 -1
  17. package/dist/cli.d.ts +8 -1
  18. package/dist/cli.js +7854 -1828
  19. package/dist/cli.js.map +1 -1
  20. package/dist/client.cjs +120 -0
  21. package/dist/client.cjs.map +1 -1
  22. package/dist/client.d.cts +74 -2
  23. package/dist/client.d.ts +74 -2
  24. package/dist/client.js +116 -1
  25. package/dist/client.js.map +1 -1
  26. package/dist/compiler.cjs +3453 -0
  27. package/dist/compiler.cjs.map +1 -0
  28. package/dist/compiler.d.cts +389 -0
  29. package/dist/compiler.d.ts +389 -0
  30. package/dist/compiler.js +3396 -0
  31. package/dist/compiler.js.map +1 -0
  32. package/dist/drizzle.cjs +14 -0
  33. package/dist/drizzle.cjs.map +1 -0
  34. package/dist/drizzle.d.cts +1 -0
  35. package/dist/drizzle.d.ts +1 -0
  36. package/dist/drizzle.js +3 -0
  37. package/dist/drizzle.js.map +1 -0
  38. package/dist/dynamic-input.cjs +391 -0
  39. package/dist/dynamic-input.cjs.map +1 -0
  40. package/dist/dynamic-input.d.cts +111 -0
  41. package/dist/dynamic-input.d.ts +111 -0
  42. package/dist/dynamic-input.js +380 -0
  43. package/dist/dynamic-input.js.map +1 -0
  44. package/dist/index.cjs +3727 -344
  45. package/dist/index.cjs.map +1 -1
  46. package/dist/index.d.cts +236 -59
  47. package/dist/index.d.ts +236 -59
  48. package/dist/index.js +3757 -482
  49. package/dist/index.js.map +1 -1
  50. package/dist/ir.cjs +642 -0
  51. package/dist/ir.cjs.map +1 -0
  52. package/dist/ir.d.cts +1119 -0
  53. package/dist/ir.d.ts +1119 -0
  54. package/dist/ir.js +623 -0
  55. package/dist/ir.js.map +1 -0
  56. package/dist/naming-CTvWSRTf.d.ts +79 -0
  57. package/dist/naming-DRyinU4u.d.cts +79 -0
  58. package/dist/predicate-v2.cjs +1093 -0
  59. package/dist/predicate-v2.cjs.map +1 -0
  60. package/dist/predicate-v2.d.cts +139 -0
  61. package/dist/predicate-v2.d.ts +139 -0
  62. package/dist/predicate-v2.js +1083 -0
  63. package/dist/predicate-v2.js.map +1 -0
  64. package/dist/sdl.cjs +1122 -0
  65. package/dist/sdl.cjs.map +1 -0
  66. package/dist/sdl.d.cts +94 -0
  67. package/dist/sdl.d.ts +94 -0
  68. package/dist/sdl.js +1098 -0
  69. package/dist/sdl.js.map +1 -0
  70. package/package.json +51 -3
  71. package/src/codegen-gql/apollo-hooks.cjs +98 -0
  72. package/src/codegen-gql/server-fns.cjs +101 -0
  73. package/templates/README.md +13 -0
  74. package/templates/orm-namespace/CLAUDE.md +82 -0
  75. package/templates/orm-namespace/codegen.ts +22 -0
  76. package/templates/orm-namespace/gitignore +5 -0
  77. package/templates/orm-namespace/graphql/example/queries.ts +56 -0
  78. package/templates/orm-namespace/package.json +24 -0
  79. package/templates/orm-namespace/schema/tables.ts +71 -0
  80. package/templates/orm-namespace/sequence.config.ts +32 -0
  81. package/templates/orm-namespace/tsconfig.json +15 -0
@@ -0,0 +1,82 @@
1
+ # Sequence ORM namespace
2
+
3
+ This directory defines ONE Sequence ORM namespace: TypeScript table
4
+ definitions with row-level policies, plus handwritten GraphQL operations.
5
+ `seq-studio orm apply` pushes it to Atlas, which validates, compiles, and
6
+ applies it. Your app never talks to Postgres — it calls the data plane with
7
+ `{ op: <hash>, variables }` through the generated hooks/functions.
8
+
9
+ ## The loop
10
+
11
+ 1. Edit `schema/*.ts` (tables, columns, links, policies) and/or
12
+ `graphql/**/*.ts` (operations).
13
+ 2. `seq-studio orm generate` — refreshes `schema.graphql` + generated types.
14
+ 3. `seq-studio orm diff` — preview the migration for a schema change.
15
+ 4. `seq-studio orm apply -e <env>` — pushes. Schema changes write a reviewed
16
+ migration file into `migrations/`; operation-only edits take the fast path
17
+ (seconds, no migration).
18
+ 5. Commit `schema/`, `graphql/`, `schema.graphql`, `generated/`, and
19
+ `migrations/` together.
20
+
21
+ ## Hard rules
22
+
23
+ - **Never hand-edit `migrations/`.** Migration files are generated by
24
+ `seq-studio orm apply`/`diff` and replayed verbatim by the server. Editing
25
+ or deleting a committed migration breaks the ledger.
26
+ - **Never hand-edit `schema.graphql` or `generated/`.** Both are outputs of
27
+ `seq-studio orm generate`. To change the API surface, change `schema/*.ts`.
28
+ - **No raw SQL.** There is no query escape hatch: every read/write is a
29
+ GraphQL document in `graphql/`, compiled at push time. For genuinely exotic
30
+ multi-statement writes, declare a table `action` — parameterized, gated,
31
+ audited.
32
+ - **Every table needs row policies.** At minimum declare `read` plus either a
33
+ legacy `write` policy or split `insert` / `update` policies when create and
34
+ resolve are different actors. Policies are the row security boundary and are
35
+ compiled into every operation touching the table. There is no "no policy"
36
+ fallback — an unrestricted table must say so explicitly (`allow: true`
37
+ variant or a `true` policy) so the decision is reviewable.
38
+ - **Exactly one `defineNamespace`** (in `sequence.config.ts`), and every
39
+ table module must call `defineTable` at import time — the CLI collects
40
+ registrations by importing your files.
41
+ - **Renames need hints.** Renaming a column or table without
42
+ `.renamedFrom('old_name')` (column) / `renamedFrom` (table config) is a
43
+ DROP + CREATE — data loss. The diff will refuse ambiguous renames; add the
44
+ hint instead of forcing.
45
+ - **Capabilities/roles are declared here, granted in the Permissions UI.**
46
+ Never hardcode user identities in policies; use `claim('sub')` /
47
+ `claim('org')` and grants.
48
+
49
+ ## Policy DSL quick reference
50
+
51
+ ```
52
+ author_sub = claim('sub') -- row value vs caller claim
53
+ org = claim('org') and status = 'open' -- boolean algebra
54
+ exists(deal, org = claim('org')) -- traverse a declared link
55
+ capability('deals-read') -- boolean grant check
56
+ role('deal-manager') -- boolean role check
57
+ ```
58
+
59
+ Variants widen access per grant (permissive union):
60
+
61
+ ```ts
62
+ policies: {
63
+ read: {
64
+ default: "author_sub = claim('sub')",
65
+ variants: [
66
+ { when: "capability('deals-read')", policy: "org = claim('org')" },
67
+ { when: "role('deal-manager')", allow: true },
68
+ ],
69
+ },
70
+ }
71
+ ```
72
+
73
+ ## GraphQL operations
74
+
75
+ - Named operations only; one export per document; gate with
76
+ `@requires(capability: "…")` / `@requires(role: "…")`.
77
+ - Dynamic filters are policy-bounded: `$where: <Table>BoolExp` /
78
+ `$orderBy` / whole-input write objects are validated against a
79
+ schema-derived whitelist and AND-ed with the table policy at runtime —
80
+ they can never widen access.
81
+ - Joins follow declared links (`toOne` / `toMany` / `toPlatformUser`) — there
82
+ are no ad-hoc joins at query time. Declare the link, then select through it.
@@ -0,0 +1,22 @@
1
+ import type { CodegenConfig } from '@graphql-codegen/cli'
2
+
3
+ /**
4
+ * graphql-codegen config. `seq-studio orm generate` writes ./schema.graphql
5
+ * from your table definitions, then runs this config when present — emitting
6
+ * typed Apollo hooks (`use<Name>Query`) and server functions (`db<Name>`)
7
+ * over the persisted-operation client.
8
+ *
9
+ * schema.graphql and the generated output are committed: reviews see the API
10
+ * surface change alongside the schema change that caused it.
11
+ */
12
+ const config: CodegenConfig = {
13
+ schema: './schema.graphql',
14
+ documents: ['graphql/**/*.ts'],
15
+ generates: {
16
+ './generated/': {
17
+ preset: '@sequenceholdings/orm/codegen',
18
+ },
19
+ },
20
+ }
21
+
22
+ export default config
@@ -0,0 +1,5 @@
1
+ # Commit schema.graphql, migrations/, and generated/ — reviews should see
2
+ # API-surface and DDL changes alongside the schema change that caused them.
3
+ node_modules/
4
+ *.log
5
+ .DS_Store
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Handwritten GraphQL operations. `seq-studio orm apply` compiles every
3
+ * exported document at push time; the wire carries only `{ op: <hash>,
4
+ * variables }` — never query text, never SQL. Type names come from the
5
+ * generated `schema.graphql` (`seq-studio orm generate`).
6
+ *
7
+ * Gate an operation with `@requires(capability: "…")` or
8
+ * `@requires(role: "…")` — checked before execution, on top of the
9
+ * table policies that are always compiled in.
10
+ */
11
+
12
+ export const GetOpenDeals = /* GraphQL */ `
13
+ query GetOpenDeals($status: DealStatus!) @requires(capability: "deals-read") {
14
+ deals(where: { status: { _eq: $status } }, orderBy: [{ createdAt: DESC }], limit: 20) {
15
+ id
16
+ title
17
+ amount
18
+ author {
19
+ name
20
+ email
21
+ }
22
+ comments(limit: 3, orderBy: [{ createdAt: DESC }]) {
23
+ id
24
+ body
25
+ }
26
+ }
27
+ }
28
+ `
29
+
30
+ /**
31
+ * Policy-bounded dynamic arguments (§4f): the filter tree is built by the
32
+ * caller at runtime, validated against the schema-derived whitelist, and
33
+ * always AND-ed with the table's read policy — clients can only narrow
34
+ * within policy-visible rows.
35
+ */
36
+ export const SearchDeals = /* GraphQL */ `
37
+ query SearchDeals($where: DealBoolExp, $orderBy: [DealOrderBy!], $limit: Int)
38
+ @requires(capability: "deals-read") {
39
+ deals(where: $where, orderBy: $orderBy, limit: $limit) {
40
+ id
41
+ title
42
+ status
43
+ amount
44
+ }
45
+ }
46
+ `
47
+
48
+ export const AddComment = /* GraphQL */ `
49
+ mutation AddComment($dealId: uuid!, $body: String!) {
50
+ insertCommentsOne(object: { dealId: $dealId, body: $body }) {
51
+ id
52
+ body
53
+ createdAt
54
+ }
55
+ }
56
+ `
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "my-namespace",
3
+ "private": true,
4
+ "type": "module",
5
+ "scripts": {
6
+ "validate": "seq-studio orm validate",
7
+ "generate": "seq-studio orm generate",
8
+ "diff": "seq-studio orm diff",
9
+ "apply": "seq-studio orm apply",
10
+ "type-check": "tsc --noEmit"
11
+ },
12
+ "dependencies": {
13
+ "@apollo/client": "^3.11.0",
14
+ "@sequenceholdings/orm": "^2.0.0",
15
+ "graphql": "^16.11.0"
16
+ },
17
+ "devDependencies": {
18
+ "@graphql-codegen/cli": "^5.0.7",
19
+ "typescript": "^5.6.0"
20
+ },
21
+ "engines": {
22
+ "node": ">=20"
23
+ }
24
+ }
@@ -0,0 +1,71 @@
1
+ import { defineEnum, defineTable, t, toMany, toOne, toPlatformUser } from '@sequenceholdings/orm'
2
+
3
+ /**
4
+ * Tables are TypeScript, but they SERIALIZE to data — the server never runs
5
+ * this file. Property names are camelCase and become snake_case Postgres
6
+ * columns (`authorSub` → `author_sub`); pass a string to the builder to
7
+ * override (`t.text('legacy_name')`).
8
+ *
9
+ * Every table carries its own row-level policies. The policy DSL:
10
+ * - `claim('sub')` / `claim('org')` — the caller's identity claims
11
+ * - `capability('x')` / `role('y')` — boolean grants, resolved per caller
12
+ * - `exists(link, <predicate>)` — traverse a declared link
13
+ * - variants: `default` applies to everyone; each variant WIDENS access
14
+ * when its `when` guard matches (permissive union — grants only ever
15
+ * widen, never narrow).
16
+ */
17
+ export const dealStatus = defineEnum('deal_status', ['open', 'won', 'lost'])
18
+
19
+ export const deals = defineTable(
20
+ 'deals',
21
+ {
22
+ id: t.uuid().primaryKey().default('gen_random_uuid()'),
23
+ title: t.text().notNull(),
24
+ status: t.enum(dealStatus).notNull().default('open'),
25
+ amount: t.numeric(),
26
+ org: t.text().notNull().description('Organization the deal belongs to'),
27
+ authorSub: t.text().notNull().description('Auth0 sub of the creator'),
28
+ createdAt: t.timestamptz().notNull().default('now()'),
29
+ },
30
+ {
31
+ description: 'Deals owned by their author, readable org-wide with the deals-read capability',
32
+ indexes: [{ columns: ['org'] }, { columns: ['authorSub'] }],
33
+ links: {
34
+ comments: toMany(() => comments, { on: 'dealId' }),
35
+ author: toPlatformUser({ on: 'authorSub' }),
36
+ },
37
+ policies: {
38
+ read: {
39
+ default: "author_sub = claim('sub')",
40
+ variants: [
41
+ { when: "capability('deals-read')", policy: "org = claim('org')" },
42
+ { when: "role('deal-manager')", allow: true },
43
+ ],
44
+ },
45
+ write: "author_sub = claim('sub')",
46
+ },
47
+ },
48
+ )
49
+
50
+ export const comments = defineTable(
51
+ 'comments',
52
+ {
53
+ id: t.uuid().primaryKey().default('gen_random_uuid()'),
54
+ dealId: t.uuid().notNull(),
55
+ body: t.text().notNull(),
56
+ authorSub: t.text().notNull(),
57
+ createdAt: t.timestamptz().notNull().default('now()'),
58
+ },
59
+ {
60
+ description: 'Comments on deals — visibility follows the parent deal',
61
+ indexes: [{ columns: ['dealId'] }],
62
+ links: {
63
+ deal: toOne(() => deals, { on: 'dealId' }),
64
+ author: toPlatformUser({ on: 'authorSub' }),
65
+ },
66
+ policies: {
67
+ read: "exists(deal, org = claim('org'))",
68
+ write: "author_sub = claim('sub')",
69
+ },
70
+ },
71
+ )
@@ -0,0 +1,32 @@
1
+ import { defineNamespace } from '@sequenceholdings/orm'
2
+
3
+ /**
4
+ * The namespace config — exactly one per directory.
5
+ *
6
+ * Capabilities and roles are DECLARED here (verbatim custom-roles grammar);
7
+ * who HOLDS them is granted later in the Permissions UI (FGA), never in code.
8
+ * Table policies reference them with `capability('…')` / `role('…')`.
9
+ */
10
+ export default defineNamespace({
11
+ // Rename me: the namespace becomes your Postgres schema (orm_<namespace>).
12
+ namespace: 'my_namespace',
13
+ description: 'Describe what this namespace stores and who uses it.',
14
+ capabilities: {
15
+ roles: {
16
+ definitions: {
17
+ 'deal-manager': {
18
+ description: 'Full access to every deal in the namespace',
19
+ default: false,
20
+ inherits: [],
21
+ grants: ['deals-read'],
22
+ },
23
+ },
24
+ capabilities: {
25
+ 'deals-read': {
26
+ description: 'Read all deals in the caller org (instead of author-only)',
27
+ },
28
+ },
29
+ },
30
+ uses: [],
31
+ },
32
+ })
@@ -0,0 +1,15 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "lib": ["ES2022", "DOM"],
7
+ "strict": true,
8
+ "noEmit": true,
9
+ "esModuleInterop": true,
10
+ "skipLibCheck": true,
11
+ "resolveJsonModule": true,
12
+ "isolatedModules": true
13
+ },
14
+ "include": ["sequence.config.ts", "codegen.ts", "schema", "graphql", "generated"]
15
+ }