@geekmidas/db 10.0.0-alpha.99 → 10.0.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 (146) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +0 -17
  3. package/dist/kysely/pagination.cjs +9 -8
  4. package/dist/kysely/pagination.cjs.map +1 -1
  5. package/dist/kysely/pagination.d.cts +14 -4
  6. package/dist/kysely/pagination.d.cts.map +1 -1
  7. package/dist/kysely/pagination.d.mts +14 -4
  8. package/dist/kysely/pagination.d.mts.map +1 -1
  9. package/dist/kysely/pagination.mjs +10 -7
  10. package/dist/kysely/pagination.mjs.map +1 -1
  11. package/dist/kysely-8WPSKCZG.cjs +17 -0
  12. package/dist/kysely-8WPSKCZG.cjs.map +1 -0
  13. package/dist/kysely-D3eeuId4.d.cts +13 -0
  14. package/dist/kysely-D3eeuId4.d.cts.map +1 -0
  15. package/dist/kysely-Dm1w5gAY.d.mts +13 -0
  16. package/dist/kysely-Dm1w5gAY.d.mts.map +1 -0
  17. package/dist/kysely-DmfA94RY.mjs +11 -0
  18. package/dist/kysely-DmfA94RY.mjs.map +1 -0
  19. package/dist/kysely.cjs +2 -11
  20. package/dist/kysely.d.cts +2 -9
  21. package/dist/kysely.d.mts +2 -9
  22. package/dist/kysely.mjs +2 -10
  23. package/dist/objection/pagination.cjs +7 -7
  24. package/dist/objection/pagination.cjs.map +1 -1
  25. package/dist/objection/pagination.d.cts +14 -4
  26. package/dist/objection/pagination.d.cts.map +1 -1
  27. package/dist/objection/pagination.d.mts +14 -4
  28. package/dist/objection/pagination.d.mts.map +1 -1
  29. package/dist/objection/pagination.mjs +8 -6
  30. package/dist/objection/pagination.mjs.map +1 -1
  31. package/dist/{pagination-CrU89125.d.cts → pagination-BAFX7C1I.d.cts} +4 -14
  32. package/dist/pagination-BAFX7C1I.d.cts.map +1 -0
  33. package/dist/pagination-BDLa7Yb_.mjs +37 -0
  34. package/dist/pagination-BDLa7Yb_.mjs.map +1 -0
  35. package/dist/pagination-Bdoa4PVj.cjs +55 -0
  36. package/dist/pagination-Bdoa4PVj.cjs.map +1 -0
  37. package/dist/{pagination-CrU89125.d.mts → pagination-BziGl-B8.d.mts} +4 -14
  38. package/dist/pagination-BziGl-B8.d.mts.map +1 -0
  39. package/dist/pagination.cjs +4 -55
  40. package/dist/pagination.d.cts +2 -2
  41. package/dist/pagination.d.mts +2 -2
  42. package/dist/pagination.mjs +2 -51
  43. package/dist/rls.cjs +29 -5
  44. package/dist/rls.cjs.map +1 -1
  45. package/dist/rls.d.cts +10 -6
  46. package/dist/rls.d.cts.map +1 -1
  47. package/dist/rls.d.mts +10 -6
  48. package/dist/rls.d.mts.map +1 -1
  49. package/dist/rls.mjs +5 -3
  50. package/dist/rls.mjs.map +1 -1
  51. package/package.json +9 -53
  52. package/src/__tests__/kysely.integration.spec.ts +741 -0
  53. package/src/__tests__/kysely.spec.ts +394 -0
  54. package/src/__tests__/rls.spec.ts +716 -0
  55. package/src/kysely/__tests__/pagination.integration.spec.ts +341 -0
  56. package/src/kysely/pagination.ts +113 -0
  57. package/src/kysely.ts +33 -0
  58. package/src/objection/__tests__/pagination.integration.spec.ts +291 -0
  59. package/src/objection/pagination.ts +98 -0
  60. package/src/pagination.ts +49 -0
  61. package/src/rls.ts +90 -0
  62. package/tsconfig.json +9 -0
  63. package/dist/errors.cjs +0 -73
  64. package/dist/errors.cjs.map +0 -1
  65. package/dist/errors.d.cts +0 -51
  66. package/dist/errors.d.cts.map +0 -1
  67. package/dist/errors.d.mts +0 -51
  68. package/dist/errors.d.mts.map +0 -1
  69. package/dist/errors.mjs +0 -69
  70. package/dist/errors.mjs.map +0 -1
  71. package/dist/introspect/DataBrowser.cjs +0 -117
  72. package/dist/introspect/DataBrowser.cjs.map +0 -1
  73. package/dist/introspect/DataBrowser.d.cts +0 -51
  74. package/dist/introspect/DataBrowser.d.cts.map +0 -1
  75. package/dist/introspect/DataBrowser.d.mts +0 -51
  76. package/dist/introspect/DataBrowser.d.mts.map +0 -1
  77. package/dist/introspect/DataBrowser.mjs +0 -116
  78. package/dist/introspect/DataBrowser.mjs.map +0 -1
  79. package/dist/introspect/errors.cjs +0 -48
  80. package/dist/introspect/errors.cjs.map +0 -1
  81. package/dist/introspect/errors.d.cts +0 -27
  82. package/dist/introspect/errors.d.cts.map +0 -1
  83. package/dist/introspect/errors.d.mts +0 -27
  84. package/dist/introspect/errors.d.mts.map +0 -1
  85. package/dist/introspect/errors.mjs +0 -45
  86. package/dist/introspect/errors.mjs.map +0 -1
  87. package/dist/introspect/filtering.cjs +0 -101
  88. package/dist/introspect/filtering.cjs.map +0 -1
  89. package/dist/introspect/filtering.d.cts +0 -29
  90. package/dist/introspect/filtering.d.cts.map +0 -1
  91. package/dist/introspect/filtering.d.mts +0 -29
  92. package/dist/introspect/filtering.d.mts.map +0 -1
  93. package/dist/introspect/filtering.mjs +0 -98
  94. package/dist/introspect/filtering.mjs.map +0 -1
  95. package/dist/introspect/handler.cjs +0 -159
  96. package/dist/introspect/handler.cjs.map +0 -1
  97. package/dist/introspect/handler.d.cts +0 -43
  98. package/dist/introspect/handler.d.cts.map +0 -1
  99. package/dist/introspect/handler.d.mts +0 -43
  100. package/dist/introspect/handler.d.mts.map +0 -1
  101. package/dist/introspect/handler.mjs +0 -158
  102. package/dist/introspect/handler.mjs.map +0 -1
  103. package/dist/introspect/index.cjs +0 -22
  104. package/dist/introspect/index.d.cts +0 -8
  105. package/dist/introspect/index.d.mts +0 -8
  106. package/dist/introspect/index.mjs +0 -8
  107. package/dist/introspect/introspection.cjs +0 -149
  108. package/dist/introspect/introspection.cjs.map +0 -1
  109. package/dist/introspect/introspection.d.cts +0 -25
  110. package/dist/introspect/introspection.d.cts.map +0 -1
  111. package/dist/introspect/introspection.d.mts +0 -25
  112. package/dist/introspect/introspection.d.mts.map +0 -1
  113. package/dist/introspect/introspection.mjs +0 -146
  114. package/dist/introspect/introspection.mjs.map +0 -1
  115. package/dist/introspect/types.cjs +0 -24
  116. package/dist/introspect/types.cjs.map +0 -1
  117. package/dist/introspect/types.d.cts +0 -113
  118. package/dist/introspect/types.d.cts.map +0 -1
  119. package/dist/introspect/types.d.mts +0 -113
  120. package/dist/introspect/types.d.mts.map +0 -1
  121. package/dist/introspect/types.mjs +0 -23
  122. package/dist/introspect/types.mjs.map +0 -1
  123. package/dist/kysely.cjs.map +0 -1
  124. package/dist/kysely.d.cts.map +0 -1
  125. package/dist/kysely.d.mts.map +0 -1
  126. package/dist/kysely.mjs.map +0 -1
  127. package/dist/pagination-CrU89125.d.cts.map +0 -1
  128. package/dist/pagination-CrU89125.d.mts.map +0 -1
  129. package/dist/pagination.cjs.map +0 -1
  130. package/dist/pagination.mjs.map +0 -1
  131. package/dist/pg/roles.cjs +0 -177
  132. package/dist/pg/roles.cjs.map +0 -1
  133. package/dist/pg/roles.d.cts +0 -90
  134. package/dist/pg/roles.d.cts.map +0 -1
  135. package/dist/pg/roles.d.mts +0 -90
  136. package/dist/pg/roles.d.mts.map +0 -1
  137. package/dist/pg/roles.mjs +0 -173
  138. package/dist/pg/roles.mjs.map +0 -1
  139. package/dist/pg/url.cjs +0 -48
  140. package/dist/pg/url.cjs.map +0 -1
  141. package/dist/pg/url.d.cts +0 -45
  142. package/dist/pg/url.d.cts.map +0 -1
  143. package/dist/pg/url.d.mts +0 -45
  144. package/dist/pg/url.d.mts.map +0 -1
  145. package/dist/pg/url.mjs +0 -47
  146. package/dist/pg/url.mjs.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.cjs","names":[],"sources":["../../src/introspect/types.ts"],"sourcesContent":["import type { Kysely } from 'kysely';\nimport type { Direction } from '../pagination';\n\n/**\n * Filter operators a row query accepts.\n */\nexport enum FilterOperator {\n\tEq = 'eq',\n\tNeq = 'neq',\n\tGt = 'gt',\n\tGte = 'gte',\n\tLt = 'lt',\n\tLte = 'lte',\n\tLike = 'like',\n\tIlike = 'ilike',\n\tIn = 'in',\n\tNin = 'nin',\n\tIsNull = 'is_null',\n\tIsNotNull = 'is_not_null',\n}\n\n/**\n * The column a table's rows are paged by, and in which direction.\n */\nexport interface CursorConfig {\n\t/** A column whose values are unique and ordered, e.g. `id` or `created_at`. */\n\tfield: string;\n\tdirection: Direction;\n}\n\n/**\n * Options for a {@link DataBrowser}.\n */\nexport interface DataBrowserOptions<DB = unknown> {\n\tdb: Kysely<DB>;\n\t/**\n\t * The schemas to read tables from (default: `['public']`). Every schema the\n\t * database has is listed by `listSchemas` regardless; this is which of them\n\t * are browsable.\n\t */\n\tschemas?: string[];\n\t/**\n\t * The cursor every table pages by. When omitted, a table pages by its\n\t * primary key when that is a single column, ascending — the one ordering\n\t * that is unique for any table that has one.\n\t */\n\tcursor?: CursorConfig;\n\t/** Per-table cursors, by table name, for tables the default does not fit. */\n\ttableCursors?: Record<string, CursorConfig>;\n\t/** Tables left out of introspection, by name. */\n\texcludeTables?: string[];\n\t/** Rows per page when a query names none (default: 50, at most 100). */\n\tdefaultPageSize?: number;\n}\n\n/**\n * A column's type, classified independently of the engine.\n */\nexport type ColumnType =\n\t| 'string'\n\t| 'number'\n\t| 'boolean'\n\t| 'date'\n\t| 'datetime'\n\t| 'json'\n\t| 'binary'\n\t| 'uuid'\n\t| 'unknown';\n\nexport interface ColumnInfo {\n\tname: string;\n\ttype: ColumnType;\n\t/** The engine's own type name, e.g. `varchar` or `int4`. */\n\trawType: string;\n\tnullable: boolean;\n\tisPrimaryKey: boolean;\n\tisForeignKey: boolean;\n\t/** The table a foreign key references. */\n\tforeignKeyTable?: string;\n\t/** The column a foreign key references. */\n\tforeignKeyColumn?: string;\n\t/** The default value expression, as the database stores it. */\n\tdefaultValue?: string;\n}\n\nexport interface TableInfo {\n\tname: string;\n\tschema: string;\n\tcolumns: ColumnInfo[];\n\t/** Primary key column names, in key order. */\n\tprimaryKey: string[];\n\t/** Postgres's planner estimate, when it has one. */\n\testimatedRowCount?: number;\n}\n\nexport interface SchemaInfo {\n\ttables: TableInfo[];\n\t/** When the schema was introspected. */\n\tupdatedAt: Date;\n}\n\nexport interface FilterCondition {\n\tcolumn: string;\n\toperator: FilterOperator;\n\t/** Omitted for `is_null` / `is_not_null`. */\n\tvalue?: unknown;\n}\n\nexport interface SortConfig {\n\tcolumn: string;\n\tdirection: Direction;\n}\n\nexport interface QueryOptions {\n\ttable: string;\n\t/** The schema the table lives in (default: the first browsable schema). */\n\tschema?: string;\n\tfilters?: FilterCondition[];\n\tsort?: SortConfig[];\n\tcursor?: string | null;\n\tpageSize?: number;\n}\n\nexport interface QueryResult<T = Record<string, unknown>> {\n\trows: T[];\n\thasMore: boolean;\n\t/** Null when there is no next page, or the table has no cursor column. */\n\tnextCursor: string | null;\n\tprevCursor: string | null;\n}\n"],"mappings":";;;;;AAMA,IAAY,iBAAL,yBAAA,gBAAA;CACN,eAAA,QAAA;CACA,eAAA,SAAA;CACA,eAAA,QAAA;CACA,eAAA,SAAA;CACA,eAAA,QAAA;CACA,eAAA,SAAA;CACA,eAAA,UAAA;CACA,eAAA,WAAA;CACA,eAAA,QAAA;CACA,eAAA,SAAA;CACA,eAAA,YAAA;CACA,eAAA,eAAA;;AACD,EAAA,CAAA,CAAA"}
