@adminiumjs/meta 0.2.2-rc.0 → 0.2.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/dist/ids.d.ts +6 -0
  2. package/dist/ids.d.ts.map +1 -1
  3. package/dist/ids.js +14 -0
  4. package/dist/ids.js.map +1 -1
  5. package/dist/migrations/0014_public_surface.d.ts +40 -0
  6. package/dist/migrations/0014_public_surface.d.ts.map +1 -0
  7. package/dist/migrations/0014_public_surface.js +186 -0
  8. package/dist/migrations/0014_public_surface.js.map +1 -0
  9. package/dist/migrations/0015_connection_tenant_config.d.ts +42 -0
  10. package/dist/migrations/0015_connection_tenant_config.d.ts.map +1 -0
  11. package/dist/migrations/0015_connection_tenant_config.js +53 -0
  12. package/dist/migrations/0015_connection_tenant_config.js.map +1 -0
  13. package/dist/migrations/0016_audit_entity.d.ts +33 -0
  14. package/dist/migrations/0016_audit_entity.d.ts.map +1 -0
  15. package/dist/migrations/0016_audit_entity.js +137 -0
  16. package/dist/migrations/0016_audit_entity.js.map +1 -0
  17. package/dist/migrations/0017_surface_binding.d.ts +23 -0
  18. package/dist/migrations/0017_surface_binding.d.ts.map +1 -0
  19. package/dist/migrations/0017_surface_binding.js +37 -0
  20. package/dist/migrations/0017_surface_binding.js.map +1 -0
  21. package/dist/migrations/0018_connection_timezone_source.d.ts +44 -0
  22. package/dist/migrations/0018_connection_timezone_source.d.ts.map +1 -0
  23. package/dist/migrations/0018_connection_timezone_source.js +51 -0
  24. package/dist/migrations/0018_connection_timezone_source.js.map +1 -0
  25. package/dist/migrations/0019_connection_disabled.d.ts +38 -0
  26. package/dist/migrations/0019_connection_disabled.d.ts.map +1 -0
  27. package/dist/migrations/0019_connection_disabled.js +43 -0
  28. package/dist/migrations/0019_connection_disabled.js.map +1 -0
  29. package/dist/migrations/0020_manifests_add_on.d.ts +71 -0
  30. package/dist/migrations/0020_manifests_add_on.d.ts.map +1 -0
  31. package/dist/migrations/0020_manifests_add_on.js +111 -0
  32. package/dist/migrations/0020_manifests_add_on.js.map +1 -0
  33. package/dist/migrations/0021_add_on_credentials.d.ts +50 -0
  34. package/dist/migrations/0021_add_on_credentials.d.ts.map +1 -0
  35. package/dist/migrations/0021_add_on_credentials.js +80 -0
  36. package/dist/migrations/0021_add_on_credentials.js.map +1 -0
  37. package/dist/migrations/0022_studio_namespace.d.ts +44 -0
  38. package/dist/migrations/0022_studio_namespace.d.ts.map +1 -0
  39. package/dist/migrations/0022_studio_namespace.js +113 -0
  40. package/dist/migrations/0022_studio_namespace.js.map +1 -0
  41. package/dist/migrations/index.d.ts.map +1 -1
  42. package/dist/migrations/index.js +18 -0
  43. package/dist/migrations/index.js.map +1 -1
  44. package/dist/relocate.d.ts +1 -1
  45. package/dist/relocate.js +1 -1
  46. package/dist/repos/audit.d.ts.map +1 -1
  47. package/dist/repos/audit.js +8 -2
  48. package/dist/repos/audit.js.map +1 -1
  49. package/dist/repos/connections.d.ts +62 -0
  50. package/dist/repos/connections.d.ts.map +1 -1
  51. package/dist/repos/connections.js +104 -0
  52. package/dist/repos/connections.js.map +1 -1
  53. package/dist/repos/index.d.ts +2 -0
  54. package/dist/repos/index.d.ts.map +1 -1
  55. package/dist/repos/index.js +2 -0
  56. package/dist/repos/index.js.map +1 -1
  57. package/dist/repos/manifests.d.ts +139 -0
  58. package/dist/repos/manifests.d.ts.map +1 -0
  59. package/dist/repos/manifests.js +279 -0
  60. package/dist/repos/manifests.js.map +1 -0
  61. package/dist/repos/pages.d.ts +7 -0
  62. package/dist/repos/pages.d.ts.map +1 -1
  63. package/dist/repos/pages.js +39 -8
  64. package/dist/repos/pages.js.map +1 -1
  65. package/dist/repos/public-api.d.ts +140 -0
  66. package/dist/repos/public-api.d.ts.map +1 -0
  67. package/dist/repos/public-api.js +299 -0
  68. package/dist/repos/public-api.js.map +1 -0
  69. package/dist/schema/json-payloads.d.ts +27 -2
  70. package/dist/schema/json-payloads.d.ts.map +1 -1
  71. package/dist/schema/json-payloads.js +41 -2
  72. package/dist/schema/json-payloads.js.map +1 -1
  73. package/dist/schema/settings-registry.d.ts +65 -0
  74. package/dist/schema/settings-registry.d.ts.map +1 -1
  75. package/dist/schema/settings-registry.js +183 -0
  76. package/dist/schema/settings-registry.js.map +1 -1
  77. package/dist/schema/tables.d.ts +177 -1
  78. package/dist/schema/tables.d.ts.map +1 -1
  79. package/dist/schema/tables.js +6 -0
  80. package/dist/schema/tables.js.map +1 -1
  81. package/package.json +1 -1
