@geekmidas/db 1.1.1 → 10.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (66) hide show
  1. package/dist/errors-CfFCan-X.mjs +69 -0
  2. package/dist/errors-CfFCan-X.mjs.map +1 -0
  3. package/dist/errors-Cs5jlAv-.cjs +93 -0
  4. package/dist/errors-Cs5jlAv-.cjs.map +1 -0
  5. package/dist/errors.cjs +6 -0
  6. package/dist/errors.d.cts +53 -0
  7. package/dist/errors.d.cts.map +1 -0
  8. package/dist/errors.d.mts +53 -0
  9. package/dist/errors.d.mts.map +1 -0
  10. package/dist/errors.mjs +3 -0
  11. package/dist/kysely/pagination.cjs +1 -1
  12. package/dist/kysely/pagination.d.mts +1 -1
  13. package/dist/kysely/pagination.mjs +1 -1
  14. package/dist/{kysely-Dm1w5gAY.d.mts → kysely-BAedYsvn.d.mts} +1 -1
  15. package/dist/{kysely-Dm1w5gAY.d.mts.map → kysely-BAedYsvn.d.mts.map} +1 -1
  16. package/dist/{kysely-DmfA94RY.mjs → kysely-Bo_Zg-D3.mjs} +1 -1
  17. package/dist/{kysely-DmfA94RY.mjs.map → kysely-Bo_Zg-D3.mjs.map} +1 -1
  18. package/dist/{kysely-8WPSKCZG.cjs → kysely-LWvS2ekY.cjs} +1 -1
  19. package/dist/{kysely-8WPSKCZG.cjs.map → kysely-LWvS2ekY.cjs.map} +1 -1
  20. package/dist/kysely.cjs +1 -1
  21. package/dist/kysely.d.mts +1 -1
  22. package/dist/kysely.mjs +1 -1
  23. package/dist/objection/pagination.cjs +1 -1
  24. package/dist/objection/pagination.d.mts +1 -1
  25. package/dist/objection/pagination.mjs +1 -1
  26. package/dist/{pagination-Bdoa4PVj.cjs → pagination-Bu1dqOy0.cjs} +1 -1
  27. package/dist/{pagination-Bdoa4PVj.cjs.map → pagination-Bu1dqOy0.cjs.map} +1 -1
  28. package/dist/{pagination-BDLa7Yb_.mjs → pagination-CQ8QHXaL.mjs} +1 -1
  29. package/dist/{pagination-BDLa7Yb_.mjs.map → pagination-CQ8QHXaL.mjs.map} +1 -1
  30. package/dist/{pagination-BziGl-B8.d.mts → pagination-kv-KWS-7.d.mts} +1 -1
  31. package/dist/{pagination-BziGl-B8.d.mts.map → pagination-kv-KWS-7.d.mts.map} +1 -1
  32. package/dist/pagination.cjs +1 -1
  33. package/dist/pagination.d.mts +1 -1
  34. package/dist/pagination.mjs +1 -1
  35. package/dist/pg/roles.cjs +154 -0
  36. package/dist/pg/roles.cjs.map +1 -0
  37. package/dist/pg/roles.d.cts +82 -0
  38. package/dist/pg/roles.d.cts.map +1 -0
  39. package/dist/pg/roles.d.mts +82 -0
  40. package/dist/pg/roles.d.mts.map +1 -0
  41. package/dist/pg/roles.mjs +150 -0
  42. package/dist/pg/roles.mjs.map +1 -0
  43. package/dist/pg/url.cjs +33 -0
  44. package/dist/pg/url.cjs.map +1 -0
  45. package/dist/pg/url.d.cts +47 -0
  46. package/dist/pg/url.d.cts.map +1 -0
  47. package/dist/pg/url.d.mts +47 -0
  48. package/dist/pg/url.d.mts.map +1 -0
  49. package/dist/pg/url.mjs +33 -0
  50. package/dist/pg/url.mjs.map +1 -0
  51. package/dist/rls.cjs +1 -1
  52. package/dist/rls.d.mts +1 -1
  53. package/dist/rls.mjs +1 -1
  54. package/package.json +34 -1
  55. package/CHANGELOG.md +0 -47
  56. package/src/__tests__/kysely.integration.spec.ts +0 -741
  57. package/src/__tests__/kysely.spec.ts +0 -394
  58. package/src/__tests__/rls.spec.ts +0 -716
  59. package/src/kysely/__tests__/pagination.integration.spec.ts +0 -341
  60. package/src/kysely/pagination.ts +0 -113
  61. package/src/kysely.ts +0 -33
  62. package/src/objection/__tests__/pagination.integration.spec.ts +0 -291
  63. package/src/objection/pagination.ts +0 -98
  64. package/src/pagination.ts +0 -49
  65. package/src/rls.ts +0 -90
  66. package/tsconfig.json +0 -9
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.cjs","names":["spec: RoleSpec","reader: string","role: string","runtime: string","name: string","value: string"],"sources":["../../src/pg/roles.ts"],"sourcesContent":["/**\n * The role DDL — an owner, a runtime role, and the grants between them.\n *\n * Generation only. The same statements have to run in-process from `gkm dev`\n * and from a provisioner inside a VPC at deploy, and one implementation is what\n * stops \"works locally, fails deployed\" — so this composes SQL and applies\n * none of it.\n *\n * **What the split is for.** A handler connects as the runtime role, which can\n * read and write rows and cannot create, alter, or drop anything. A migrator\n * connects as the owner, which can. That is a security property rather than a\n * convention — a compromised handler cannot `DROP TABLE` because its role holds\n * no such grant — and it is only a property at all once these statements have\n * actually run. Until then a schema is a namespace, not a boundary.\n *\n * **Passwords are an input.** This module invents no secrets: a caller derives\n * or fetches them and passes them in, because where a password comes from is\n * the one part that legitimately differs between a laptop and a deploy.\n */\n\n/** The roles and schema one tenant needs. */\nexport interface RoleSpec {\n\t/** The role a handler connects as. Reads and writes rows; owns nothing. */\n\truntime: string;\n\t/** The role a migrator connects as. Owns the schema and everything in it. */\n\towner: string;\n\t/**\n\t * A third role that may only read, for a reader endpoint.\n\t *\n\t * Optional because it is only worth creating where something points at it.\n\t * Read-only is enforced *here*, by the grants, rather than by which endpoint\n\t * a URL happens to name — which is what makes falling back to the writer's\n\t * endpoint safe when a cluster has no replica.\n\t */\n\treader?: string;\n\t/** The schema they operate in, pinned on every role's `search_path`. */\n\tschema: string;\n\t/** Supplied, never generated here. */\n\tpasswords: { runtime: string; owner: string; reader?: string };\n}\n\n/** One statement, and how to tell whether it still needs running. */\nexport interface RoleStatement {\n\t/** What it does, for the line a reconciler prints. */\n\tdescribe: string;\n\t/**\n\t * Answers \"does this already exist\", parameterised.\n\t *\n\t * Absent means the statement is idempotent in itself and is run every time —\n\t * a `GRANT` re-granting what is already granted is a no-op, and checking\n\t * would cost a round trip to learn nothing.\n\t */\n\texists?: { sql: string; values: unknown[] };\n\tsql: string;\n}\n\n/**\n * Everything one tenant's roles need, in dependency order.\n *\n * The owner before the schema it owns, the schema before the grants on it. A\n * caller applies them in sequence and may stop at the first failure without\n * leaving a half-granted role that looks complete.\n */\nexport function roleStatements(spec: RoleSpec): RoleStatement[] {\n\tconst { runtime, owner, schema, passwords } = spec;\n\n\treturn [\n\t\t{\n\t\t\tdescribe: `role ${owner}`,\n\t\t\texists: roleExists(owner),\n\t\t\tsql: `CREATE ROLE ${ident(owner)} LOGIN PASSWORD ${literal(passwords.owner)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `role ${runtime}`,\n\t\t\texists: roleExists(runtime),\n\t\t\tsql: `CREATE ROLE ${ident(runtime)} LOGIN PASSWORD ${literal(passwords.runtime)}`,\n\t\t},\n\t\t{\n\t\t\t// Postgres will not let you create an object owned by a role you are\n\t\t\t// not a member of, and an RDS master is not a superuser — so\n\t\t\t// `CREATE SCHEMA … AUTHORIZATION` fails with \"must be able to SET\n\t\t\t// ROLE\" unless the connected role is granted membership first.\n\t\t\t//\n\t\t\t// Granting is idempotent and needs no check. It also leaves the master\n\t\t\t// able to administer the schema, which is what you want from the one\n\t\t\t// credential that exists before any role does.\n\t\t\tdescribe: `${owner} granted to the connecting role`,\n\t\t\tsql: `GRANT ${ident(owner)} TO CURRENT_USER`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `schema ${schema}`,\n\t\t\texists: {\n\t\t\t\tsql: 'SELECT 1 FROM information_schema.schemata WHERE schema_name = $1',\n\t\t\t\tvalues: [schema],\n\t\t\t},\n\t\t\t// Owned by the owner from the moment it exists. Creating it as the\n\t\t\t// cluster master and granting afterwards leaves a window where the\n\t\t\t// master owns objects the owner is supposed to.\n\t\t\tsql: `CREATE SCHEMA ${ident(schema)} AUTHORIZATION ${ident(owner)}`,\n\t\t},\n\t\t{\n\t\t\t// And the schema that was already there, which the guard above skips.\n\t\t\t// A schema created before its roles existed is owned by whoever\n\t\t\t// created it — the cluster master — and nothing below fixes that, so\n\t\t\t// the owner cannot create in the schema it is supposed to own. The\n\t\t\t// failure is `no schema has been selected to create in`, which names\n\t\t\t// neither the schema nor the owner and reads like a missing\n\t\t\t// `search_path`.\n\t\t\t//\n\t\t\t// Idempotent when the owner is already right, so it costs a statement\n\t\t\t// rather than a check.\n\t\t\tdescribe: `${schema} is owned by ${owner}`,\n\t\t\tsql: `ALTER SCHEMA ${ident(schema)} OWNER TO ${ident(owner)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${runtime} may use ${schema}`,\n\t\t\tsql: `GRANT USAGE ON SCHEMA ${ident(schema)} TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\t// The important half: *future* tables. A grant on what exists today\n\t\t\t// covers nothing the next migration creates, which is how a runtime\n\t\t\t// role silently loses access one deploy later.\n\t\t\tdescribe: `${runtime} may read and write future tables in ${schema}`,\n\t\t\tsql:\n\t\t\t\t`ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} ` +\n\t\t\t\t`GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${runtime} may use future sequences in ${schema}`,\n\t\t\tsql:\n\t\t\t\t`ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} ` +\n\t\t\t\t`GRANT USAGE, SELECT ON SEQUENCES TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\t// And the tables that are already there, for a schema that existed\n\t\t\t// before its roles did.\n\t\t\tdescribe: `${runtime} may read and write existing tables in ${schema}`,\n\t\t\tsql:\n\t\t\t\t`GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA ${ident(schema)} ` +\n\t\t\t\t`TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${runtime} may use existing sequences in ${schema}`,\n\t\t\tsql: `GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA ${ident(schema)} TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\t// Pinned on the role rather than carried in every URL. A connection\n\t\t\t// string that has to remember `search_path` is one that eventually\n\t\t\t// forgets, and the forgetting looks like an empty database.\n\t\t\tdescribe: `${runtime} resolves names in ${schema}`,\n\t\t\tsql: `ALTER ROLE ${ident(runtime)} SET search_path TO ${ident(schema)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${owner} resolves names in ${schema}`,\n\t\t\tsql: `ALTER ROLE ${ident(owner)} SET search_path TO ${ident(schema)}`,\n\t\t},\n\t\t...(spec.reader ? readerStatements(spec, spec.reader) : []),\n\t];\n}\n\n/**\n * A role that may read and nothing else.\n *\n * `SELECT` and no more, on what exists and on what the owner creates later. No\n * `INSERT`, no `UPDATE`, no `DELETE`, and no sequence `USAGE` — a reader that\n * could take a sequence value could not write the row it was for, so granting\n * it would be surface with no use.\n */\nfunction readerStatements(spec: RoleSpec, reader: string): RoleStatement[] {\n\tconst { owner, schema, passwords } = spec;\n\tconst password = passwords.reader;\n\n\tif (!password) throw new ReaderNeedsPassword(reader);\n\n\treturn [\n\t\t{\n\t\t\tdescribe: `role ${reader}`,\n\t\t\texists: roleExists(reader),\n\t\t\tsql: `CREATE ROLE ${ident(reader)} LOGIN PASSWORD ${literal(password)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${reader} may use ${schema}`,\n\t\t\tsql: `GRANT USAGE ON SCHEMA ${ident(schema)} TO ${ident(reader)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${reader} may read future tables in ${schema}`,\n\t\t\tsql:\n\t\t\t\t`ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} ` +\n\t\t\t\t`GRANT SELECT ON TABLES TO ${ident(reader)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${reader} may read existing tables in ${schema}`,\n\t\t\tsql: `GRANT SELECT ON ALL TABLES IN SCHEMA ${ident(schema)} TO ${ident(reader)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${reader} resolves names in ${schema}`,\n\t\t\tsql: `ALTER ROLE ${ident(reader)} SET search_path TO ${ident(schema)}`,\n\t\t},\n\t];\n}\n\n/** A reader role was named and no password was supplied for it. */\nexport class ReaderNeedsPassword extends Error {\n\tconstructor(readonly role: string) {\n\t\tsuper(\n\t\t\t`'${role}' is a reader role and no password was supplied for it. ` +\n\t\t\t\t`This module composes DDL and invents no secrets: pass ` +\n\t\t\t\t`passwords.reader alongside the role name.`,\n\t\t);\n\t\tthis.name = 'ReaderNeedsPassword';\n\t}\n}\n\n/** The read-only role's name for a given runtime role. */\nexport function readerRole(runtime: string): string {\n\treturn `${runtime}_reader`;\n}\n\n/** The owner role's name for a given runtime role. */\nexport function ownerRole(runtime: string): string {\n\treturn `${runtime}_owner`;\n}\n\nfunction roleExists(role: string): { sql: string; values: unknown[] } {\n\treturn { sql: 'SELECT 1 FROM pg_roles WHERE rolname = $1', values: [role] };\n}\n\n/**\n * Quote an identifier for use in DDL.\n *\n * Names reaching here are already canonical, so this is defence rather than\n * sanitisation — but DDL cannot be parameterised, and an unquoted identifier is\n * the one place a name could ever be more than a name.\n */\nfunction ident(name: string): string {\n\treturn `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Quote a string literal.\n *\n * A password cannot be a bind parameter in `CREATE ROLE`, which is the one\n * place in this module where a value rather than a name is interpolated — so it\n * is escaped rather than trusted, whatever the caller believes it derived.\n */\nfunction literal(value: string): string {\n\treturn `'${value.replace(/'/g, \"''\")}'`;\n}\n"],"mappings":";;;;;;;;;AA+DA,SAAgB,eAAeA,MAAiC;CAC/D,MAAM,EAAE,SAAS,OAAO,QAAQ,WAAW,GAAG;AAE9C,QAAO;EACN;GACC,WAAW,OAAO,MAAM;GACxB,QAAQ,WAAW,MAAM;GACzB,MAAM,cAAc,MAAM,MAAM,CAAC,kBAAkB,QAAQ,UAAU,MAAM,CAAC;EAC5E;EACD;GACC,WAAW,OAAO,QAAQ;GAC1B,QAAQ,WAAW,QAAQ;GAC3B,MAAM,cAAc,MAAM,QAAQ,CAAC,kBAAkB,QAAQ,UAAU,QAAQ,CAAC;EAChF;EACD;GASC,WAAW,EAAE,MAAM;GACnB,MAAM,QAAQ,MAAM,MAAM,CAAC;EAC3B;EACD;GACC,WAAW,SAAS,OAAO;GAC3B,QAAQ;IACP,KAAK;IACL,QAAQ,CAAC,MAAO;GAChB;GAID,MAAM,gBAAgB,MAAM,OAAO,CAAC,iBAAiB,MAAM,MAAM,CAAC;EAClE;EACD;GAWC,WAAW,EAAE,OAAO,eAAe,MAAM;GACzC,MAAM,eAAe,MAAM,OAAO,CAAC,YAAY,MAAM,MAAM,CAAC;EAC5D;EACD;GACC,WAAW,EAAE,QAAQ,WAAW,OAAO;GACvC,MAAM,wBAAwB,MAAM,OAAO,CAAC,MAAM,MAAM,QAAQ,CAAC;EACjE;EACD;GAIC,WAAW,EAAE,QAAQ,uCAAuC,OAAO;GACnE,MACE,oCAAoC,MAAM,MAAM,CAAC,aAAa,MAAM,OAAO,CAAC,qDACxB,MAAM,QAAQ,CAAC;EACrE;EACD;GACC,WAAW,EAAE,QAAQ,+BAA+B,OAAO;GAC3D,MACE,oCAAoC,MAAM,MAAM,CAAC,aAAa,MAAM,OAAO,CAAC,uCACtC,MAAM,QAAQ,CAAC;EACvD;EACD;GAGC,WAAW,EAAE,QAAQ,yCAAyC,OAAO;GACrE,MACE,+DAA+D,MAAM,OAAO,CAAC,MACxE,MAAM,QAAQ,CAAC;EACtB;EACD;GACC,WAAW,EAAE,QAAQ,iCAAiC,OAAO;GAC7D,MAAM,iDAAiD,MAAM,OAAO,CAAC,MAAM,MAAM,QAAQ,CAAC;EAC1F;EACD;GAIC,WAAW,EAAE,QAAQ,qBAAqB,OAAO;GACjD,MAAM,aAAa,MAAM,QAAQ,CAAC,sBAAsB,MAAM,OAAO,CAAC;EACtE;EACD;GACC,WAAW,EAAE,MAAM,qBAAqB,OAAO;GAC/C,MAAM,aAAa,MAAM,MAAM,CAAC,sBAAsB,MAAM,OAAO,CAAC;EACpE;EACD,GAAI,KAAK,SAAS,iBAAiB,MAAM,KAAK,OAAO,GAAG,CAAE;CAC1D;AACD;;;;;;;;;AAUD,SAAS,iBAAiBA,MAAgBC,QAAiC;CAC1E,MAAM,EAAE,OAAO,QAAQ,WAAW,GAAG;CACrC,MAAM,WAAW,UAAU;AAE3B,MAAK,SAAU,OAAM,IAAI,oBAAoB;AAE7C,QAAO;EACN;GACC,WAAW,OAAO,OAAO;GACzB,QAAQ,WAAW,OAAO;GAC1B,MAAM,cAAc,MAAM,OAAO,CAAC,kBAAkB,QAAQ,SAAS,CAAC;EACtE;EACD;GACC,WAAW,EAAE,OAAO,WAAW,OAAO;GACtC,MAAM,wBAAwB,MAAM,OAAO,CAAC,MAAM,MAAM,OAAO,CAAC;EAChE;EACD;GACC,WAAW,EAAE,OAAO,6BAA6B,OAAO;GACxD,MACE,oCAAoC,MAAM,MAAM,CAAC,aAAa,MAAM,OAAO,CAAC,6BAChD,MAAM,OAAO,CAAC;EAC5C;EACD;GACC,WAAW,EAAE,OAAO,+BAA+B,OAAO;GAC1D,MAAM,uCAAuC,MAAM,OAAO,CAAC,MAAM,MAAM,OAAO,CAAC;EAC/E;EACD;GACC,WAAW,EAAE,OAAO,qBAAqB,OAAO;GAChD,MAAM,aAAa,MAAM,OAAO,CAAC,sBAAsB,MAAM,OAAO,CAAC;EACrE;CACD;AACD;;AAGD,IAAa,sBAAb,cAAyC,MAAM;CAC9C,YAAqBC,MAAc;AAClC,SACE,GAAG,KAAK,yJAGT;EALmB;AAMpB,OAAK,OAAO;CACZ;AACD;;AAGD,SAAgB,WAAWC,SAAyB;AACnD,SAAQ,EAAE,QAAQ;AAClB;;AAGD,SAAgB,UAAUA,SAAyB;AAClD,SAAQ,EAAE,QAAQ;AAClB;AAED,SAAS,WAAWD,MAAkD;AACrE,QAAO;EAAE,KAAK;EAA6C,QAAQ,CAAC,IAAK;CAAE;AAC3E;;;;;;;;AASD,SAAS,MAAME,MAAsB;AACpC,SAAQ,GAAG,KAAK,QAAQ,MAAM,OAAK,CAAC;AACpC;;;;;;;;AASD,SAAS,QAAQC,OAAuB;AACvC,SAAQ,GAAG,MAAM,QAAQ,MAAM,KAAK,CAAC;AACrC"}
@@ -0,0 +1,82 @@
1
+ //#region src/pg/roles.d.ts
2
+ /**
3
+ * The role DDL — an owner, a runtime role, and the grants between them.
4
+ *
5
+ * Generation only. The same statements have to run in-process from `gkm dev`
6
+ * and from a provisioner inside a VPC at deploy, and one implementation is what
7
+ * stops "works locally, fails deployed" — so this composes SQL and applies
8
+ * none of it.
9
+ *
10
+ * **What the split is for.** A handler connects as the runtime role, which can
11
+ * read and write rows and cannot create, alter, or drop anything. A migrator
12
+ * connects as the owner, which can. That is a security property rather than a
13
+ * convention — a compromised handler cannot `DROP TABLE` because its role holds
14
+ * no such grant — and it is only a property at all once these statements have
15
+ * actually run. Until then a schema is a namespace, not a boundary.
16
+ *
17
+ * **Passwords are an input.** This module invents no secrets: a caller derives
18
+ * or fetches them and passes them in, because where a password comes from is
19
+ * the one part that legitimately differs between a laptop and a deploy.
20
+ */
21
+ /** The roles and schema one tenant needs. */
22
+ interface RoleSpec {
23
+ /** The role a handler connects as. Reads and writes rows; owns nothing. */
24
+ runtime: string;
25
+ /** The role a migrator connects as. Owns the schema and everything in it. */
26
+ owner: string;
27
+ /**
28
+ * A third role that may only read, for a reader endpoint.
29
+ *
30
+ * Optional because it is only worth creating where something points at it.
31
+ * Read-only is enforced *here*, by the grants, rather than by which endpoint
32
+ * a URL happens to name — which is what makes falling back to the writer's
33
+ * endpoint safe when a cluster has no replica.
34
+ */
35
+ reader?: string;
36
+ /** The schema they operate in, pinned on every role's `search_path`. */
37
+ schema: string;
38
+ /** Supplied, never generated here. */
39
+ passwords: {
40
+ runtime: string;
41
+ owner: string;
42
+ reader?: string;
43
+ };
44
+ }
45
+ /** One statement, and how to tell whether it still needs running. */
46
+ interface RoleStatement {
47
+ /** What it does, for the line a reconciler prints. */
48
+ describe: string;
49
+ /**
50
+ * Answers "does this already exist", parameterised.
51
+ *
52
+ * Absent means the statement is idempotent in itself and is run every time —
53
+ * a `GRANT` re-granting what is already granted is a no-op, and checking
54
+ * would cost a round trip to learn nothing.
55
+ */
56
+ exists?: {
57
+ sql: string;
58
+ values: unknown[];
59
+ };
60
+ sql: string;
61
+ }
62
+ /**
63
+ * Everything one tenant's roles need, in dependency order.
64
+ *
65
+ * The owner before the schema it owns, the schema before the grants on it. A
66
+ * caller applies them in sequence and may stop at the first failure without
67
+ * leaving a half-granted role that looks complete.
68
+ */
69
+ declare function roleStatements(spec: RoleSpec): RoleStatement[];
70
+ /** A reader role was named and no password was supplied for it. */
71
+ declare class ReaderNeedsPassword extends Error {
72
+ readonly role: string;
73
+ constructor(role: string);
74
+ }
75
+ /** The read-only role's name for a given runtime role. */
76
+ declare function readerRole(runtime: string): string;
77
+ /** The owner role's name for a given runtime role. */
78
+ declare function ownerRole(runtime: string): string;
79
+ //# sourceMappingURL=roles.d.ts.map
80
+ //#endregion
81
+ export { ReaderNeedsPassword, RoleSpec, RoleStatement, ownerRole, readerRole, roleStatements };
82
+ //# sourceMappingURL=roles.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.d.cts","names":[],"sources":["../../src/pg/roles.ts"],"sourcesContent":[],"mappings":";;AAqBA;AAqBA;AAqBA;;;;AAA6D;AA2I7D;AAYA;AAKA;;;;;;;;;;UAtMiB,QAAA;;;;;;;;;;;;;;;;;;;;;;;;UAqBA,aAAA;;;;;;;;;;;;;;;;;;;;;;;iBAqBD,cAAA,OAAqB,WAAW;;cA2InC,mBAAA,SAA4B,KAAK;;;;;iBAY9B,UAAA;;iBAKA,SAAA"}
@@ -0,0 +1,82 @@
1
+ //#region src/pg/roles.d.ts
2
+ /**
3
+ * The role DDL — an owner, a runtime role, and the grants between them.
4
+ *
5
+ * Generation only. The same statements have to run in-process from `gkm dev`
6
+ * and from a provisioner inside a VPC at deploy, and one implementation is what
7
+ * stops "works locally, fails deployed" — so this composes SQL and applies
8
+ * none of it.
9
+ *
10
+ * **What the split is for.** A handler connects as the runtime role, which can
11
+ * read and write rows and cannot create, alter, or drop anything. A migrator
12
+ * connects as the owner, which can. That is a security property rather than a
13
+ * convention — a compromised handler cannot `DROP TABLE` because its role holds
14
+ * no such grant — and it is only a property at all once these statements have
15
+ * actually run. Until then a schema is a namespace, not a boundary.
16
+ *
17
+ * **Passwords are an input.** This module invents no secrets: a caller derives
18
+ * or fetches them and passes them in, because where a password comes from is
19
+ * the one part that legitimately differs between a laptop and a deploy.
20
+ */
21
+ /** The roles and schema one tenant needs. */
22
+ interface RoleSpec {
23
+ /** The role a handler connects as. Reads and writes rows; owns nothing. */
24
+ runtime: string;
25
+ /** The role a migrator connects as. Owns the schema and everything in it. */
26
+ owner: string;
27
+ /**
28
+ * A third role that may only read, for a reader endpoint.
29
+ *
30
+ * Optional because it is only worth creating where something points at it.
31
+ * Read-only is enforced *here*, by the grants, rather than by which endpoint
32
+ * a URL happens to name — which is what makes falling back to the writer's
33
+ * endpoint safe when a cluster has no replica.
34
+ */
35
+ reader?: string;
36
+ /** The schema they operate in, pinned on every role's `search_path`. */
37
+ schema: string;
38
+ /** Supplied, never generated here. */
39
+ passwords: {
40
+ runtime: string;
41
+ owner: string;
42
+ reader?: string;
43
+ };
44
+ }
45
+ /** One statement, and how to tell whether it still needs running. */
46
+ interface RoleStatement {
47
+ /** What it does, for the line a reconciler prints. */
48
+ describe: string;
49
+ /**
50
+ * Answers "does this already exist", parameterised.
51
+ *
52
+ * Absent means the statement is idempotent in itself and is run every time —
53
+ * a `GRANT` re-granting what is already granted is a no-op, and checking
54
+ * would cost a round trip to learn nothing.
55
+ */
56
+ exists?: {
57
+ sql: string;
58
+ values: unknown[];
59
+ };
60
+ sql: string;
61
+ }
62
+ /**
63
+ * Everything one tenant's roles need, in dependency order.
64
+ *
65
+ * The owner before the schema it owns, the schema before the grants on it. A
66
+ * caller applies them in sequence and may stop at the first failure without
67
+ * leaving a half-granted role that looks complete.
68
+ */
69
+ declare function roleStatements(spec: RoleSpec): RoleStatement[];
70
+ /** A reader role was named and no password was supplied for it. */
71
+ declare class ReaderNeedsPassword extends Error {
72
+ readonly role: string;
73
+ constructor(role: string);
74
+ }
75
+ /** The read-only role's name for a given runtime role. */
76
+ declare function readerRole(runtime: string): string;
77
+ /** The owner role's name for a given runtime role. */
78
+ declare function ownerRole(runtime: string): string;
79
+ //# sourceMappingURL=roles.d.ts.map
80
+ //#endregion
81
+ export { ReaderNeedsPassword, RoleSpec, RoleStatement, ownerRole, readerRole, roleStatements };
82
+ //# sourceMappingURL=roles.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.d.mts","names":[],"sources":["../../src/pg/roles.ts"],"sourcesContent":[],"mappings":";;AAqBA;AAqBA;AAqBA;;;;AAA6D;AA2I7D;AAYA;AAKA;;;;;;;;;;UAtMiB,QAAA;;;;;;;;;;;;;;;;;;;;;;;;UAqBA,aAAA;;;;;;;;;;;;;;;;;;;;;;;iBAqBD,cAAA,OAAqB,WAAW;;cA2InC,mBAAA,SAA4B,KAAK;;;;;iBAY9B,UAAA;;iBAKA,SAAA"}
@@ -0,0 +1,150 @@
1
+ //#region src/pg/roles.ts
2
+ /**
3
+ * Everything one tenant's roles need, in dependency order.
4
+ *
5
+ * The owner before the schema it owns, the schema before the grants on it. A
6
+ * caller applies them in sequence and may stop at the first failure without
7
+ * leaving a half-granted role that looks complete.
8
+ */
9
+ function roleStatements(spec) {
10
+ const { runtime, owner, schema, passwords } = spec;
11
+ return [
12
+ {
13
+ describe: `role ${owner}`,
14
+ exists: roleExists(owner),
15
+ sql: `CREATE ROLE ${ident(owner)} LOGIN PASSWORD ${literal(passwords.owner)}`
16
+ },
17
+ {
18
+ describe: `role ${runtime}`,
19
+ exists: roleExists(runtime),
20
+ sql: `CREATE ROLE ${ident(runtime)} LOGIN PASSWORD ${literal(passwords.runtime)}`
21
+ },
22
+ {
23
+ describe: `${owner} granted to the connecting role`,
24
+ sql: `GRANT ${ident(owner)} TO CURRENT_USER`
25
+ },
26
+ {
27
+ describe: `schema ${schema}`,
28
+ exists: {
29
+ sql: "SELECT 1 FROM information_schema.schemata WHERE schema_name = $1",
30
+ values: [schema]
31
+ },
32
+ sql: `CREATE SCHEMA ${ident(schema)} AUTHORIZATION ${ident(owner)}`
33
+ },
34
+ {
35
+ describe: `${schema} is owned by ${owner}`,
36
+ sql: `ALTER SCHEMA ${ident(schema)} OWNER TO ${ident(owner)}`
37
+ },
38
+ {
39
+ describe: `${runtime} may use ${schema}`,
40
+ sql: `GRANT USAGE ON SCHEMA ${ident(schema)} TO ${ident(runtime)}`
41
+ },
42
+ {
43
+ describe: `${runtime} may read and write future tables in ${schema}`,
44
+ sql: `ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ${ident(runtime)}`
45
+ },
46
+ {
47
+ describe: `${runtime} may use future sequences in ${schema}`,
48
+ sql: `ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} GRANT USAGE, SELECT ON SEQUENCES TO ${ident(runtime)}`
49
+ },
50
+ {
51
+ describe: `${runtime} may read and write existing tables in ${schema}`,
52
+ sql: `GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA ${ident(schema)} TO ${ident(runtime)}`
53
+ },
54
+ {
55
+ describe: `${runtime} may use existing sequences in ${schema}`,
56
+ sql: `GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA ${ident(schema)} TO ${ident(runtime)}`
57
+ },
58
+ {
59
+ describe: `${runtime} resolves names in ${schema}`,
60
+ sql: `ALTER ROLE ${ident(runtime)} SET search_path TO ${ident(schema)}`
61
+ },
62
+ {
63
+ describe: `${owner} resolves names in ${schema}`,
64
+ sql: `ALTER ROLE ${ident(owner)} SET search_path TO ${ident(schema)}`
65
+ },
66
+ ...spec.reader ? readerStatements(spec, spec.reader) : []
67
+ ];
68
+ }
69
+ /**
70
+ * A role that may read and nothing else.
71
+ *
72
+ * `SELECT` and no more, on what exists and on what the owner creates later. No
73
+ * `INSERT`, no `UPDATE`, no `DELETE`, and no sequence `USAGE` — a reader that
74
+ * could take a sequence value could not write the row it was for, so granting
75
+ * it would be surface with no use.
76
+ */
77
+ function readerStatements(spec, reader) {
78
+ const { owner, schema, passwords } = spec;
79
+ const password = passwords.reader;
80
+ if (!password) throw new ReaderNeedsPassword(reader);
81
+ return [
82
+ {
83
+ describe: `role ${reader}`,
84
+ exists: roleExists(reader),
85
+ sql: `CREATE ROLE ${ident(reader)} LOGIN PASSWORD ${literal(password)}`
86
+ },
87
+ {
88
+ describe: `${reader} may use ${schema}`,
89
+ sql: `GRANT USAGE ON SCHEMA ${ident(schema)} TO ${ident(reader)}`
90
+ },
91
+ {
92
+ describe: `${reader} may read future tables in ${schema}`,
93
+ sql: `ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} GRANT SELECT ON TABLES TO ${ident(reader)}`
94
+ },
95
+ {
96
+ describe: `${reader} may read existing tables in ${schema}`,
97
+ sql: `GRANT SELECT ON ALL TABLES IN SCHEMA ${ident(schema)} TO ${ident(reader)}`
98
+ },
99
+ {
100
+ describe: `${reader} resolves names in ${schema}`,
101
+ sql: `ALTER ROLE ${ident(reader)} SET search_path TO ${ident(schema)}`
102
+ }
103
+ ];
104
+ }
105
+ /** A reader role was named and no password was supplied for it. */
106
+ var ReaderNeedsPassword = class extends Error {
107
+ constructor(role) {
108
+ super(`'${role}' is a reader role and no password was supplied for it. This module composes DDL and invents no secrets: pass passwords.reader alongside the role name.`);
109
+ this.role = role;
110
+ this.name = "ReaderNeedsPassword";
111
+ }
112
+ };
113
+ /** The read-only role's name for a given runtime role. */
114
+ function readerRole(runtime) {
115
+ return `${runtime}_reader`;
116
+ }
117
+ /** The owner role's name for a given runtime role. */
118
+ function ownerRole(runtime) {
119
+ return `${runtime}_owner`;
120
+ }
121
+ function roleExists(role) {
122
+ return {
123
+ sql: "SELECT 1 FROM pg_roles WHERE rolname = $1",
124
+ values: [role]
125
+ };
126
+ }
127
+ /**
128
+ * Quote an identifier for use in DDL.
129
+ *
130
+ * Names reaching here are already canonical, so this is defence rather than
131
+ * sanitisation — but DDL cannot be parameterised, and an unquoted identifier is
132
+ * the one place a name could ever be more than a name.
133
+ */
134
+ function ident(name) {
135
+ return `"${name.replace(/"/g, "\"\"")}"`;
136
+ }
137
+ /**
138
+ * Quote a string literal.
139
+ *
140
+ * A password cannot be a bind parameter in `CREATE ROLE`, which is the one
141
+ * place in this module where a value rather than a name is interpolated — so it
142
+ * is escaped rather than trusted, whatever the caller believes it derived.
143
+ */
144
+ function literal(value) {
145
+ return `'${value.replace(/'/g, "''")}'`;
146
+ }
147
+
148
+ //#endregion
149
+ export { ReaderNeedsPassword, ownerRole, readerRole, roleStatements };
150
+ //# sourceMappingURL=roles.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"roles.mjs","names":["spec: RoleSpec","reader: string","role: string","runtime: string","name: string","value: string"],"sources":["../../src/pg/roles.ts"],"sourcesContent":["/**\n * The role DDL — an owner, a runtime role, and the grants between them.\n *\n * Generation only. The same statements have to run in-process from `gkm dev`\n * and from a provisioner inside a VPC at deploy, and one implementation is what\n * stops \"works locally, fails deployed\" — so this composes SQL and applies\n * none of it.\n *\n * **What the split is for.** A handler connects as the runtime role, which can\n * read and write rows and cannot create, alter, or drop anything. A migrator\n * connects as the owner, which can. That is a security property rather than a\n * convention — a compromised handler cannot `DROP TABLE` because its role holds\n * no such grant — and it is only a property at all once these statements have\n * actually run. Until then a schema is a namespace, not a boundary.\n *\n * **Passwords are an input.** This module invents no secrets: a caller derives\n * or fetches them and passes them in, because where a password comes from is\n * the one part that legitimately differs between a laptop and a deploy.\n */\n\n/** The roles and schema one tenant needs. */\nexport interface RoleSpec {\n\t/** The role a handler connects as. Reads and writes rows; owns nothing. */\n\truntime: string;\n\t/** The role a migrator connects as. Owns the schema and everything in it. */\n\towner: string;\n\t/**\n\t * A third role that may only read, for a reader endpoint.\n\t *\n\t * Optional because it is only worth creating where something points at it.\n\t * Read-only is enforced *here*, by the grants, rather than by which endpoint\n\t * a URL happens to name — which is what makes falling back to the writer's\n\t * endpoint safe when a cluster has no replica.\n\t */\n\treader?: string;\n\t/** The schema they operate in, pinned on every role's `search_path`. */\n\tschema: string;\n\t/** Supplied, never generated here. */\n\tpasswords: { runtime: string; owner: string; reader?: string };\n}\n\n/** One statement, and how to tell whether it still needs running. */\nexport interface RoleStatement {\n\t/** What it does, for the line a reconciler prints. */\n\tdescribe: string;\n\t/**\n\t * Answers \"does this already exist\", parameterised.\n\t *\n\t * Absent means the statement is idempotent in itself and is run every time —\n\t * a `GRANT` re-granting what is already granted is a no-op, and checking\n\t * would cost a round trip to learn nothing.\n\t */\n\texists?: { sql: string; values: unknown[] };\n\tsql: string;\n}\n\n/**\n * Everything one tenant's roles need, in dependency order.\n *\n * The owner before the schema it owns, the schema before the grants on it. A\n * caller applies them in sequence and may stop at the first failure without\n * leaving a half-granted role that looks complete.\n */\nexport function roleStatements(spec: RoleSpec): RoleStatement[] {\n\tconst { runtime, owner, schema, passwords } = spec;\n\n\treturn [\n\t\t{\n\t\t\tdescribe: `role ${owner}`,\n\t\t\texists: roleExists(owner),\n\t\t\tsql: `CREATE ROLE ${ident(owner)} LOGIN PASSWORD ${literal(passwords.owner)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `role ${runtime}`,\n\t\t\texists: roleExists(runtime),\n\t\t\tsql: `CREATE ROLE ${ident(runtime)} LOGIN PASSWORD ${literal(passwords.runtime)}`,\n\t\t},\n\t\t{\n\t\t\t// Postgres will not let you create an object owned by a role you are\n\t\t\t// not a member of, and an RDS master is not a superuser — so\n\t\t\t// `CREATE SCHEMA … AUTHORIZATION` fails with \"must be able to SET\n\t\t\t// ROLE\" unless the connected role is granted membership first.\n\t\t\t//\n\t\t\t// Granting is idempotent and needs no check. It also leaves the master\n\t\t\t// able to administer the schema, which is what you want from the one\n\t\t\t// credential that exists before any role does.\n\t\t\tdescribe: `${owner} granted to the connecting role`,\n\t\t\tsql: `GRANT ${ident(owner)} TO CURRENT_USER`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `schema ${schema}`,\n\t\t\texists: {\n\t\t\t\tsql: 'SELECT 1 FROM information_schema.schemata WHERE schema_name = $1',\n\t\t\t\tvalues: [schema],\n\t\t\t},\n\t\t\t// Owned by the owner from the moment it exists. Creating it as the\n\t\t\t// cluster master and granting afterwards leaves a window where the\n\t\t\t// master owns objects the owner is supposed to.\n\t\t\tsql: `CREATE SCHEMA ${ident(schema)} AUTHORIZATION ${ident(owner)}`,\n\t\t},\n\t\t{\n\t\t\t// And the schema that was already there, which the guard above skips.\n\t\t\t// A schema created before its roles existed is owned by whoever\n\t\t\t// created it — the cluster master — and nothing below fixes that, so\n\t\t\t// the owner cannot create in the schema it is supposed to own. The\n\t\t\t// failure is `no schema has been selected to create in`, which names\n\t\t\t// neither the schema nor the owner and reads like a missing\n\t\t\t// `search_path`.\n\t\t\t//\n\t\t\t// Idempotent when the owner is already right, so it costs a statement\n\t\t\t// rather than a check.\n\t\t\tdescribe: `${schema} is owned by ${owner}`,\n\t\t\tsql: `ALTER SCHEMA ${ident(schema)} OWNER TO ${ident(owner)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${runtime} may use ${schema}`,\n\t\t\tsql: `GRANT USAGE ON SCHEMA ${ident(schema)} TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\t// The important half: *future* tables. A grant on what exists today\n\t\t\t// covers nothing the next migration creates, which is how a runtime\n\t\t\t// role silently loses access one deploy later.\n\t\t\tdescribe: `${runtime} may read and write future tables in ${schema}`,\n\t\t\tsql:\n\t\t\t\t`ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} ` +\n\t\t\t\t`GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${runtime} may use future sequences in ${schema}`,\n\t\t\tsql:\n\t\t\t\t`ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} ` +\n\t\t\t\t`GRANT USAGE, SELECT ON SEQUENCES TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\t// And the tables that are already there, for a schema that existed\n\t\t\t// before its roles did.\n\t\t\tdescribe: `${runtime} may read and write existing tables in ${schema}`,\n\t\t\tsql:\n\t\t\t\t`GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA ${ident(schema)} ` +\n\t\t\t\t`TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${runtime} may use existing sequences in ${schema}`,\n\t\t\tsql: `GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA ${ident(schema)} TO ${ident(runtime)}`,\n\t\t},\n\t\t{\n\t\t\t// Pinned on the role rather than carried in every URL. A connection\n\t\t\t// string that has to remember `search_path` is one that eventually\n\t\t\t// forgets, and the forgetting looks like an empty database.\n\t\t\tdescribe: `${runtime} resolves names in ${schema}`,\n\t\t\tsql: `ALTER ROLE ${ident(runtime)} SET search_path TO ${ident(schema)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${owner} resolves names in ${schema}`,\n\t\t\tsql: `ALTER ROLE ${ident(owner)} SET search_path TO ${ident(schema)}`,\n\t\t},\n\t\t...(spec.reader ? readerStatements(spec, spec.reader) : []),\n\t];\n}\n\n/**\n * A role that may read and nothing else.\n *\n * `SELECT` and no more, on what exists and on what the owner creates later. No\n * `INSERT`, no `UPDATE`, no `DELETE`, and no sequence `USAGE` — a reader that\n * could take a sequence value could not write the row it was for, so granting\n * it would be surface with no use.\n */\nfunction readerStatements(spec: RoleSpec, reader: string): RoleStatement[] {\n\tconst { owner, schema, passwords } = spec;\n\tconst password = passwords.reader;\n\n\tif (!password) throw new ReaderNeedsPassword(reader);\n\n\treturn [\n\t\t{\n\t\t\tdescribe: `role ${reader}`,\n\t\t\texists: roleExists(reader),\n\t\t\tsql: `CREATE ROLE ${ident(reader)} LOGIN PASSWORD ${literal(password)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${reader} may use ${schema}`,\n\t\t\tsql: `GRANT USAGE ON SCHEMA ${ident(schema)} TO ${ident(reader)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${reader} may read future tables in ${schema}`,\n\t\t\tsql:\n\t\t\t\t`ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} ` +\n\t\t\t\t`GRANT SELECT ON TABLES TO ${ident(reader)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${reader} may read existing tables in ${schema}`,\n\t\t\tsql: `GRANT SELECT ON ALL TABLES IN SCHEMA ${ident(schema)} TO ${ident(reader)}`,\n\t\t},\n\t\t{\n\t\t\tdescribe: `${reader} resolves names in ${schema}`,\n\t\t\tsql: `ALTER ROLE ${ident(reader)} SET search_path TO ${ident(schema)}`,\n\t\t},\n\t];\n}\n\n/** A reader role was named and no password was supplied for it. */\nexport class ReaderNeedsPassword extends Error {\n\tconstructor(readonly role: string) {\n\t\tsuper(\n\t\t\t`'${role}' is a reader role and no password was supplied for it. ` +\n\t\t\t\t`This module composes DDL and invents no secrets: pass ` +\n\t\t\t\t`passwords.reader alongside the role name.`,\n\t\t);\n\t\tthis.name = 'ReaderNeedsPassword';\n\t}\n}\n\n/** The read-only role's name for a given runtime role. */\nexport function readerRole(runtime: string): string {\n\treturn `${runtime}_reader`;\n}\n\n/** The owner role's name for a given runtime role. */\nexport function ownerRole(runtime: string): string {\n\treturn `${runtime}_owner`;\n}\n\nfunction roleExists(role: string): { sql: string; values: unknown[] } {\n\treturn { sql: 'SELECT 1 FROM pg_roles WHERE rolname = $1', values: [role] };\n}\n\n/**\n * Quote an identifier for use in DDL.\n *\n * Names reaching here are already canonical, so this is defence rather than\n * sanitisation — but DDL cannot be parameterised, and an unquoted identifier is\n * the one place a name could ever be more than a name.\n */\nfunction ident(name: string): string {\n\treturn `\"${name.replace(/\"/g, '\"\"')}\"`;\n}\n\n/**\n * Quote a string literal.\n *\n * A password cannot be a bind parameter in `CREATE ROLE`, which is the one\n * place in this module where a value rather than a name is interpolated — so it\n * is escaped rather than trusted, whatever the caller believes it derived.\n */\nfunction literal(value: string): string {\n\treturn `'${value.replace(/'/g, \"''\")}'`;\n}\n"],"mappings":";;;;;;;;AA+DA,SAAgB,eAAeA,MAAiC;CAC/D,MAAM,EAAE,SAAS,OAAO,QAAQ,WAAW,GAAG;AAE9C,QAAO;EACN;GACC,WAAW,OAAO,MAAM;GACxB,QAAQ,WAAW,MAAM;GACzB,MAAM,cAAc,MAAM,MAAM,CAAC,kBAAkB,QAAQ,UAAU,MAAM,CAAC;EAC5E;EACD;GACC,WAAW,OAAO,QAAQ;GAC1B,QAAQ,WAAW,QAAQ;GAC3B,MAAM,cAAc,MAAM,QAAQ,CAAC,kBAAkB,QAAQ,UAAU,QAAQ,CAAC;EAChF;EACD;GASC,WAAW,EAAE,MAAM;GACnB,MAAM,QAAQ,MAAM,MAAM,CAAC;EAC3B;EACD;GACC,WAAW,SAAS,OAAO;GAC3B,QAAQ;IACP,KAAK;IACL,QAAQ,CAAC,MAAO;GAChB;GAID,MAAM,gBAAgB,MAAM,OAAO,CAAC,iBAAiB,MAAM,MAAM,CAAC;EAClE;EACD;GAWC,WAAW,EAAE,OAAO,eAAe,MAAM;GACzC,MAAM,eAAe,MAAM,OAAO,CAAC,YAAY,MAAM,MAAM,CAAC;EAC5D;EACD;GACC,WAAW,EAAE,QAAQ,WAAW,OAAO;GACvC,MAAM,wBAAwB,MAAM,OAAO,CAAC,MAAM,MAAM,QAAQ,CAAC;EACjE;EACD;GAIC,WAAW,EAAE,QAAQ,uCAAuC,OAAO;GACnE,MACE,oCAAoC,MAAM,MAAM,CAAC,aAAa,MAAM,OAAO,CAAC,qDACxB,MAAM,QAAQ,CAAC;EACrE;EACD;GACC,WAAW,EAAE,QAAQ,+BAA+B,OAAO;GAC3D,MACE,oCAAoC,MAAM,MAAM,CAAC,aAAa,MAAM,OAAO,CAAC,uCACtC,MAAM,QAAQ,CAAC;EACvD;EACD;GAGC,WAAW,EAAE,QAAQ,yCAAyC,OAAO;GACrE,MACE,+DAA+D,MAAM,OAAO,CAAC,MACxE,MAAM,QAAQ,CAAC;EACtB;EACD;GACC,WAAW,EAAE,QAAQ,iCAAiC,OAAO;GAC7D,MAAM,iDAAiD,MAAM,OAAO,CAAC,MAAM,MAAM,QAAQ,CAAC;EAC1F;EACD;GAIC,WAAW,EAAE,QAAQ,qBAAqB,OAAO;GACjD,MAAM,aAAa,MAAM,QAAQ,CAAC,sBAAsB,MAAM,OAAO,CAAC;EACtE;EACD;GACC,WAAW,EAAE,MAAM,qBAAqB,OAAO;GAC/C,MAAM,aAAa,MAAM,MAAM,CAAC,sBAAsB,MAAM,OAAO,CAAC;EACpE;EACD,GAAI,KAAK,SAAS,iBAAiB,MAAM,KAAK,OAAO,GAAG,CAAE;CAC1D;AACD;;;;;;;;;AAUD,SAAS,iBAAiBA,MAAgBC,QAAiC;CAC1E,MAAM,EAAE,OAAO,QAAQ,WAAW,GAAG;CACrC,MAAM,WAAW,UAAU;AAE3B,MAAK,SAAU,OAAM,IAAI,oBAAoB;AAE7C,QAAO;EACN;GACC,WAAW,OAAO,OAAO;GACzB,QAAQ,WAAW,OAAO;GAC1B,MAAM,cAAc,MAAM,OAAO,CAAC,kBAAkB,QAAQ,SAAS,CAAC;EACtE;EACD;GACC,WAAW,EAAE,OAAO,WAAW,OAAO;GACtC,MAAM,wBAAwB,MAAM,OAAO,CAAC,MAAM,MAAM,OAAO,CAAC;EAChE;EACD;GACC,WAAW,EAAE,OAAO,6BAA6B,OAAO;GACxD,MACE,oCAAoC,MAAM,MAAM,CAAC,aAAa,MAAM,OAAO,CAAC,6BAChD,MAAM,OAAO,CAAC;EAC5C;EACD;GACC,WAAW,EAAE,OAAO,+BAA+B,OAAO;GAC1D,MAAM,uCAAuC,MAAM,OAAO,CAAC,MAAM,MAAM,OAAO,CAAC;EAC/E;EACD;GACC,WAAW,EAAE,OAAO,qBAAqB,OAAO;GAChD,MAAM,aAAa,MAAM,OAAO,CAAC,sBAAsB,MAAM,OAAO,CAAC;EACrE;CACD;AACD;;AAGD,IAAa,sBAAb,cAAyC,MAAM;CAC9C,YAAqBC,MAAc;AAClC,SACE,GAAG,KAAK,yJAGT;EALmB;AAMpB,OAAK,OAAO;CACZ;AACD;;AAGD,SAAgB,WAAWC,SAAyB;AACnD,SAAQ,EAAE,QAAQ;AAClB;;AAGD,SAAgB,UAAUA,SAAyB;AAClD,SAAQ,EAAE,QAAQ;AAClB;AAED,SAAS,WAAWD,MAAkD;AACrE,QAAO;EAAE,KAAK;EAA6C,QAAQ,CAAC,IAAK;CAAE;AAC3E;;;;;;;;AASD,SAAS,MAAME,MAAsB;AACpC,SAAQ,GAAG,KAAK,QAAQ,MAAM,OAAK,CAAC;AACpC;;;;;;;;AASD,SAAS,QAAQC,OAAuB;AACvC,SAAQ,GAAG,MAAM,QAAQ,MAAM,KAAK,CAAC;AACrC"}
@@ -0,0 +1,33 @@
1
+ const require_errors = require('../errors-Cs5jlAv-.cjs');
2
+
3
+ //#region src/pg/url.ts
4
+ const SCHEME = "postgres:";
5
+ const DEFAULT_PORT = 5432;
6
+ /**
7
+ * `search_path` travels as a libpq connection option, not a query parameter.
8
+ * The server reads `options`; a plain `?search_path=app` is accepted by the URL
9
+ * parser, ignored by the server, and produces a database that looks empty.
10
+ * The `-c` prefix is what makes it a runtime configuration setting.
11
+ */
12
+ function searchPathOption(schema) {
13
+ return `-c search_path=${schema}`;
14
+ }
15
+ /** Compose an address into a connection string. */
16
+ function build(address) {
17
+ const { host, port, database, username, password, searchPath, ssl } = address;
18
+ if (!host) throw new require_errors.MissingDatabaseHost();
19
+ if (!database) throw new require_errors.MissingDatabaseName();
20
+ const url = new URL(`${SCHEME}//`);
21
+ url.hostname = host;
22
+ if (port && port !== DEFAULT_PORT) url.port = String(port);
23
+ if (username) url.username = encodeURIComponent(username);
24
+ if (password) url.password = encodeURIComponent(password);
25
+ url.pathname = `/${database}`;
26
+ if (searchPath) url.searchParams.set("options", searchPathOption(searchPath));
27
+ if (ssl === false) url.searchParams.set("sslmode", "disable");
28
+ return url.toString();
29
+ }
30
+
31
+ //#endregion
32
+ exports.build = build;
33
+ //# sourceMappingURL=url.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.cjs","names":["schema: string","address: PostgresAddress","MissingDatabaseHost","MissingDatabaseName"],"sources":["../../src/pg/url.ts"],"sourcesContent":["/**\n * Composing a `postgres://` connection string.\n *\n * One direction only. Parsing exists in `pg` already — every consumer hands the\n * string straight to `new Pool({ connectionString })` — so a parser here would\n * be surface whose only caller is its own round-trip test.\n *\n * What is *not* already solved is composition: a provisioned instance exposes\n * host, port, database, username, and password as five separate values, and the\n * construct reads one URL. Doing that join by hand is where `search_path`\n * quietly goes missing.\n *\n * A URL built here is a secret — it carries the role's password. See\n * {@link redactDatabaseUrl} before writing one anywhere.\n */\n\nimport { MissingDatabaseHost, MissingDatabaseName } from '../errors';\n\n/** What a Postgres connection string addresses. */\nexport interface PostgresAddress {\n\thost: string;\n\tport?: number;\n\t/** The logical database. */\n\tdatabase: string;\n\t/** The role connecting — which is what decides DDL versus DML. */\n\tusername?: string;\n\tpassword?: string;\n\t/**\n\t * The schema to resolve unqualified names against.\n\t *\n\t * Normally pinned on the *role* instead (`ALTER ROLE … SET search_path`), so\n\t * it does not appear in the URL at all. It is carried here for the one case\n\t * with no role of its own to pin — connecting as the cluster master under\n\t * `roles: false` — where without it every query looks in `public`.\n\t *\n\t * Postgres-specific by nature, not just by spelling: it becomes a libpq\n\t * connection option, and no other engine accepts one. MySQL has no schemas\n\t * distinct from databases at all.\n\t */\n\tsearchPath?: string;\n\t/** Off only for local development, where there is no certificate to verify. */\n\tssl?: boolean;\n}\n\nconst SCHEME = 'postgres:';\n\nconst DEFAULT_PORT = 5432;\n\n/**\n * `search_path` travels as a libpq connection option, not a query parameter.\n * The server reads `options`; a plain `?search_path=app` is accepted by the URL\n * parser, ignored by the server, and produces a database that looks empty.\n * The `-c` prefix is what makes it a runtime configuration setting.\n */\nfunction searchPathOption(schema: string): string {\n\treturn `-c search_path=${schema}`;\n}\n\n/** Compose an address into a connection string. */\nexport function build(address: PostgresAddress): string {\n\tconst { host, port, database, username, password, searchPath, ssl } = address;\n\n\tif (!host) throw new MissingDatabaseHost();\n\tif (!database) throw new MissingDatabaseName();\n\n\tconst url = new URL(`${SCHEME}//`);\n\turl.hostname = host;\n\tif (port && port !== DEFAULT_PORT) url.port = String(port);\n\t// Username before password: WHATWG URL drops a password set without one.\n\tif (username) url.username = encodeURIComponent(username);\n\tif (password) url.password = encodeURIComponent(password);\n\turl.pathname = `/${database}`;\n\n\tif (searchPath) url.searchParams.set('options', searchPathOption(searchPath));\n\tif (ssl === false) url.searchParams.set('sslmode', 'disable');\n\n\treturn url.toString();\n}\n"],"mappings":";;;AA4CA,MAAM,SAAS;AAEf,MAAM,eAAe;;;;;;;AAQrB,SAAS,iBAAiBA,QAAwB;AACjD,SAAQ,iBAAiB,OAAO;AAChC;;AAGD,SAAgB,MAAMC,SAAkC;CACvD,MAAM,EAAE,MAAM,MAAM,UAAU,UAAU,UAAU,YAAY,KAAK,GAAG;AAEtE,MAAK,KAAM,OAAM,IAAIC;AACrB,MAAK,SAAU,OAAM,IAAIC;CAEzB,MAAM,MAAM,IAAI,KAAK,EAAE,OAAO;AAC9B,KAAI,WAAW;AACf,KAAI,QAAQ,SAAS,aAAc,KAAI,OAAO,OAAO,KAAK;AAE1D,KAAI,SAAU,KAAI,WAAW,mBAAmB,SAAS;AACzD,KAAI,SAAU,KAAI,WAAW,mBAAmB,SAAS;AACzD,KAAI,YAAY,GAAG,SAAS;AAE5B,KAAI,WAAY,KAAI,aAAa,IAAI,WAAW,iBAAiB,WAAW,CAAC;AAC7E,KAAI,QAAQ,MAAO,KAAI,aAAa,IAAI,WAAW,UAAU;AAE7D,QAAO,IAAI,UAAU;AACrB"}
@@ -0,0 +1,47 @@
1
+ //#region src/pg/url.d.ts
2
+ /**
3
+ * Composing a `postgres://` connection string.
4
+ *
5
+ * One direction only. Parsing exists in `pg` already — every consumer hands the
6
+ * string straight to `new Pool({ connectionString })` — so a parser here would
7
+ * be surface whose only caller is its own round-trip test.
8
+ *
9
+ * What is *not* already solved is composition: a provisioned instance exposes
10
+ * host, port, database, username, and password as five separate values, and the
11
+ * construct reads one URL. Doing that join by hand is where `search_path`
12
+ * quietly goes missing.
13
+ *
14
+ * A URL built here is a secret — it carries the role's password. See
15
+ * {@link redactDatabaseUrl} before writing one anywhere.
16
+ */
17
+ /** What a Postgres connection string addresses. */
18
+ interface PostgresAddress {
19
+ host: string;
20
+ port?: number;
21
+ /** The logical database. */
22
+ database: string;
23
+ /** The role connecting — which is what decides DDL versus DML. */
24
+ username?: string;
25
+ password?: string;
26
+ /**
27
+ * The schema to resolve unqualified names against.
28
+ *
29
+ * Normally pinned on the *role* instead (`ALTER ROLE … SET search_path`), so
30
+ * it does not appear in the URL at all. It is carried here for the one case
31
+ * with no role of its own to pin — connecting as the cluster master under
32
+ * `roles: false` — where without it every query looks in `public`.
33
+ *
34
+ * Postgres-specific by nature, not just by spelling: it becomes a libpq
35
+ * connection option, and no other engine accepts one. MySQL has no schemas
36
+ * distinct from databases at all.
37
+ */
38
+ searchPath?: string;
39
+ /** Off only for local development, where there is no certificate to verify. */
40
+ ssl?: boolean;
41
+ }
42
+ /** Compose an address into a connection string. */
43
+ declare function build(address: PostgresAddress): string;
44
+ //# sourceMappingURL=url.d.ts.map
45
+ //#endregion
46
+ export { PostgresAddress, build };
47
+ //# sourceMappingURL=url.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.cts","names":[],"sources":["../../src/pg/url.ts"],"sourcesContent":[],"mappings":";;AAmBA;AAwCA;;;;;;;;;;;;;;UAxCiB,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCD,KAAA,UAAe"}
@@ -0,0 +1,47 @@
1
+ //#region src/pg/url.d.ts
2
+ /**
3
+ * Composing a `postgres://` connection string.
4
+ *
5
+ * One direction only. Parsing exists in `pg` already — every consumer hands the
6
+ * string straight to `new Pool({ connectionString })` — so a parser here would
7
+ * be surface whose only caller is its own round-trip test.
8
+ *
9
+ * What is *not* already solved is composition: a provisioned instance exposes
10
+ * host, port, database, username, and password as five separate values, and the
11
+ * construct reads one URL. Doing that join by hand is where `search_path`
12
+ * quietly goes missing.
13
+ *
14
+ * A URL built here is a secret — it carries the role's password. See
15
+ * {@link redactDatabaseUrl} before writing one anywhere.
16
+ */
17
+ /** What a Postgres connection string addresses. */
18
+ interface PostgresAddress {
19
+ host: string;
20
+ port?: number;
21
+ /** The logical database. */
22
+ database: string;
23
+ /** The role connecting — which is what decides DDL versus DML. */
24
+ username?: string;
25
+ password?: string;
26
+ /**
27
+ * The schema to resolve unqualified names against.
28
+ *
29
+ * Normally pinned on the *role* instead (`ALTER ROLE … SET search_path`), so
30
+ * it does not appear in the URL at all. It is carried here for the one case
31
+ * with no role of its own to pin — connecting as the cluster master under
32
+ * `roles: false` — where without it every query looks in `public`.
33
+ *
34
+ * Postgres-specific by nature, not just by spelling: it becomes a libpq
35
+ * connection option, and no other engine accepts one. MySQL has no schemas
36
+ * distinct from databases at all.
37
+ */
38
+ searchPath?: string;
39
+ /** Off only for local development, where there is no certificate to verify. */
40
+ ssl?: boolean;
41
+ }
42
+ /** Compose an address into a connection string. */
43
+ declare function build(address: PostgresAddress): string;
44
+ //# sourceMappingURL=url.d.ts.map
45
+ //#endregion
46
+ export { PostgresAddress, build };
47
+ //# sourceMappingURL=url.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.d.mts","names":[],"sources":["../../src/pg/url.ts"],"sourcesContent":[],"mappings":";;AAmBA;AAwCA;;;;;;;;;;;;;;UAxCiB,eAAA;;;;;;;;;;;;;;;;;;;;;;;;;iBAwCD,KAAA,UAAe"}
@@ -0,0 +1,33 @@
1
+ import { MissingDatabaseHost, MissingDatabaseName } from "../errors-CfFCan-X.mjs";
2
+
3
+ //#region src/pg/url.ts
4
+ const SCHEME = "postgres:";
5
+ const DEFAULT_PORT = 5432;
6
+ /**
7
+ * `search_path` travels as a libpq connection option, not a query parameter.
8
+ * The server reads `options`; a plain `?search_path=app` is accepted by the URL
9
+ * parser, ignored by the server, and produces a database that looks empty.
10
+ * The `-c` prefix is what makes it a runtime configuration setting.
11
+ */
12
+ function searchPathOption(schema) {
13
+ return `-c search_path=${schema}`;
14
+ }
15
+ /** Compose an address into a connection string. */
16
+ function build(address) {
17
+ const { host, port, database, username, password, searchPath, ssl } = address;
18
+ if (!host) throw new MissingDatabaseHost();
19
+ if (!database) throw new MissingDatabaseName();
20
+ const url = new URL(`${SCHEME}//`);
21
+ url.hostname = host;
22
+ if (port && port !== DEFAULT_PORT) url.port = String(port);
23
+ if (username) url.username = encodeURIComponent(username);
24
+ if (password) url.password = encodeURIComponent(password);
25
+ url.pathname = `/${database}`;
26
+ if (searchPath) url.searchParams.set("options", searchPathOption(searchPath));
27
+ if (ssl === false) url.searchParams.set("sslmode", "disable");
28
+ return url.toString();
29
+ }
30
+
31
+ //#endregion
32
+ export { build };
33
+ //# sourceMappingURL=url.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"url.mjs","names":["schema: string","address: PostgresAddress"],"sources":["../../src/pg/url.ts"],"sourcesContent":["/**\n * Composing a `postgres://` connection string.\n *\n * One direction only. Parsing exists in `pg` already — every consumer hands the\n * string straight to `new Pool({ connectionString })` — so a parser here would\n * be surface whose only caller is its own round-trip test.\n *\n * What is *not* already solved is composition: a provisioned instance exposes\n * host, port, database, username, and password as five separate values, and the\n * construct reads one URL. Doing that join by hand is where `search_path`\n * quietly goes missing.\n *\n * A URL built here is a secret — it carries the role's password. See\n * {@link redactDatabaseUrl} before writing one anywhere.\n */\n\nimport { MissingDatabaseHost, MissingDatabaseName } from '../errors';\n\n/** What a Postgres connection string addresses. */\nexport interface PostgresAddress {\n\thost: string;\n\tport?: number;\n\t/** The logical database. */\n\tdatabase: string;\n\t/** The role connecting — which is what decides DDL versus DML. */\n\tusername?: string;\n\tpassword?: string;\n\t/**\n\t * The schema to resolve unqualified names against.\n\t *\n\t * Normally pinned on the *role* instead (`ALTER ROLE … SET search_path`), so\n\t * it does not appear in the URL at all. It is carried here for the one case\n\t * with no role of its own to pin — connecting as the cluster master under\n\t * `roles: false` — where without it every query looks in `public`.\n\t *\n\t * Postgres-specific by nature, not just by spelling: it becomes a libpq\n\t * connection option, and no other engine accepts one. MySQL has no schemas\n\t * distinct from databases at all.\n\t */\n\tsearchPath?: string;\n\t/** Off only for local development, where there is no certificate to verify. */\n\tssl?: boolean;\n}\n\nconst SCHEME = 'postgres:';\n\nconst DEFAULT_PORT = 5432;\n\n/**\n * `search_path` travels as a libpq connection option, not a query parameter.\n * The server reads `options`; a plain `?search_path=app` is accepted by the URL\n * parser, ignored by the server, and produces a database that looks empty.\n * The `-c` prefix is what makes it a runtime configuration setting.\n */\nfunction searchPathOption(schema: string): string {\n\treturn `-c search_path=${schema}`;\n}\n\n/** Compose an address into a connection string. */\nexport function build(address: PostgresAddress): string {\n\tconst { host, port, database, username, password, searchPath, ssl } = address;\n\n\tif (!host) throw new MissingDatabaseHost();\n\tif (!database) throw new MissingDatabaseName();\n\n\tconst url = new URL(`${SCHEME}//`);\n\turl.hostname = host;\n\tif (port && port !== DEFAULT_PORT) url.port = String(port);\n\t// Username before password: WHATWG URL drops a password set without one.\n\tif (username) url.username = encodeURIComponent(username);\n\tif (password) url.password = encodeURIComponent(password);\n\turl.pathname = `/${database}`;\n\n\tif (searchPath) url.searchParams.set('options', searchPathOption(searchPath));\n\tif (ssl === false) url.searchParams.set('sslmode', 'disable');\n\n\treturn url.toString();\n}\n"],"mappings":";;;AA4CA,MAAM,SAAS;AAEf,MAAM,eAAe;;;;;;;AAQrB,SAAS,iBAAiBA,QAAwB;AACjD,SAAQ,iBAAiB,OAAO;AAChC;;AAGD,SAAgB,MAAMC,SAAkC;CACvD,MAAM,EAAE,MAAM,MAAM,UAAU,UAAU,UAAU,YAAY,KAAK,GAAG;AAEtE,MAAK,KAAM,OAAM,IAAI;AACrB,MAAK,SAAU,OAAM,IAAI;CAEzB,MAAM,MAAM,IAAI,KAAK,EAAE,OAAO;AAC9B,KAAI,WAAW;AACf,KAAI,QAAQ,SAAS,aAAc,KAAI,OAAO,OAAO,KAAK;AAE1D,KAAI,SAAU,KAAI,WAAW,mBAAmB,SAAS;AACzD,KAAI,SAAU,KAAI,WAAW,mBAAmB,SAAS;AACzD,KAAI,YAAY,GAAG,SAAS;AAE5B,KAAI,WAAY,KAAI,aAAa,IAAI,WAAW,iBAAiB,WAAW,CAAC;AAC7E,KAAI,QAAQ,MAAO,KAAI,aAAa,IAAI,WAAW,UAAU;AAE7D,QAAO,IAAI,UAAU;AACrB"}
package/dist/rls.cjs CHANGED
@@ -21,7 +21,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
21
21
  }) : target, mod));