@@ -1,113 +0,0 @@
1
- import { t as Direction } from "../pagination-CrU89125.cjs";
2
- import { Kysely } from "kysely";
3
- //#region src/introspect/types.d.ts
4
- /**
5
- * Filter operators a row query accepts.
6
- */
7
- export declare enum FilterOperator {
8
- Eq = "eq",
9
- Neq = "neq",
10
- Gt = "gt",
11
- Gte = "gte",
12
- Lt = "lt",
13
- Lte = "lte",
14
- Like = "like",
15
- Ilike = "ilike",
16
- In = "in",
17
- Nin = "nin",
18
- IsNull = "is_null",
19
- IsNotNull = "is_not_null"
20
- }
21
- /**
22
- * The column a table's rows are paged by, and in which direction.
23
- */
24
- export interface CursorConfig {
25
- /** A column whose values are unique and ordered, e.g. `id` or `created_at`. */
26
- field: string;
27
- direction: Direction;
28
- }
29
- /**
30
- * Options for a {@link DataBrowser}.
31
- */
32
- export interface DataBrowserOptions<DB = unknown> {
33
- db: Kysely<DB>;
34
- /**
35
- * The schemas to read tables from (default: `['public']`). Every schema the
36
- * database has is listed by `listSchemas` regardless; this is which of them
37
- * are browsable.
38
- */
39
- schemas?: string[];
40
- /**
41
- * The cursor every table pages by. When omitted, a table pages by its
42
- * primary key when that is a single column, ascending — the one ordering
43
- * that is unique for any table that has one.
44
- */
45
- cursor?: CursorConfig;
46
- /** Per-table cursors, by table name, for tables the default does not fit. */
47
- tableCursors?: Record<string, CursorConfig>;
48
- /** Tables left out of introspection, by name. */
49
- excludeTables?: string[];
50
- /** Rows per page when a query names none (default: 50, at most 100). */
51
- defaultPageSize?: number;
52
- }
53
- /**
54
- * A column's type, classified independently of the engine.
55
- */
56
- export type ColumnType = 'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'json' | 'binary' | 'uuid' | 'unknown';
57
- export interface ColumnInfo {
58
- name: string;
59
- type: ColumnType;
60
- /** The engine's own type name, e.g. `varchar` or `int4`. */
61
- rawType: string;
62
- nullable: boolean;
63
- isPrimaryKey: boolean;
64
- isForeignKey: boolean;
65
- /** The table a foreign key references. */
66
- foreignKeyTable?: string;
67
- /** The column a foreign key references. */
68
- foreignKeyColumn?: string;
69
- /** The default value expression, as the database stores it. */
70
- defaultValue?: string;
71
- }
72
- export interface TableInfo {
73
- name: string;
74
- schema: string;
75
- columns: ColumnInfo[];
76
- /** Primary key column names, in key order. */
77
- primaryKey: string[];
78
- /** Postgres's planner estimate, when it has one. */
79
- estimatedRowCount?: number;
80
- }
81
- export interface SchemaInfo {
82
- tables: TableInfo[];
83
- /** When the schema was introspected. */
84
- updatedAt: Date;
85
- }
86
- export interface FilterCondition {
87
- column: string;
88
- operator: FilterOperator;
89
- /** Omitted for `is_null` / `is_not_null`. */
90
- value?: unknown;
91
- }
92
- export interface SortConfig {
93
- column: string;
94
- direction: Direction;
95
- }
96
- export interface QueryOptions {
97
- table: string;
98
- /** The schema the table lives in (default: the first browsable schema). */
99
- schema?: string;
100
- filters?: FilterCondition[];
101
- sort?: SortConfig[];
102
- cursor?: string | null;
103
- pageSize?: number;
104
- }
105
- export interface QueryResult<T = Record<string, unknown>> {
106
- rows: T[];
107
- hasMore: boolean;
108
- /** Null when there is no next page, or the table has no cursor column. */
109
- nextCursor: string | null;
110
- prevCursor: string | null;
111
- }
112
- //#endregion
113
- //# sourceMappingURL=types.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.cts","names":[],"sources":["../../src/introspect/types.ts"],"mappings":";;;;;;oBAMY;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;iBAMgB;;EAEhB;EACA,WAAW;;;;;iBAMK,mBAAmB;EACnC,IAAI,OAAO;;;;;;EAMX;;;;;;EAMA,SAAS;;EAET,eAAe,eAAe;;EAE9B;;EAEA;;;;;YAMW;iBAWK;EAChB;EACA,MAAM;;EAEN;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;;iBAGgB;EAChB;EACA;EACA,SAAS;;EAET;;EAEA;;iBAGgB;EAChB,QAAQ;;EAER,WAAW;;iBAGK;EAChB;EACA,UAAU;;EAEV;;iBAGgB;EAChB;EACA,WAAW;;iBAGK;EAChB;;EAEA;EACA,UAAU;EACV,OAAO;EACP;EACA;;iBAGgB,YAAY,IAAI;EAChC,MAAM;EACN;;EAEA;EACA"}
@@ -1,113 +0,0 @@
1
- import { t as Direction } from "../pagination-CrU89125.mjs";
2
- import { Kysely } from "kysely";
3
- //#region src/introspect/types.d.ts
4
- /**
5
- * Filter operators a row query accepts.
6
- */
7
- export declare enum FilterOperator {
8
- Eq = "eq",
9
- Neq = "neq",
10
- Gt = "gt",
11
- Gte = "gte",
12
- Lt = "lt",
13
- Lte = "lte",
14
- Like = "like",
15
- Ilike = "ilike",
16
- In = "in",
17
- Nin = "nin",
18
- IsNull = "is_null",
19
- IsNotNull = "is_not_null"
20
- }
21
- /**
22
- * The column a table's rows are paged by, and in which direction.
23
- */
24
- export interface CursorConfig {
25
- /** A column whose values are unique and ordered, e.g. `id` or `created_at`. */
26
- field: string;
27
- direction: Direction;
28
- }
29
- /**
30
- * Options for a {@link DataBrowser}.
31
- */
32
- export interface DataBrowserOptions<DB = unknown> {
33
- db: Kysely<DB>;
34
- /**
35
- * The schemas to read tables from (default: `['public']`). Every schema the
36
- * database has is listed by `listSchemas` regardless; this is which of them
37
- * are browsable.
38
- */
39
- schemas?: string[];
40
- /**
41
- * The cursor every table pages by. When omitted, a table pages by its
42
- * primary key when that is a single column, ascending — the one ordering
43
- * that is unique for any table that has one.
44
- */
45
- cursor?: CursorConfig;
46
- /** Per-table cursors, by table name, for tables the default does not fit. */
47
- tableCursors?: Record<string, CursorConfig>;
48
- /** Tables left out of introspection, by name. */
49
- excludeTables?: string[];
50
- /** Rows per page when a query names none (default: 50, at most 100). */
51
- defaultPageSize?: number;
52
- }
53
- /**
54
- * A column's type, classified independently of the engine.
55
- */
56
- export type ColumnType = 'string' | 'number' | 'boolean' | 'date' | 'datetime' | 'json' | 'binary' | 'uuid' | 'unknown';
57
- export interface ColumnInfo {
58
- name: string;
59
- type: ColumnType;
60
- /** The engine's own type name, e.g. `varchar` or `int4`. */
61
- rawType: string;
62
- nullable: boolean;
63
- isPrimaryKey: boolean;
64
- isForeignKey: boolean;
65
- /** The table a foreign key references. */
66
- foreignKeyTable?: string;
67
- /** The column a foreign key references. */
68
- foreignKeyColumn?: string;
69
- /** The default value expression, as the database stores it. */
70
- defaultValue?: string;
71
- }
72
- export interface TableInfo {
73
- name: string;
74
- schema: string;
75
- columns: ColumnInfo[];
76
- /** Primary key column names, in key order. */
77
- primaryKey: string[];
78
- /** Postgres's planner estimate, when it has one. */
79
- estimatedRowCount?: number;
80
- }
81
- export interface SchemaInfo {
82
- tables: TableInfo[];
83
- /** When the schema was introspected. */
84
- updatedAt: Date;
85
- }
86
- export interface FilterCondition {
87
- column: string;
88
- operator: FilterOperator;
89
- /** Omitted for `is_null` / `is_not_null`. */
90
- value?: unknown;
91
- }
92
- export interface SortConfig {
93
- column: string;
94
- direction: Direction;
95
- }
96
- export interface QueryOptions {
97
- table: string;
98
- /** The schema the table lives in (default: the first browsable schema). */
99
- schema?: string;
100
- filters?: FilterCondition[];
101
- sort?: SortConfig[];
102
- cursor?: string | null;
103
- pageSize?: number;
104
- }
105
- export interface QueryResult<T = Record<string, unknown>> {
106
- rows: T[];
107
- hasMore: boolean;
108
- /** Null when there is no next page, or the table has no cursor column. */
109
- nextCursor: string | null;
110
- prevCursor: string | null;
111
- }
112
- //#endregion
113
- //# sourceMappingURL=types.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.d.mts","names":[],"sources":["../../src/introspect/types.ts"],"mappings":";;;;;;oBAMY;EACX;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;;;;;iBAMgB;;EAEhB;EACA,WAAW;;;;;iBAMK,mBAAmB;EACnC,IAAI,OAAO;;;;;;EAMX;;;;;;EAMA,SAAS;;EAET,eAAe,eAAe;;EAE9B;;EAEA;;;;;YAMW;iBAWK;EAChB;EACA,MAAM;;EAEN;EACA;EACA;EACA;;EAEA;;EAEA;;EAEA;;iBAGgB;EAChB;EACA;EACA,SAAS;;EAET;;EAEA;;iBAGgB;EAChB,QAAQ;;EAER,WAAW;;iBAGK;EAChB;EACA,UAAU;;EAEV;;iBAGgB;EAChB;EACA,WAAW;;iBAGK;EAChB;;EAEA;EACA,UAAU;EACV,OAAO;EACP;EACA;;iBAGgB,YAAY,IAAI;EAChC,MAAM;EACN;;EAEA;EACA"}
@@ -1,23 +0,0 @@
1
- //#region src/introspect/types.ts
2
- /**
3
- * Filter operators a row query accepts.
4
- */
5
- let FilterOperator = /* @__PURE__ */ function(FilterOperator) {
6
- FilterOperator["Eq"] = "eq";
7
- FilterOperator["Neq"] = "neq";
8
- FilterOperator["Gt"] = "gt";
9
- FilterOperator["Gte"] = "gte";
10
- FilterOperator["Lt"] = "lt";
11
- FilterOperator["Lte"] = "lte";
12
- FilterOperator["Like"] = "like";
13
- FilterOperator["Ilike"] = "ilike";
14
- FilterOperator["In"] = "in";
15
- FilterOperator["Nin"] = "nin";
16
- FilterOperator["IsNull"] = "is_null";
17
- FilterOperator["IsNotNull"] = "is_not_null";
18
- return FilterOperator;
19
- }({});
20
- //#endregion
21
- export { FilterOperator };
22
-
23
- //# sourceMappingURL=types.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"types.mjs","names":[],"sources":["../../src/introspect/types.ts"],"sourcesContent":["import type { Kysely } from 'kysely';\nimport type { Direction } from '../pagination';\n\n/**\n * Filter operators a row query accepts.\n */\nexport enum FilterOperator {\n\tEq = 'eq',\n\tNeq = 'neq',\n\tGt = 'gt',\n\tGte = 'gte',\n\tLt = 'lt',\n\tLte = 'lte',\n\tLike = 'like',\n\tIlike = 'ilike',\n\tIn = 'in',\n\tNin = 'nin',\n\tIsNull = 'is_null',\n\tIsNotNull = 'is_not_null',\n}\n\n/**\n * The column a table's rows are paged by, and in which direction.\n */\nexport interface CursorConfig {\n\t/** A column whose values are unique and ordered, e.g. `id` or `created_at`. */\n\tfield: string;\n\tdirection: Direction;\n}\n\n/**\n * Options for a {@link DataBrowser}.\n */\nexport interface DataBrowserOptions<DB = unknown> {\n\tdb: Kysely<DB>;\n\t/**\n\t * The schemas to read tables from (default: `['public']`). Every schema the\n\t * database has is listed by `listSchemas` regardless; this is which of them\n\t * are browsable.\n\t */\n\tschemas?: string[];\n\t/**\n\t * The cursor every table pages by. When omitted, a table pages by its\n\t * primary key when that is a single column, ascending — the one ordering\n\t * that is unique for any table that has one.\n\t */\n\tcursor?: CursorConfig;\n\t/** Per-table cursors, by table name, for tables the default does not fit. */\n\ttableCursors?: Record<string, CursorConfig>;\n\t/** Tables left out of introspection, by name. */\n\texcludeTables?: string[];\n\t/** Rows per page when a query names none (default: 50, at most 100). */\n\tdefaultPageSize?: number;\n}\n\n/**\n * A column's type, classified independently of the engine.\n */\nexport type ColumnType =\n\t| 'string'\n\t| 'number'\n\t| 'boolean'\n\t| 'date'\n\t| 'datetime'\n\t| 'json'\n\t| 'binary'\n\t| 'uuid'\n\t| 'unknown';\n\nexport interface ColumnInfo {\n\tname: string;\n\ttype: ColumnType;\n\t/** The engine's own type name, e.g. `varchar` or `int4`. */\n\trawType: string;\n\tnullable: boolean;\n\tisPrimaryKey: boolean;\n\tisForeignKey: boolean;\n\t/** The table a foreign key references. */\n\tforeignKeyTable?: string;\n\t/** The column a foreign key references. */\n\tforeignKeyColumn?: string;\n\t/** The default value expression, as the database stores it. */\n\tdefaultValue?: string;\n}\n\nexport interface TableInfo {\n\tname: string;\n\tschema: string;\n\tcolumns: ColumnInfo[];\n\t/** Primary key column names, in key order. */\n\tprimaryKey: string[];\n\t/** Postgres's planner estimate, when it has one. */\n\testimatedRowCount?: number;\n}\n\nexport interface SchemaInfo {\n\ttables: TableInfo[];\n\t/** When the schema was introspected. */\n\tupdatedAt: Date;\n}\n\nexport interface FilterCondition {\n\tcolumn: string;\n\toperator: FilterOperator;\n\t/** Omitted for `is_null` / `is_not_null`. */\n\tvalue?: unknown;\n}\n\nexport interface SortConfig {\n\tcolumn: string;\n\tdirection: Direction;\n}\n\nexport interface QueryOptions {\n\ttable: string;\n\t/** The schema the table lives in (default: the first browsable schema). */\n\tschema?: string;\n\tfilters?: FilterCondition[];\n\tsort?: SortConfig[];\n\tcursor?: string | null;\n\tpageSize?: number;\n}\n\nexport interface QueryResult<T = Record<string, unknown>> {\n\trows: T[];\n\thasMore: boolean;\n\t/** Null when there is no next page, or the table has no cursor column. */\n\tnextCursor: string | null;\n\tprevCursor: string | null;\n}\n"],"mappings":";;;;AAMA,IAAY,iBAAL,yBAAA,gBAAA;CACN,eAAA,QAAA;CACA,eAAA,SAAA;CACA,eAAA,QAAA;CACA,eAAA,SAAA;CACA,eAAA,QAAA;CACA,eAAA,SAAA;CACA,eAAA,UAAA;CACA,eAAA,WAAA;CACA,eAAA,QAAA;CACA,eAAA,SAAA;CACA,eAAA,YAAA;CACA,eAAA,eAAA;;AACD,EAAA,CAAA,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kysely.cjs","names":[],"sources":["../src/kysely.ts"],"sourcesContent":["import type {\n\tControlledTransaction,\n\tIsolationLevel,\n\tKysely,\n\tTransaction,\n} from 'kysely';\n\nexport interface TransactionSettings {\n\tisolationLevel?: IsolationLevel;\n}\n\nexport function withTransaction<DB, T>(\n\tdb: DatabaseConnection<DB>,\n\tcb: (trx: Transaction<DB>) => Promise<T>,\n\tsettings?: TransactionSettings,\n): Promise<T> {\n\tif (db.isTransaction) {\n\t\treturn cb(db as Transaction<DB>);\n\t}\n\n\tconst builder = db.transaction();\n\n\tif (settings?.isolationLevel) {\n\t\treturn builder.setIsolationLevel(settings.isolationLevel).execute(cb);\n\t}\n\n\treturn builder.execute(cb);\n}\n\nexport type DatabaseConnection<T> =\n\t| ControlledTransaction<T>\n\t| Kysely<T>\n\t| Transaction<T>;\n"],"mappings":";;AAWA,SAAgB,gBACf,IACA,IACA,UACa;CACb,IAAI,GAAG,eACN,OAAO,GAAG,EAAqB;CAGhC,MAAM,UAAU,GAAG,YAAY;CAE/B,IAAI,UAAU,gBACb,OAAO,QAAQ,kBAAkB,SAAS,cAAc,CAAC,CAAC,QAAQ,EAAE;CAGrE,OAAO,QAAQ,QAAQ,EAAE;AAC1B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kysely.d.cts","names":[],"sources":["../src/kysely.ts"],"mappings":";;iBAOiB;EAChB,iBAAiB;;wBAGF,gBAAgB,IAAI,GACnC,IAAI,mBAAmB,KACvB,KAAK,KAAK,YAAY,QAAQ,QAAQ,IACtC,WAAW,sBACT,QAAQ;YAcC,mBAAmB,KAC5B,sBAAsB,KACtB,OAAO,KACP,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kysely.d.mts","names":[],"sources":["../src/kysely.ts"],"mappings":";;iBAOiB;EAChB,iBAAiB;;wBAGF,gBAAgB,IAAI,GACnC,IAAI,mBAAmB,KACvB,KAAK,KAAK,YAAY,QAAQ,QAAQ,IACtC,WAAW,sBACT,QAAQ;YAcC,mBAAmB,KAC5B,sBAAsB,KACtB,OAAO,KACP,YAAY"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"kysely.mjs","names":[],"sources":["../src/kysely.ts"],"sourcesContent":["import type {\n\tControlledTransaction,\n\tIsolationLevel,\n\tKysely,\n\tTransaction,\n} from 'kysely';\n\nexport interface TransactionSettings {\n\tisolationLevel?: IsolationLevel;\n}\n\nexport function withTransaction<DB, T>(\n\tdb: DatabaseConnection<DB>,\n\tcb: (trx: Transaction<DB>) => Promise<T>,\n\tsettings?: TransactionSettings,\n): Promise<T> {\n\tif (db.isTransaction) {\n\t\treturn cb(db as Transaction<DB>);\n\t}\n\n\tconst builder = db.transaction();\n\n\tif (settings?.isolationLevel) {\n\t\treturn builder.setIsolationLevel(settings.isolationLevel).execute(cb);\n\t}\n\n\treturn builder.execute(cb);\n}\n\nexport type DatabaseConnection<T> =\n\t| ControlledTransaction<T>\n\t| Kysely<T>\n\t| Transaction<T>;\n"],"mappings":";AAWA,SAAgB,gBACf,IACA,IACA,UACa;CACb,IAAI,GAAG,eACN,OAAO,GAAG,EAAqB;CAGhC,MAAM,UAAU,GAAG,YAAY;CAE/B,IAAI,UAAU,gBACb,OAAO,QAAQ,kBAAkB,SAAS,cAAc,CAAC,CAAC,QAAQ,EAAE;CAGrE,OAAO,QAAQ,QAAQ,EAAE;AAC1B"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pagination-CrU89125.d.cts","names":[],"sources":["../src/pagination.ts"],"mappings":";;;;aAGY;EACX;EACA;;;;;UAMgB,iBAAiB;EACjC,OAAO;EACP;IACC;IACA;IACA;;;;;;;iBAQc,aAAa;;;;iBAWb,aAAa;;;;;;;;cAsBhB,sBAAsB;WACb;EAArB,YAAqB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pagination-CrU89125.d.mts","names":[],"sources":["../src/pagination.ts"],"mappings":";;;;aAGY;EACX;EACA;;;;;UAMgB,iBAAiB;EACjC,OAAO;EACP;IACC;IACA;IACA;;;;;;;iBAQc,aAAa;;;;iBAWb,aAAa;;;;;;;;cAsBhB,sBAAsB;WACb;EAArB,YAAqB"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pagination.cjs","names":[],"sources":["../src/pagination.ts"],"sourcesContent":["/**\n * Sort direction for cursor-based pagination.\n */\nexport enum Direction {\n\tAsc = 'asc',\n\tDesc = 'desc',\n}\n\n/**\n * Result of a paginated query.\n */\nexport interface PaginationResult<TItem> {\n\titems: TItem[];\n\tpagination: {\n\t\ttotal: number;\n\t\thasMore: boolean;\n\t\tcursor?: string;\n\t};\n}\n\n/**\n * Encode a cursor value for safe URL transmission.\n * Supports various types: string, number, Date, etc.\n */\nexport function encodeCursor(value: unknown): string {\n\tconst payload = {\n\t\tv: value instanceof Date ? value.toISOString() : value,\n\t\tt: value instanceof Date ? 'date' : typeof value,\n\t};\n\treturn Buffer.from(JSON.stringify(payload)).toString('base64url');\n}\n\n/**\n * Decode a cursor string back to its original value.\n */\nexport function decodeCursor(cursor: string): unknown {\n\ttry {\n\t\tconst json = Buffer.from(cursor, 'base64url').toString('utf-8');\n\t\tconst payload = JSON.parse(json);\n\n\t\tif (payload.t === 'date') {\n\t\t\treturn new Date(payload.v);\n\t\t}\n\n\t\treturn payload.v;\n\t} catch {\n\t\tthrow new InvalidCursor(cursor);\n\t}\n}\n\n/**\n * A cursor that did not come from `encodeCursor`.\n *\n * Usually a cursor from a different page size or sort that was edited by hand,\n * or truncated on its way through a URL. The fix is the same either way: start\n * again from the first page.\n */\nexport class InvalidCursor extends Error {\n\tconstructor(readonly cursor: string) {\n\t\tsuper(\n\t\t\t'This cursor was not produced by encodeCursor. Drop it and request the first page again.',\n\t\t);\n\t\tthis.name = 'InvalidCursor';\n\t}\n}\n"],"mappings":";;;;;AAGA,IAAY,YAAL,yBAAA,WAAA;CACN,UAAA,SAAA;CACA,UAAA,UAAA;;AACD,EAAA,CAAA,CAAA;;;;;AAkBA,SAAgB,aAAa,OAAwB;CACpD,MAAM,UAAU;EACf,GAAG,iBAAiB,OAAO,MAAM,YAAY,IAAI;EACjD,GAAG,iBAAiB,OAAO,SAAS,OAAO;CAC5C;CACA,OAAO,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,CAAC,CAAC,SAAS,WAAW;AACjE;;;;AAKA,SAAgB,aAAa,QAAyB;CACrD,IAAI;EACH,MAAM,OAAO,OAAO,KAAK,QAAQ,WAAW,CAAC,CAAC,SAAS,OAAO;EAC9D,MAAM,UAAU,KAAK,MAAM,IAAI;EAE/B,IAAI,QAAQ,MAAM,QACjB,OAAO,IAAI,KAAK,QAAQ,CAAC;EAG1B,OAAO,QAAQ;CAChB,QAAQ;EACP,MAAM,IAAI,cAAc,MAAM;CAC/B;AACD;;;;;;;;AASA,IAAa,gBAAb,cAAmC,MAAM;CACnB;CAArB,YAAY,QAAyB;EACpC,MACC,yFACD;EAHoB,KAAA,SAAA;EAIpB,KAAK,OAAO;CACb;AACD"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"pagination.mjs","names":[],"sources":["../src/pagination.ts"],"sourcesContent":["/**\n * Sort direction for cursor-based pagination.\n */\nexport enum Direction {\n\tAsc = 'asc',\n\tDesc = 'desc',\n}\n\n/**\n * Result of a paginated query.\n */\nexport interface PaginationResult<TItem> {\n\titems: TItem[];\n\tpagination: {\n\t\ttotal: number;\n\t\thasMore: boolean;\n\t\tcursor?: string;\n\t};\n}\n\n/**\n * Encode a cursor value for safe URL transmission.\n * Supports various types: string, number, Date, etc.\n */\nexport function encodeCursor(value: unknown): string {\n\tconst payload = {\n\t\tv: value instanceof Date ? value.toISOString() : value,\n\t\tt: value instanceof Date ? 'date' : typeof value,\n\t};\n\treturn Buffer.from(JSON.stringify(payload)).toString('base64url');\n}\n\n/**\n * Decode a cursor string back to its original value.\n */\nexport function decodeCursor(cursor: string): unknown {\n\ttry {\n\t\tconst json = Buffer.from(cursor, 'base64url').toString('utf-8');\n\t\tconst payload = JSON.parse(json);\n\n\t\tif (payload.t === 'date') {\n\t\t\treturn new Date(payload.v);\n\t\t}\n\n\t\treturn payload.v;\n\t} catch {\n\t\tthrow new InvalidCursor(cursor);\n\t}\n}\n\n/**\n * A cursor that did not come from `encodeCursor`.\n *\n * Usually a cursor from a different page size or sort that was edited by hand,\n * or truncated on its way through a URL. The fix is the same either way: start\n * again from the first page.\n */\nexport class InvalidCursor extends Error {\n\tconstructor(readonly cursor: string) {\n\t\tsuper(\n\t\t\t'This cursor was not produced by encodeCursor. Drop it and request the first page again.',\n\t\t);\n\t\tthis.name = 'InvalidCursor';\n\t}\n}\n"],"mappings":";;;;AAGA,IAAY,YAAL,yBAAA,WAAA;CACN,UAAA,SAAA;CACA,UAAA,UAAA;;AACD,EAAA,CAAA,CAAA;;;;;AAkBA,SAAgB,aAAa,OAAwB;CACpD,MAAM,UAAU;EACf,GAAG,iBAAiB,OAAO,MAAM,YAAY,IAAI;EACjD,GAAG,iBAAiB,OAAO,SAAS,OAAO;CAC5C;CACA,OAAO,OAAO,KAAK,KAAK,UAAU,OAAO,CAAC,CAAC,CAAC,SAAS,WAAW;AACjE;;;;AAKA,SAAgB,aAAa,QAAyB;CACrD,IAAI;EACH,MAAM,OAAO,OAAO,KAAK,QAAQ,WAAW,CAAC,CAAC,SAAS,OAAO;EAC9D,MAAM,UAAU,KAAK,MAAM,IAAI;EAE/B,IAAI,QAAQ,MAAM,QACjB,OAAO,IAAI,KAAK,QAAQ,CAAC;EAG1B,OAAO,QAAQ;CAChB,QAAQ;EACP,MAAM,IAAI,cAAc,MAAM;CAC/B;AACD;;;;;;;;AASA,IAAa,gBAAb,cAAmC,MAAM;CACnB;CAArB,YAAY,QAAyB;EACpC,MACC,yFACD;EAHoB,KAAA,SAAA;EAIpB,KAAK,OAAO;CACb;AACD"}
package/dist/pg/roles.cjs DELETED
@@ -1,177 +0,0 @@
1
- Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
- //#region src/pg/roles.ts
3
- /**
4
- * Everything one tenant's roles need, in dependency order.
5
- *
6
- * The owner before the schema it owns, the schema before the grants on it. A
7
- * caller applies them in sequence and may stop at the first failure without
8
- * leaving a half-granted role that looks complete.
9
- */
10
- function roleStatements(spec) {
11
- const { runtime, owner, schema, passwords, database } = spec;
12
- return [
13
- {
14
- describe: `role ${owner}`,
15
- exists: roleExists(owner),
16
- sql: `CREATE ROLE ${ident(owner)} LOGIN PASSWORD ${literal(passwords.owner)}`
17
- },
18
- passwordStatement(owner, passwords.owner),
19
- {
20
- describe: `role ${runtime}`,
21
- exists: roleExists(runtime),
22
- sql: `CREATE ROLE ${ident(runtime)} LOGIN PASSWORD ${literal(passwords.runtime)}`
23
- },
24
- passwordStatement(runtime, passwords.runtime),
25
- {
26
- describe: `${owner} granted to the connecting role`,
27
- sql: `GRANT ${ident(owner)} TO CURRENT_USER`
28
- },
29
- {
30
- describe: `schema ${schema}`,
31
- exists: {
32
- sql: "SELECT 1 FROM information_schema.schemata WHERE schema_name = $1",
33
- values: [schema]
34
- },
35
- sql: `CREATE SCHEMA ${ident(schema)} AUTHORIZATION ${ident(owner)}`
36
- },
37
- {
38
- describe: `${schema} is owned by ${owner}`,
39
- sql: `ALTER SCHEMA ${ident(schema)} OWNER TO ${ident(owner)}`
40
- },
41
- ...database ? [{
42
- describe: `${owner} may create in database ${database}`,
43
- sql: `GRANT CREATE ON DATABASE ${ident(database)} TO ${ident(owner)}`
44
- }] : [],
45
- {
46
- describe: `${runtime} may use ${schema}`,
47
- sql: `GRANT USAGE ON SCHEMA ${ident(schema)} TO ${ident(runtime)}`
48
- },
49
- {
50
- describe: `${runtime} may read and write future tables in ${schema}`,
51
- sql: `ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO ${ident(runtime)}`
52
- },
53
- {
54
- describe: `${runtime} may use future sequences in ${schema}`,
55
- sql: `ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} GRANT USAGE, SELECT ON SEQUENCES TO ${ident(runtime)}`
56
- },
57
- {
58
- describe: `${runtime} may read and write existing tables in ${schema}`,
59
- sql: `GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA ${ident(schema)} TO ${ident(runtime)}`
60
- },
61
- {
62
- describe: `${runtime} may use existing sequences in ${schema}`,
63
- sql: `GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA ${ident(schema)} TO ${ident(runtime)}`
64
- },
65
- {
66
- describe: `${runtime} resolves names in ${schema}`,
67
- sql: `ALTER ROLE ${ident(runtime)} SET search_path TO ${ident(schema)}`
68
- },
69
- {
70
- describe: `${owner} resolves names in ${schema}`,
71
- sql: `ALTER ROLE ${ident(owner)} SET search_path TO ${ident(schema)}`
72
- },
73
- ...spec.reader ? readerStatements(spec, spec.reader) : []
74
- ];
75
- }
76
- /**
77
- * The role's password, set to what the caller supplied — every time.
78
- *
79
- * `CREATE ROLE` only runs for a role that is not there, so on its own a role
80
- * keeps the password it was born with, and an app handed a different one is
81
- * locked out. Setting it on every apply is what lets the supplied password be
82
- * the truth: rotated, or re-derived, the role follows. Idempotent, so it needs
83
- * no check.
84
- */
85
- function passwordStatement(role, password) {
86
- return {
87
- describe: `${role} has its current password`,
88
- sql: `ALTER ROLE ${ident(role)} WITH LOGIN PASSWORD ${literal(password)}`
89
- };
90
- }
91
- /**
92
- * A role that may read and nothing else.
93
- *
94
- * `SELECT` and no more, on what exists and on what the owner creates later. No
95
- * `INSERT`, no `UPDATE`, no `DELETE`, and no sequence `USAGE` — a reader that
96
- * could take a sequence value could not write the row it was for, so granting
97
- * it would be surface with no use.
98
- */
99
- function readerStatements(spec, reader) {
100
- const { owner, schema, passwords } = spec;
101
- const password = passwords.reader;
102
- if (!password) throw new ReaderNeedsPassword(reader);
103
- return [
104
- {
105
- describe: `role ${reader}`,
106
- exists: roleExists(reader),
107
- sql: `CREATE ROLE ${ident(reader)} LOGIN PASSWORD ${literal(password)}`
108
- },
109
- passwordStatement(reader, password),
110
- {
111
- describe: `${reader} may use ${schema}`,
112
- sql: `GRANT USAGE ON SCHEMA ${ident(schema)} TO ${ident(reader)}`
113
- },
114
- {
115
- describe: `${reader} may read future tables in ${schema}`,
116
- sql: `ALTER DEFAULT PRIVILEGES FOR ROLE ${ident(owner)} IN SCHEMA ${ident(schema)} GRANT SELECT ON TABLES TO ${ident(reader)}`
117
- },
118
- {
119
- describe: `${reader} may read existing tables in ${schema}`,
120
- sql: `GRANT SELECT ON ALL TABLES IN SCHEMA ${ident(schema)} TO ${ident(reader)}`
121
- },
122
- {
123
- describe: `${reader} resolves names in ${schema}`,
124
- sql: `ALTER ROLE ${ident(reader)} SET search_path TO ${ident(schema)}`
125
- }
126
- ];
127
- }
128
- /** A reader role was named and no password was supplied for it. */
129
- var ReaderNeedsPassword = class extends Error {
130
- role;
131
- constructor(role) {
132
- 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.`);
133
- this.role = role;
134
- this.name = "ReaderNeedsPassword";
135
- }
136
- };
137
- /** The read-only role's name for a given runtime role. */
138
- function readerRole(runtime) {
139
- return `${runtime}_reader`;
140
- }
141
- /** The owner role's name for a given runtime role. */
142
- function ownerRole(runtime) {
143
- return `${runtime}_owner`;
144
- }
145
- function roleExists(role) {
146
- return {
147
- sql: "SELECT 1 FROM pg_roles WHERE rolname = $1",
148
- values: [role]
149
- };
150
- }
151
- /**
152
- * Quote an identifier for use in DDL.
153
- *
154
- * Names reaching here are already canonical, so this is defence rather than
155
- * sanitisation — but DDL cannot be parameterised, and an unquoted identifier is
156
- * the one place a name could ever be more than a name.
157
- */
158
- function ident(name) {
159
- return `"${name.replace(/"/g, "\"\"")}"`;
160
- }
161
- /**
162
- * Quote a string literal.
163
- *
164
- * A password cannot be a bind parameter in `CREATE ROLE`, which is the one
165
- * place in this module where a value rather than a name is interpolated — so it
166
- * is escaped rather than trusted, whatever the caller believes it derived.
167
- */
168
- function literal(value) {
169
- return `'${value.replace(/'/g, "''")}'`;
170
- }
171
- //#endregion
172
- exports.ReaderNeedsPassword = ReaderNeedsPassword;
173
- exports.ownerRole = ownerRole;
174
- exports.readerRole = readerRole;
175
- exports.roleStatements = roleStatements;
176
-
177
- //# sourceMappingURL=roles.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"roles.cjs","names":[],"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/**\n\t * The database itself, when these are a database construct's roles rather\n\t * than a schema tenant's.\n\t *\n\t * Its owner may then create in the database — which is what a migration\n\t * needs to create a trusted extension (`citext`, `pg_trgm`): Postgres asks\n\t * for `CREATE` on the database for that, not on the schema. A tenant's owner\n\t * is not given it, so it stays confined to its own schema.\n\t */\n\tdatabase?: 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, database } = 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\tpasswordStatement(owner, passwords.owner),\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\tpasswordStatement(runtime, passwords.runtime),\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...(database\n\t\t\t? [\n\t\t\t\t\t{\n\t\t\t\t\t\t// The migrator of a database, not of a tenant inside one: it may\n\t\t\t\t\t\t// create trusted extensions, which Postgres grants on the\n\t\t\t\t\t\t// database rather than the schema. Idempotent, so no check.\n\t\t\t\t\t\tdescribe: `${owner} may create in database ${database}`,\n\t\t\t\t\t\tsql: `GRANT CREATE ON DATABASE ${ident(database)} TO ${ident(owner)}`,\n\t\t\t\t\t},\n\t\t\t\t]\n\t\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 * The role's password, set to what the caller supplied — every time.\n *\n * `CREATE ROLE` only runs for a role that is not there, so on its own a role\n * keeps the password it was born with, and an app handed a different one is\n * locked out. Setting it on every apply is what lets the supplied password be\n * the truth: rotated, or re-derived, the role follows. Idempotent, so it needs\n * no check.\n */\nfunction passwordStatement(role: string, password: string): RoleStatement {\n\treturn {\n\t\tdescribe: `${role} has its current password`,\n\t\tsql: `ALTER ROLE ${ident(role)} WITH LOGIN PASSWORD ${literal(password)}`,\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\tpasswordStatement(reader, password),\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":"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"}
@@ -1,90 +0,0 @@
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
- export 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
- /**
39
- * The database itself, when these are a database construct's roles rather
40
- * than a schema tenant's.
41
- *
42
- * Its owner may then create in the database — which is what a migration
43
- * needs to create a trusted extension (`citext`, `pg_trgm`): Postgres asks
44
- * for `CREATE` on the database for that, not on the schema. A tenant's owner
45
- * is not given it, so it stays confined to its own schema.
46
- */
47
- database?: string;
48
- /** Supplied, never generated here. */
49
- passwords: {
50
- runtime: string;
51
- owner: string;
52
- reader?: string;
53
- };
54
- }
55
- /** One statement, and how to tell whether it still needs running. */
56
- export interface RoleStatement {
57
- /** What it does, for the line a reconciler prints. */
58
- describe: string;
59
- /**
60
- * Answers "does this already exist", parameterised.
61
- *
62
- * Absent means the statement is idempotent in itself and is run every time —
63
- * a `GRANT` re-granting what is already granted is a no-op, and checking
64
- * would cost a round trip to learn nothing.
65
- */
66
- exists?: {
67
- sql: string;
68
- values: unknown[];
69
- };
70
- sql: string;
71
- }
72
- /**
73
- * Everything one tenant's roles need, in dependency order.
74
- *
75
- * The owner before the schema it owns, the schema before the grants on it. A
76
- * caller applies them in sequence and may stop at the first failure without
77
- * leaving a half-granted role that looks complete.
78
- */
79
- export declare function roleStatements(spec: RoleSpec): RoleStatement[];
80
- /** A reader role was named and no password was supplied for it. */
81
- export declare class ReaderNeedsPassword extends Error {
82
- readonly role: string;
83
- constructor(role: string);
84
- }
85
- /** The read-only role's name for a given runtime role. */
86
- export declare function readerRole(runtime: string): string;
87
- /** The owner role's name for a given runtime role. */
88
- export declare function ownerRole(runtime: string): string;
89
- //#endregion
90
- //# sourceMappingURL=roles.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"roles.d.cts","names":[],"sources":["../../src/pg/roles.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;iBAqBiB;;EAEhB;;EAEA;;;;;;;;;EASA;;EAEA;;;;;;;;;;EAUA;;EAEA;IAAa;IAAiB;IAAe;;;;iBAI7B;;EAEhB;;;;;;;;EAQA;IAAW;IAAa;;EACxB;;;;;;;;;wBAUe,eAAe,MAAM,WAAW;;qBAyKnC,4BAA4B;WACnB;EAArB,YAAqB;;;wBAWN,WAAW;;wBAKX,UAAU"}