package/dist/ids.d.ts CHANGED
@@ -44,8 +44,14 @@ export declare const ID_PREFIXES: {
44
44
  readonly whd: "adminium_webhook_deliveries";
45
45
  readonly flag: "adminium_feature_flags";
46
46
  readonly mft: "adminium_manifests";
47
+ readonly mat: "adminium_manifest_attachments";
48
+ readonly aoc: "adminium_add_on_credentials";
47
49
  readonly loc: "adminium_locales";
48
50
  readonly trn: "adminium_translations";
51
+ readonly pbk: "adminium_public_keys";
52
+ readonly psc: "adminium_public_scopes";
53
+ readonly pss: "adminium_public_sessions";
54
+ readonly pch: "adminium_public_challenges";
49
55
  };
50
56
  export type IdPrefix = keyof typeof ID_PREFIXES;
51
57
  /** Maximum column width every id must fit (07-meta-store.md §2.1). */
package/dist/ids.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAQH,QAAA,MAAM,QAAQ,QAAsB,CAAC;AAErC;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA6Bd,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,WAAW,CAAC;AAOhD,sEAAsE;AACtE,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,qBAAa,cAAe,SAAQ,KAAK;IAC9B,IAAI,SAAoB;CAClC;AAiDD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAc9C;AAED,MAAM,WAAW,QAAQ;IACvB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC;IACjB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8FAA8F;AAC9F,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAY5C;AAED,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAQ5D;AAED,yDAAyD;AACzD,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAED,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,CAAC"}
1
+ {"version":3,"file":"ids.d.ts","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;GAWG;AAQH,QAAA,MAAM,QAAQ,QAAsB,CAAC;AAErC;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2Cd,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,MAAM,OAAO,WAAW,CAAC;AAOhD,sEAAsE;AACtE,eAAO,MAAM,aAAa,KAAK,CAAC;AAEhC,qBAAa,cAAe,SAAQ,KAAK;IAC9B,IAAI,SAAoB;CAClC;AAiDD;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,QAAQ,GAAG,MAAM,CAc9C;AAED,MAAM,WAAW,QAAQ;IACvB,qCAAqC;IACrC,MAAM,EAAE,QAAQ,CAAC;IACjB,2DAA2D;IAC3D,KAAK,EAAE,MAAM,CAAC;IACd,6BAA6B;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;CACd;AAED,8FAA8F;AAC9F,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,QAAQ,CAY5C;AAED,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,EAAE,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,QAAQ,GAAG,OAAO,CAQ5D;AAED,yDAAyD;AACzD,wBAAgB,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEzC;AAED,2CAA2C;AAC3C,wBAAgB,aAAa,IAAI,IAAI,CAGpC;AAED,OAAO,EAAE,QAAQ,IAAI,YAAY,EAAE,CAAC"}
package/dist/ids.js CHANGED
@@ -50,8 +50,22 @@ export const ID_PREFIXES = {
50
50
  whd: 'adminium_webhook_deliveries',
51
51
  flag: 'adminium_feature_flags',
52
52
  mft: 'adminium_manifests',
53
+ // 26-add-on-runtime.md §4. `mat` and `aoc` are separate spaces rather than
54
+ // reusing `mft`: an attachment and a credential are addressable rows with
55
+ // their own lifetimes (an attachment is disabled per host, a credential is
56
+ // deleted on disconnect while the manifest stays), and sharing an id space
57
+ // with the manifest they hang off would make a stray id look valid.
58
+ mat: 'adminium_manifest_attachments',
59
+ aoc: 'adminium_add_on_credentials',
53
60
  loc: 'adminium_locales',
54
61
  trn: 'adminium_translations',
62
+ // 28-public-surface.md §3.3 — the public surface. `pbk` is deliberately not
63
+ // `key`: a publishable key is never an RbacPrincipal (28 D3) and sharing an
64
+ // id space with `adminium_api_keys` would blur exactly that boundary.
65
+ pbk: 'adminium_public_keys',
66
+ psc: 'adminium_public_scopes',
67
+ pss: 'adminium_public_sessions',
68
+ pch: 'adminium_public_challenges',
55
69
  };
56
70
  const PREFIX_SET = new Set(Object.keys(ID_PREFIXES));
57
71
  /** `<prefix>_<26 Crockford chars>` — Crockford charset excludes I, L, O, U. */