22
22
 
23
23
  //#endregion
24
- const require_kysely = require('./kysely-8WPSKCZG.cjs');
24
+ const require_kysely = require('./kysely-LWvS2ekY.cjs');
25
25
  const kysely = __toESM(require("kysely"));
26
26
 
27
27
  //#region src/rls.ts
package/dist/rls.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { DatabaseConnection, TransactionSettings } from "./kysely-Dm1w5gAY.mjs";
1
+ import { DatabaseConnection, TransactionSettings } from "./kysely-BAedYsvn.mjs";
2
2
  import { Transaction } from "kysely";
3
3
 
4
4
  //#region src/rls.d.ts
package/dist/rls.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { withTransaction } from "./kysely-DmfA94RY.mjs";
1
+ import { withTransaction } from "./kysely-Bo_Zg-D3.mjs";
2
2
  import { sql } from "kysely";
3
3
 
4
4
  //#region src/rls.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/db",
3
- "version": "1.1.1",
3
+ "version": "10.0.0-alpha.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -53,8 +53,41 @@
53
53
  "types": "./dist/rls.d.cts",
54
54
  "default": "./dist/rls.cjs"
55
55
  }
56
+ },
57
+ "./pg/url": {
58
+ "import": {
59
+ "types": "./dist/pg/url.d.mts",
60
+ "default": "./dist/pg/url.mjs"
61
+ },
62
+ "require": {
63
+ "types": "./dist/pg/url.d.cts",
64
+ "default": "./dist/pg/url.cjs"
65
+ }
66
+ },
67
+ "./pg/roles": {
68
+ "import": {
69
+ "types": "./dist/pg/roles.d.mts",
70
+ "default": "./dist/pg/roles.mjs"
71
+ },
72
+ "require": {
73
+ "types": "./dist/pg/roles.d.cts",
74
+ "default": "./dist/pg/roles.cjs"
75
+ }
76
+ },
77
+ "./errors": {
78
+ "import": {
79
+ "types": "./dist/errors.d.mts",
80
+ "default": "./dist/errors.mjs"
81
+ },
82
+ "require": {
83
+ "types": "./dist/errors.d.cts",
84
+ "default": "./dist/errors.cjs"
85
+ }
56
86
  }
57
87
  },
88
+ "files": [
89
+ "dist"
90
+ ],
58
91
  "dependencies": {
59
92
  "@faker-js/faker": "~9.9.0"
60
93
  },