package/dist/ids.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"ids.js","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ids.js","sourceRoot":"","sources":["../src/ids.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Wave 0014 — the public surface (28-public-surface.md §3).
3
+ *
4
+ * Four tables behind the scoped, off-by-default `/api/v1/public` namespace:
5
+ * the browser-safe key kind, the scope document that says what a key may reach,
6
+ * the end-customer session a `claim` mints, and the one-time challenge the
7
+ * `email-code` claim tier needs.
8
+ *
9
+ * WHY THESE ARE NOT `adminium_api_keys` ROWS WITH A `kind` COLUMN (28 §3.3).
10
+ * Two properties diverge and both are load-bearing:
11
+ *
12
+ * 1. The secret is RE-READABLE by an admin. `adminium_api_keys` reveals once
13
+ * and never again, which is right for a server-side credential a human
14
+ * copies into a deployment. An `adm_pub_` secret lives in a public bundle
15
+ * and has to survive a rebuild months later, so the row keeps a reversible
16
+ * copy alongside the hash.
17
+ * 2. A publishable key is NEVER an `RbacPrincipal` (28 D3). `parseBearerApiKey`
18
+ * gates on the `adm_sk_` prefix, so a token from this table cannot resolve
19
+ * through the rbac plugin at all — which is what makes it inert on every
20
+ * other route BY CONSTRUCTION rather than by an allow-list somebody has to
21
+ * maintain. Sharing a table would invite exactly the `kind`-check-per-route
22
+ * that property exists to avoid.
23
+ *
24
+ * NO SETTINGS TABLE HERE, deliberately (28 D20). Tenant configuration is
25
+ * hybrid: the only key Adminium must own is the `timezone` it needs to
26
+ * interpret its own `timestamptz` values, and that rides on the SCOPE document
27
+ * below — already per-connection, already operator-authored, already served on
28
+ * `/public/config`. A global settings key would be one line cheaper and wrong
29
+ * the moment an instance holds two connections for businesses in different
30
+ * zones. Everything else an app needs (tax rates, tip presets, opening hours)
31
+ * stays in the app's own tables, where the generated dashboard edits it for
32
+ * free.
33
+ *
34
+ * `0013_connection_last_error_hint` is the latest applied migration; never edit
35
+ * a shipped one (the one-time checksum exception in `index.ts` was pre-release).
36
+ */
37
+ import type { Kysely } from 'kysely';
38
+ import type { ColumnHelpers } from '../columns.js';
39
+ export declare function up(db: Kysely<unknown>, c: ColumnHelpers): Promise<void>;
40
+ //# sourceMappingURL=0014_public_surface.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0014_public_surface.d.ts","sourceRoot":"","sources":["../../src/migrations/0014_public_surface.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA6L7E"}
@@ -0,0 +1,186 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ /**
3
+ * Wave 0014 — the public surface (28-public-surface.md §3).
4
+ *
5
+ * Four tables behind the scoped, off-by-default `/api/v1/public` namespace:
6
+ * the browser-safe key kind, the scope document that says what a key may reach,
7
+ * the end-customer session a `claim` mints, and the one-time challenge the
8
+ * `email-code` claim tier needs.
9
+ *
10
+ * WHY THESE ARE NOT `adminium_api_keys` ROWS WITH A `kind` COLUMN (28 §3.3).
11
+ * Two properties diverge and both are load-bearing:
12
+ *
13
+ * 1. The secret is RE-READABLE by an admin. `adminium_api_keys` reveals once
14
+ * and never again, which is right for a server-side credential a human
15
+ * copies into a deployment. An `adm_pub_` secret lives in a public bundle
16
+ * and has to survive a rebuild months later, so the row keeps a reversible
17
+ * copy alongside the hash.
18
+ * 2. A publishable key is NEVER an `RbacPrincipal` (28 D3). `parseBearerApiKey`
19
+ * gates on the `adm_sk_` prefix, so a token from this table cannot resolve
20
+ * through the rbac plugin at all — which is what makes it inert on every
21
+ * other route BY CONSTRUCTION rather than by an allow-list somebody has to
22
+ * maintain. Sharing a table would invite exactly the `kind`-check-per-route
23
+ * that property exists to avoid.
24
+ *
25
+ * NO SETTINGS TABLE HERE, deliberately (28 D20). Tenant configuration is
26
+ * hybrid: the only key Adminium must own is the `timezone` it needs to
27
+ * interpret its own `timestamptz` values, and that rides on the SCOPE document
28
+ * below — already per-connection, already operator-authored, already served on
29
+ * `/public/config`. A global settings key would be one line cheaper and wrong
30
+ * the moment an instance holds two connections for businesses in different
31
+ * zones. Everything else an app needs (tax rates, tip presets, opening hours)
32
+ * stays in the app's own tables, where the generated dashboard edits it for
33
+ * free.
34
+ *
35
+ * `0013_connection_last_error_hint` is the latest applied migration; never edit
36
+ * a shipped one (the one-time checksum exception in `index.ts` was pre-release).
37
+ */
38
+ import { metaTable } from '../prefix.js';
39
+ export async function up(db, c) {
40
+ /*
41
+ * The scope document. One row per (connection, side) the operator publishes.
42
+ *
43
+ * `document` is the compiled-from JSON: resources, their logical refs, the
44
+ * `expose` column allow-list, `filterable`/`searchable`/`orderable`, the
45
+ * mandatory `where` predicate, and the claim strategy. It is validated by
46
+ * `compileScope` on every write and on every boot — never trusted from disk.
47
+ *
48
+ * `timezone` is a COLUMN rather than a field inside `document` because it is
49
+ * the one datum the SERIALIZER needs, not the caller: a reader has to reach it
50
+ * without parsing and validating the whole document first.
51
+ *
52
+ * `proposed_from_manifest` records whether this row was seeded from a
53
+ * manifest's `publicApi.propose` block (O2 option b) or authored from scratch
54
+ * in Studio (option c). It exists so widening past a publisher's proposal can
55
+ * be audited, and it is nullable because option (c) is unblocked today and
56
+ * option (b) is not yet ruled.
57
+ */
58
+ await db.schema
59
+ .createTable(metaTable('public_scopes'))
60
+ .ifNotExists()
61
+ .addColumn('id', c.id, (col) => col.primaryKey())
62
+ .addColumn('connection_id', c.id, (col) => col.notNull())
63
+ .addColumn('side', c.str(16), (col) => col.notNull())
64
+ .addColumn('name', c.str(80), (col) => col.notNull())
65
+ /** IANA zone, e.g. `Europe/London`. Required — see 28 D20. */
66
+ .addColumn('timezone', c.str(64), (col) => col.notNull())
67
+ .addColumn('document', c.json, (col) => col.notNull())
68
+ .addColumn('proposed_from_manifest', c.str(80))
69
+ .addColumn('created_by', c.id)
70
+ .addColumn('created_at', c.ts, (col) => col.notNull())
71
+ .addColumn('updated_at', c.ts, (col) => col.notNull())
72
+ .addForeignKeyConstraint('fk_adminium_public_scopes_connection', ['connection_id'], metaTable('connections'), ['id'], (cb) => cb.onDelete('cascade'))
73
+ .addForeignKeyConstraint('fk_adminium_public_scopes_created_by', ['created_by'], metaTable('users'), ['id'], (cb) => cb.onDelete('set null'))
74
+ .execute();
75
+ await db.schema
76
+ .createIndex('ix_adminium_public_scopes_connection')
77
+ .on(metaTable('public_scopes'))
78
+ .columns(['connection_id'])
79
+ .execute();
80
+ /*
81
+ * The publishable key.
82
+ *
83
+ * `token_hash` is what a request is verified against. `token_encrypted` is the
84
+ * reversible copy that makes the secret re-readable (see the header) — it uses
85
+ * the same `dsnCryptoFromSecret` envelope as connection DSNs, so it is
86
+ * unreadable without `ADMINIUM_SECRET` and it never leaves the server except
87
+ * through an authenticated reveal.
88
+ *
89
+ * `origins` is a JSON array narrowing the instance-wide
90
+ * `ADMINIUM_PUBLIC_API_ORIGINS` allow-list for this one key. Empty array means
91
+ * "no narrowing" — it does NOT mean "any origin", because the env var is
92
+ * always the outer bound.
93
+ *
94
+ * `scope_id` is `restrict` on delete, not `cascade`: deleting a scope out from
95
+ * under a live key would silently turn a working public surface into a 500,
96
+ * so the operator has to revoke the key first and see what they are breaking.
97
+ */
98
+ await db.schema
99
+ .createTable(metaTable('public_keys'))
100
+ .ifNotExists()
101
+ .addColumn('id', c.id, (col) => col.primaryKey())
102
+ .addColumn('name', c.str(80), (col) => col.notNull())
103
+ /** Display fragment, e.g. `adm_pub_4f2a91cd`. */
104
+ .addColumn('prefix', c.str(32), (col) => col.notNull())
105
+ .addColumn('token_hash', c.str(128), (col) => col.notNull())
106
+ .addColumn('token_encrypted', c.text, (col) => col.notNull())
107
+ .addColumn('scope_id', c.id, (col) => col.notNull())
108
+ .addColumn('side', c.str(16), (col) => col.notNull())
109
+ .addColumn('origins', c.json, (col) => col.notNull())
110
+ .addColumn('expires_at', c.ts)
111
+ .addColumn('revoked_at', c.ts)
112
+ .addColumn('last_used_at', c.ts)
113
+ .addColumn('created_by', c.id)
114
+ .addColumn('created_at', c.ts, (col) => col.notNull())
115
+ .addColumn('updated_at', c.ts, (col) => col.notNull())
116
+ .addForeignKeyConstraint('fk_adminium_public_keys_scope', ['scope_id'], metaTable('public_scopes'), ['id'], (cb) => cb.onDelete('restrict'))
117
+ .addForeignKeyConstraint('fk_adminium_public_keys_created_by', ['created_by'], metaTable('users'), ['id'], (cb) => cb.onDelete('set null'))
118
+ .execute();
119
+ /* Lookup is by prefix on every public request — the hot path of this wave. */
120
+ await db.schema
121
+ .createIndex('ix_adminium_public_keys_prefix')
122
+ .on(metaTable('public_keys'))
123
+ .columns(['prefix'])
124
+ .execute();
125
+ /*
126
+ * The end-customer session minted by `POST /public/claim` (28 §3.4).
127
+ *
128
+ * `grants` is the resolved claim — the mandatory predicate later requests are
129
+ * filtered by, e.g. "patient_id = 41". It is stored RESOLVED rather than
130
+ * re-derived per request so that editing a scope cannot retroactively widen a
131
+ * session that is already in someone's browser.
132
+ *
133
+ * There is no `user_id`. An end customer is deliberately NOT an
134
+ * `adminium_users` row (28 §3.4) — Adminium grows no second identity system,
135
+ * and this table is the whole of what a claimed customer is.
136
+ */
137
+ await db.schema
138
+ .createTable(metaTable('public_sessions'))
139
+ .ifNotExists()
140
+ .addColumn('id', c.id, (col) => col.primaryKey())
141
+ .addColumn('key_id', c.id, (col) => col.notNull())
142
+ .addColumn('token_hash', c.str(128), (col) => col.notNull())
143
+ .addColumn('grants', c.json, (col) => col.notNull())
144
+ .addColumn('expires_at', c.ts, (col) => col.notNull())
145
+ .addColumn('created_at', c.ts, (col) => col.notNull())
146
+ .addColumn('last_seen_at', c.ts)
147
+ .addForeignKeyConstraint('fk_adminium_public_sessions_key', ['key_id'], metaTable('public_keys'), ['id'], (cb) => cb.onDelete('cascade'))
148
+ .execute();
149
+ await db.schema
150
+ .createIndex('ix_adminium_public_sessions_token_hash')
151
+ .on(metaTable('public_sessions'))
152
+ .columns(['token_hash'])
153
+ .execute();
154
+ /*
155
+ * One-time challenges for the `email-code` claim tier (28 D11/D17).
156
+ *
157
+ * `attempts` is on the row rather than in a counter elsewhere because the
158
+ * containment property this tier is bought for — a low-entropy code is only
159
+ * safe if guesses are bounded — has to survive a process restart, and the
160
+ * rate limiter is an in-process `Map` that does not.
161
+ *
162
+ * `destination_hash` rather than the address itself: a challenge table is a
163
+ * list of customer email addresses otherwise, which is a needless standing
164
+ * disclosure for a row whose whole life is five minutes.
165
+ */
166
+ await db.schema
167
+ .createTable(metaTable('public_challenges'))
168
+ .ifNotExists()
169
+ .addColumn('id', c.id, (col) => col.primaryKey())
170
+ .addColumn('key_id', c.id, (col) => col.notNull())
171
+ .addColumn('ref', c.str(64), (col) => col.notNull())
172
+ .addColumn('destination_hash', c.str(128), (col) => col.notNull())
173
+ .addColumn('code_hash', c.str(128), (col) => col.notNull())
174
+ .addColumn('attempts', c.int, (col) => col.notNull().defaultTo(0))
175
+ .addColumn('consumed_at', c.ts)
176
+ .addColumn('expires_at', c.ts, (col) => col.notNull())
177
+ .addColumn('created_at', c.ts, (col) => col.notNull())
178
+ .addForeignKeyConstraint('fk_adminium_public_challenges_key', ['key_id'], metaTable('public_keys'), ['id'], (cb) => cb.onDelete('cascade'))
179
+ .execute();
180
+ await db.schema
181
+ .createIndex('ix_adminium_public_challenges_key_ref')
182
+ .on(metaTable('public_challenges'))
183
+ .columns(['key_id', 'ref'])
184
+ .execute();
185
+ }
186
+ //# sourceMappingURL=0014_public_surface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0014_public_surface.js","sourceRoot":"","sources":["../../src/migrations/0014_public_surface.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,EAAmB,EAAE,CAAgB;IAC5D;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;SACvC,WAAW,EAAE;SACb,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;SAChD,SAAS,CAAC,eAAe,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACxD,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACpD,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACrD,8DAA8D;SAC7D,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACxD,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,SAAS,CAAC,wBAAwB,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SAC9C,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC;SAC7B,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,uBAAuB,CACtB,sCAAsC,EACtC,CAAC,eAAe,CAAC,EACjB,SAAS,CAAC,aAAa,CAAC,EACxB,CAAC,IAAI,CAAC,EACN,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAC/B;SACA,uBAAuB,CACtB,sCAAsC,EACtC,CAAC,YAAY,CAAC,EACd,SAAS,CAAC,OAAO,CAAC,EAClB,CAAC,IAAI,CAAC,EACN,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAChC;SACA,OAAO,EAAE,CAAC;IAEb,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,sCAAsC,CAAC;SACnD,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC;SAC9B,OAAO,CAAC,CAAC,eAAe,CAAC,CAAC;SAC1B,OAAO,EAAE,CAAC;IAEb;;;;;;;;;;;;;;;;;OAiBG;IACH,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;SACrC,WAAW,EAAE;SACb,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;SAChD,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACrD,iDAAiD;SAChD,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACtD,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SAC3D,SAAS,CAAC,iBAAiB,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SAC5D,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACnD,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACpD,SAAS,CAAC,SAAS,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACpD,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC;SAC7B,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC;SAC7B,SAAS,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC;SAC/B,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,CAAC;SAC7B,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,uBAAuB,CACtB,+BAA+B,EAC/B,CAAC,UAAU,CAAC,EACZ,SAAS,CAAC,eAAe,CAAC,EAC1B,CAAC,IAAI,CAAC,EACN,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAChC;SACA,uBAAuB,CACtB,oCAAoC,EACpC,CAAC,YAAY,CAAC,EACd,SAAS,CAAC,OAAO,CAAC,EAClB,CAAC,IAAI,CAAC,EACN,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAChC;SACA,OAAO,EAAE,CAAC;IAEb,8EAA8E;IAC9E,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,gCAAgC,CAAC;SAC7C,EAAE,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;SAC5B,OAAO,CAAC,CAAC,QAAQ,CAAC,CAAC;SACnB,OAAO,EAAE,CAAC;IAEb;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;SACzC,WAAW,EAAE;SACb,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;SAChD,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACjD,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SAC3D,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACnD,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,SAAS,CAAC,cAAc,EAAE,CAAC,CAAC,EAAE,CAAC;SAC/B,uBAAuB,CACtB,iCAAiC,EACjC,CAAC,QAAQ,CAAC,EACV,SAAS,CAAC,aAAa,CAAC,EACxB,CAAC,IAAI,CAAC,EACN,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAC/B;SACA,OAAO,EAAE,CAAC;IAEb,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,wCAAwC,CAAC;SACrD,EAAE,CAAC,SAAS,CAAC,iBAAiB,CAAC,CAAC;SAChC,OAAO,CAAC,CAAC,YAAY,CAAC,CAAC;SACvB,OAAO,EAAE,CAAC;IAEb;;;;;;;;;;;OAWG;IACH,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;SAC3C,WAAW,EAAE;SACb,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,UAAU,EAAE,CAAC;SAChD,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACjD,SAAS,CAAC,KAAK,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACnD,SAAS,CAAC,kBAAkB,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACjE,SAAS,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SAC1D,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;SACjE,SAAS,CAAC,aAAa,EAAE,CAAC,CAAC,EAAE,CAAC;SAC9B,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;SACrD,uBAAuB,CACtB,mCAAmC,EACnC,CAAC,QAAQ,CAAC,EACV,SAAS,CAAC,aAAa,CAAC,EACxB,CAAC,IAAI,CAAC,EACN,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,QAAQ,CAAC,SAAS,CAAC,CAC/B;SACA,OAAO,EAAE,CAAC;IAEb,MAAM,EAAE,CAAC,MAAM;SACZ,WAAW,CAAC,uCAAuC,CAAC;SACpD,EAAE,CAAC,SAAS,CAAC,mBAAmB,CAAC,CAAC;SAClC,OAAO,CAAC,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;SAC1B,OAAO,EAAE,CAAC;AACf,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Wave 0015 — `adminium_connections.timezone` / `.currency` (28-T34, D20).
3
+ *
4
+ * ─── Why these are connection columns and not a settings table ──────────────
5
+ *
6
+ * D20 ruled O7 as a hybrid: Adminium owns only what it needs to serve correct
7
+ * data, and it placed `timezone` on the SCOPE document — explicitly rejecting a
8
+ * global `adminium_settings` key, because that is "wrong the moment an instance
9
+ * holds two connections for businesses in different zones".
10
+ *
11
+ * That argument is right, and it points one level further than the ruling took
12
+ * it. The thing a zone belongs to is the BUSINESS, and the business is the
13
+ * database — the connection. A scope is a view onto a connection, and an
14
+ * instance can hold several scopes over one connection; putting the zone on the
15
+ * scope means the same business can be described by two scopes that disagree
16
+ * about what time it is there, with nothing to reconcile them.
17
+ *
18
+ * ─── What forced it ────────────────────────────────────────────────────────
19
+ *
20
+ * A surface hosted BY Adminium reads through the operator's session. It has no
21
+ * publishable key and therefore NO SCOPE, so under the old placement there was
22
+ * nowhere for it to read a zone from at all — the app took it as a build
23
+ * argument, which makes a property of the business a property of the artifact.
24
+ * Hosted mode postdates D20; this is the consequence, not a reversal.
25
+ *
26
+ * ─── What does not change ──────────────────────────────────────────────────
27
+ *
28
+ * `scope.timezone` stays, as an OVERRIDE. It becomes optional and inherits from
29
+ * the connection when omitted, and `compileScope` still refuses a scope that
30
+ * ends up with no valid IANA zone from either source — so D20's boot-fatal
31
+ * guarantee survives intact and every scope already stored keeps working
32
+ * unchanged.
33
+ *
34
+ * Both columns are nullable with no default. There is no defensible default for
35
+ * a timezone: a wrong zone is worse than a missing one, because it looks like
36
+ * data. `null` means "not configured", and the refusal happens where the value
37
+ * is needed rather than being papered over here.
38
+ */
39
+ import type { Kysely } from 'kysely';
40
+ import type { ColumnHelpers } from '../columns.js';
41
+ export declare function up(db: Kysely<unknown>, c: ColumnHelpers): Promise<void>;
42
+ //# sourceMappingURL=0015_connection_tenant_config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0015_connection_tenant_config.d.ts","sourceRoot":"","sources":["../../src/migrations/0015_connection_tenant_config.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAY7E"}
@@ -0,0 +1,53 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ /**
3
+ * Wave 0015 — `adminium_connections.timezone` / `.currency` (28-T34, D20).
4
+ *
5
+ * ─── Why these are connection columns and not a settings table ──────────────
6
+ *
7
+ * D20 ruled O7 as a hybrid: Adminium owns only what it needs to serve correct
8
+ * data, and it placed `timezone` on the SCOPE document — explicitly rejecting a
9
+ * global `adminium_settings` key, because that is "wrong the moment an instance
10
+ * holds two connections for businesses in different zones".
11
+ *
12
+ * That argument is right, and it points one level further than the ruling took
13
+ * it. The thing a zone belongs to is the BUSINESS, and the business is the
14
+ * database — the connection. A scope is a view onto a connection, and an
15
+ * instance can hold several scopes over one connection; putting the zone on the
16
+ * scope means the same business can be described by two scopes that disagree
17
+ * about what time it is there, with nothing to reconcile them.
18
+ *
19
+ * ─── What forced it ────────────────────────────────────────────────────────
20
+ *
21
+ * A surface hosted BY Adminium reads through the operator's session. It has no
22
+ * publishable key and therefore NO SCOPE, so under the old placement there was
23
+ * nowhere for it to read a zone from at all — the app took it as a build
24
+ * argument, which makes a property of the business a property of the artifact.
25
+ * Hosted mode postdates D20; this is the consequence, not a reversal.
26
+ *
27
+ * ─── What does not change ──────────────────────────────────────────────────
28
+ *
29
+ * `scope.timezone` stays, as an OVERRIDE. It becomes optional and inherits from
30
+ * the connection when omitted, and `compileScope` still refuses a scope that
31
+ * ends up with no valid IANA zone from either source — so D20's boot-fatal
32
+ * guarantee survives intact and every scope already stored keeps working
33
+ * unchanged.
34
+ *
35
+ * Both columns are nullable with no default. There is no defensible default for
36
+ * a timezone: a wrong zone is worse than a missing one, because it looks like
37
+ * data. `null` means "not configured", and the refusal happens where the value
38
+ * is needed rather than being papered over here.
39
+ */
40
+ import { metaTable } from '../prefix.js';
41
+ export async function up(db, c) {
42
+ await db.schema
43
+ .alterTable(metaTable('connections'))
44
+ // IANA zone, e.g. `Europe/Lisbon`. 64 matches the scope schema's own cap.
45
+ .addColumn('timezone', c.str(64))
46
+ .execute();
47
+ await db.schema
48
+ .alterTable(metaTable('connections'))
49
+ // ISO-4217, three letters. Null when this business's data carries no money.
50
+ .addColumn('currency', c.str(3))
51
+ .execute();
52
+ }
53
+ //# sourceMappingURL=0015_connection_tenant_config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0015_connection_tenant_config.js","sourceRoot":"","sources":["../../src/migrations/0015_connection_tenant_config.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAKH,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAEzC,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,EAAmB,EAAE,CAAgB;IAC5D,MAAM,EAAE,CAAC,MAAM;SACZ,UAAU,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,0EAA0E;SACzE,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SAChC,OAAO,EAAE,CAAC;IAEb,MAAM,EAAE,CAAC,MAAM;SACZ,UAAU,CAAC,SAAS,CAAC,aAAa,CAAC,CAAC;QACrC,4EAA4E;SAC3E,SAAS,CAAC,UAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;SAC/B,OAAO,EAAE,CAAC;AACf,CAAC"}
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Wave 0016 — per-record audit lookup (30-record-pages.md WS-A).
3
+ *
4
+ * The record page's Activity tab needs "every audit entry for THIS record".
5
+ * The entry's `entity` column stores a RecordRef, but as packed JSON text —
6
+ * filtering on it means either a per-dialect JSON-extract expression (three
7
+ * syntaxes, none of them indexable without generated columns) or a full scan
8
+ * of the `data` category. Both are the amplification anti-pattern D6 exists
9
+ * to rule out, so the ref's two lookup facts are denormalized into real,
10
+ * indexed columns instead:
11
+ *
12
+ * entity_table the ref's qualified table ("public.invoices")
13
+ * entity_id the canonical record-id string — `pkLabel` form: a single
14
+ * PK stringified, a composite PK as the JSON value tuple —
15
+ * the same string the record route and `rowIdOf` use. See
16
+ * `keysOf` for why a BACKFILLED composite reads the ref's
17
+ * `label` rather than re-deriving from `pk`.
18
+ *
19
+ * Both are clamped by `auditEntityKeyPart` (write AND query side, so a
20
+ * truncated key still matches itself), written by `auditRepo.append` from
21
+ * this wave on, and BACKFILLED here from the stored refs so pre-existing
22
+ * activity shows up on day one. The backfill parses each ref in JS — the
23
+ * only portable JSON reader across the three dialects — in id-keyset batches;
24
+ * audit volume is retention-bounded (§8 `audit-log` policy), so this is a
25
+ * one-time bounded cost.
26
+ *
27
+ * Bulk writes (`record.bulk-*`) carry no per-row entity and stay out of the
28
+ * feed by design (30 D6) — their rows simply keep NULL keys.
29
+ */
30
+ import type { Kysely } from 'kysely';
31
+ import type { ColumnHelpers } from '../columns.js';
32
+ export declare function up(db: Kysely<unknown>, c: ColumnHelpers): Promise<void>;
33
+ //# sourceMappingURL=0016_audit_entity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0016_audit_entity.d.ts","sourceRoot":"","sources":["../../src/migrations/0016_audit_entity.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAuEnD,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CA8C7E"}
@@ -0,0 +1,137 @@
1
+ // SPDX-License-Identifier: AGPL-3.0-only
2
+ /**
3
+ * Wave 0016 — per-record audit lookup (30-record-pages.md WS-A).
4
+ *
5
+ * The record page's Activity tab needs "every audit entry for THIS record".
6
+ * The entry's `entity` column stores a RecordRef, but as packed JSON text —
7
+ * filtering on it means either a per-dialect JSON-extract expression (three
8
+ * syntaxes, none of them indexable without generated columns) or a full scan
9
+ * of the `data` category. Both are the amplification anti-pattern D6 exists
10
+ * to rule out, so the ref's two lookup facts are denormalized into real,
11
+ * indexed columns instead:
12
+ *
13
+ * entity_table the ref's qualified table ("public.invoices")
14
+ * entity_id the canonical record-id string — `pkLabel` form: a single
15
+ * PK stringified, a composite PK as the JSON value tuple —
16
+ * the same string the record route and `rowIdOf` use. See
17
+ * `keysOf` for why a BACKFILLED composite reads the ref's
18
+ * `label` rather than re-deriving from `pk`.
19
+ *
20
+ * Both are clamped by `auditEntityKeyPart` (write AND query side, so a
21
+ * truncated key still matches itself), written by `auditRepo.append` from
22
+ * this wave on, and BACKFILLED here from the stored refs so pre-existing
23
+ * activity shows up on day one. The backfill parses each ref in JS — the
24
+ * only portable JSON reader across the three dialects — in id-keyset batches;
25
+ * audit volume is retention-bounded (§8 `audit-log` policy), so this is a
26
+ * one-time bounded cost.
27
+ *
28
+ * Bulk writes (`record.bulk-*`) carry no per-row entity and stay out of the
29
+ * feed by design (30 D6) — their rows simply keep NULL keys.
30
+ */
31
+ import { metaTable } from '../prefix.js';
32
+ import { AUDIT_ENTITY_KEY_MAX, auditEntityKeyOf, auditEntityKeyPart, recordRefSchema, } from '../schema/json-payloads.js';
33
+ const BACKFILL_BATCH = 500;
34
+ /**
35
+ * A STORED ref has lost the one thing `auditEntityKeyOf` reads for a composite
36
+ * key: the order of `pk`.
37
+ *
38
+ * `entity` is `jsonb` on postgres and `json` on mysql, and both normalise
39
+ * object keys on the way in — jsonb by key length then bytes, mysql likewise.
40
+ * So `{invoice_id, line}` comes back as `{line, invoice_id}` and
41
+ * `Object.values()` yields the tuple backwards, producing an id that matches no
42
+ * record. Only sqlite, which stores the column as text, keeps the written
43
+ * order, which is why deriving from `pk` passed there and failed on the other
44
+ * two. There is no recovering the order from the stored value: jsonb and mysql
45
+ * json are parsed forms, so even casting back to text returns the normalised
46
+ * key order rather than the original.
47
+ *
48
+ * A single-column key has no order to lose, so it still derives from `pk`.
49
+ *
50
+ * A composite one falls back to the ref's `label`, which the only writer of a
51
+ * record ref (`routes/data`) sets to `pkLabel(table, pk)` — the same tuple, in
52
+ * the table's primary-key order, as a plain string no store reorders. `label`
53
+ * is display text by contract and NOT guaranteed to be that tuple, so it is
54
+ * used only when it still parses as a JSON array of the right arity; anything
55
+ * else leaves the row NULL. A legacy row that opts out of the feed is the
56
+ * documented outcome for a ref this reader cannot trust — a confidently wrong
57
+ * key that silently attaches one record's history to another is not.
58
+ */
59
+ function keysOf(entity) {
60
+ // Postgres `jsonb` columns come back as objects; sqlite/mysql as text.
61
+ let candidate = entity;
62
+ if (typeof entity === 'string') {
63
+ try {
64
+ candidate = JSON.parse(entity);
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ const parsed = recordRefSchema.safeParse(candidate);
71
+ if (!parsed.success)
72
+ return null;
73
+ const ref = parsed.data;
74
+ const arity = Object.keys(ref.pk).length;
75
+ if (arity <= 1)
76
+ return auditEntityKeyOf(ref);
77
+ let tuple;
78
+ try {
79
+ tuple = JSON.parse(ref.label);
80
+ }
81
+ catch {
82
+ return null;
83
+ }
84
+ if (!Array.isArray(tuple) || tuple.length !== arity)
85
+ return null;
86
+ return { entityTable: auditEntityKeyPart(ref.table), entityId: auditEntityKeyPart(ref.label) };
87
+ }
88
+ export async function up(db, c) {
89
+ await db.schema
90
+ .alterTable(metaTable('audit_log'))
91
+ .addColumn('entity_table', c.str(AUDIT_ENTITY_KEY_MAX))
92
+ .execute();
93
+ await db.schema
94
+ .alterTable(metaTable('audit_log'))
95
+ .addColumn('entity_id', c.str(AUDIT_ENTITY_KEY_MAX))
96
+ .execute();
97
+ // (entity_table, entity_id, created_at): the feed's exact access path —
98
+ // equality on both keys, ordered by recency. 2×200×4 utf8mb4 bytes + the
99
+ // timestamp stays under MySQL's 3072-byte InnoDB index cap.
100
+ await db.schema
101
+ .createIndex('idx_adminium_audit_log_entity_created')
102
+ .on(metaTable('audit_log'))
103
+ .columns(['entity_table', 'entity_id', 'created_at'])
104
+ .execute();
105
+ // Backfill: id-keyset batches over rows that carry a ref. Unparseable JSON
106
+ // stays NULL — a row this reader cannot interpret must not fail the wave.
107
+ const backfill = db;
108
+ const audit = metaTable('audit_log');
109
+ let after = '';
110
+ for (;;) {
111
+ const rows = await backfill
112
+ .selectFrom(audit)
113
+ .select(['id', 'entity'])
114
+ .where('entity', 'is not', null)
115
+ .where('id', '>', after)
116
+ .orderBy('id', 'asc')
117
+ .limit(BACKFILL_BATCH)
118
+ .execute();
119
+ if (rows.length === 0)
120
+ break;
121
+ for (const row of rows) {
122
+ const keys = keysOf(row.entity);
123
+ if (keys === null)
124
+ continue;
125
+ await backfill
126
+ .updateTable(audit)
127
+ .set({ entityTable: keys.entityTable, entityId: keys.entityId })
128
+ .where('id', '=', row.id)
129
+ .execute();
130
+ }
131
+ const last = rows[rows.length - 1];
132
+ if (last === undefined || rows.length < BACKFILL_BATCH)
133
+ break;
134
+ after = last.id;
135
+ }
136
+ }
137
+ //# sourceMappingURL=0016_audit_entity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0016_audit_entity.js","sourceRoot":"","sources":["../../src/migrations/0016_audit_entity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Wave 0017 — key ↔ app-surface binding (29-app-surfaces.md D10, 29-T15).
3
+ *
4
+ * A hosted CUSTOMER surface is configured at serve time, not bake time: the
5
+ * surfaces plugin answers `GET /apps/<key>/customer/surface-config.json` with
6
+ * the newest live publishable key bound to that app, so rotating a key is
7
+ * Studio + reload rather than a rebuild. "Bound to that app" needs a COLUMN —
8
+ * before this wave, "minted per side per app" was a naming convention on
9
+ * `adminium_public_keys.name`, which nothing could query.
10
+ *
11
+ * `app_key` is the manifest key of the owning app (`clients`, `clinic`, …),
12
+ * nullable because binding is optional: a key minted for a standalone build
13
+ * or a third-party integration belongs to no hosted surface and stays NULL.
14
+ *
15
+ * Planned as `0016_surface_binding` (29 D10); renumbered to 0017 because
16
+ * plan 30's `0016_audit_entity` shipped first and the list is append-only.
17
+ */
18
+ import type { Kysely } from 'kysely';
19
+ import type { ColumnHelpers } from '../columns.js';
20
+ /** Matches the discovery layout: a surface directory name, e.g. `clients`. */
21
+ export declare const PUBLIC_KEY_APP_KEY_MAX = 64;
22
+ export declare function up(db: Kysely<unknown>, c: ColumnHelpers): Promise<void>;
23
+ //# sourceMappingURL=0017_surface_binding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"0017_surface_binding.d.ts","sourceRoot":"","sources":["../../src/migrations/0017_surface_binding.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAErC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGnD,8EAA8E;AAC9E,eAAO,MAAM,sBAAsB,KAAK,CAAC;AAEzC,wBAAsB,EAAE,CAAC,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAc7E"}