@tangen/db 0.0.0-stage → 0.1.54

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 (127) hide show
  1. package/README.md +73 -2
  2. package/drizzle/0000_flimsy_shotgun.sql +339 -0
  3. package/drizzle/0001_app_data.sql +11 -0
  4. package/drizzle/0002_add_indexes.sql +36 -0
  5. package/drizzle/0003_extension_data_rename.sql +3 -0
  6. package/drizzle/0004_extension_data_published_globals.sql +12 -0
  7. package/drizzle/0005_auth.sql +51 -0
  8. package/drizzle/0006_auth_identities.sql +13 -0
  9. package/drizzle/0007_ai.sql +18 -0
  10. package/drizzle/0008_ai_chat_threads.sql +5 -0
  11. package/drizzle/0009_forms_source_slug.sql +11 -0
  12. package/drizzle/0010_rbac_roles_status.sql +14 -0
  13. package/drizzle/0011_jobs_v2.sql +67 -0
  14. package/drizzle/0012_search_platform.sql +36 -0
  15. package/drizzle/0013_search_synonym_label.sql +2 -0
  16. package/drizzle/0014_object_types_disabled.sql +2 -0
  17. package/drizzle/0015_playground_collections.sql +25 -0
  18. package/drizzle/0016_catchup_activity_promotion.sql +36 -0
  19. package/drizzle/0017_fields_disabled.sql +3 -0
  20. package/drizzle/0018_object_types_plural.sql +1 -0
  21. package/drizzle/0019_member_profile.sql +2 -0
  22. package/drizzle/0020_paused_jobs.sql +7 -0
  23. package/drizzle/0021_form_submission_notes.sql +2 -0
  24. package/drizzle/0022_paused_jobs_env_reason.sql +4 -0
  25. package/drizzle/0023_scale_indexes.sql +3 -0
  26. package/drizzle/0024_migration_runs.sql +24 -0
  27. package/drizzle/0025_porter_transfer.sql +30 -0
  28. package/drizzle/0026_cron_timezone.sql +1 -0
  29. package/drizzle/0027_rate_limit_counters.sql +6 -0
  30. package/drizzle/0028_realtime_events.sql +10 -0
  31. package/drizzle/0030_delivery_artifacts.sql +10 -0
  32. package/drizzle/0031_realtime_presence.sql +8 -0
  33. package/drizzle/0032_schema_pushes.sql +15 -0
  34. package/drizzle/0033_studio_design_capability.sql +37 -0
  35. package/drizzle/0034_object_types_provided_by.sql +12 -0
  36. package/drizzle/0035_custom_table_owners.sql +24 -0
  37. package/drizzle/0036_revision_version_unique.sql +42 -0
  38. package/drizzle/0037_job_queue_dedupe.sql +20 -0
  39. package/drizzle/0038_search_documents.sql +87 -0
  40. package/drizzle/0039_job_prune_indexes.sql +25 -0
  41. package/drizzle/0040_extension_data_object_idx.sql +10 -0
  42. package/drizzle/0041_knowledge.sql +79 -0
  43. package/drizzle/0042_schema_pushes_checksum.sql +7 -0
  44. package/drizzle/0043_guard_adoption.sql +12 -0
  45. package/drizzle/0044_retention_indexes.sql +7 -0
  46. package/drizzle/0045_extension_install_per_env.sql +40 -0
  47. package/drizzle/0046_extension_migrations.sql +18 -0
  48. package/drizzle/0047_environment_role.sql +25 -0
  49. package/drizzle/0048_environment_role_adoption.sql +30 -0
  50. package/drizzle/0049_release_lifecycle.sql +55 -0
  51. package/drizzle/0050_member_env_membership.sql +21 -0
  52. package/drizzle/0051_remove_environments.sql +350 -0
  53. package/drizzle/0052_release_actor_columns.sql +9 -0
  54. package/drizzle/0053_kernel_event_record.sql +26 -0
  55. package/drizzle/0054_kernel_run_identity.sql +13 -0
  56. package/drizzle/0055_kernel_identities.sql +19 -0
  57. package/drizzle/0056_forms_leave_core.sql +7 -0
  58. package/drizzle/0057_auth_leave_core.sql +8 -0
  59. package/drizzle/0058_member_grants.sql +11 -0
  60. package/drizzle/0059_extension_granted_grants.sql +7 -0
  61. package/drizzle/0060_connections.sql +21 -0
  62. package/drizzle/meta/0000_snapshot.json +1692 -0
  63. package/drizzle/meta/0002_snapshot.json +1935 -0
  64. package/drizzle/meta/0003_snapshot.json +1935 -0
  65. package/drizzle/meta/_journal.json +426 -0
  66. package/package.json +85 -4
  67. package/src/cache-keys.ts +43 -0
  68. package/src/client.ts +136 -0
  69. package/src/content-ddl.ts +70 -0
  70. package/src/content-tables.ts +110 -0
  71. package/src/custom-tables-loader.ts +65 -0
  72. package/src/ensure-system-schema.ts +195 -0
  73. package/src/extension-install-state.ts +220 -0
  74. package/src/extension-migrations.ts +270 -0
  75. package/src/generated/content-tables.ts +271 -0
  76. package/src/generated/custom-tables.ts +18 -0
  77. package/src/index.ts +24 -0
  78. package/src/instrument.ts +139 -0
  79. package/src/knowledge-schema.ts +232 -0
  80. package/src/migrate.ts +67 -0
  81. package/src/prepare-policy.ts +118 -0
  82. package/src/reconcile.ts +115 -0
  83. package/src/schema/ai-chats.ts +36 -0
  84. package/src/schema/ai-settings.ts +24 -0
  85. package/src/schema/api-keys.ts +37 -0
  86. package/src/schema/audit-log.ts +54 -0
  87. package/src/schema/connections.ts +35 -0
  88. package/src/schema/custom-table-owners.ts +32 -0
  89. package/src/schema/delivery-artifacts.ts +33 -0
  90. package/src/schema/entity-tags.ts +33 -0
  91. package/src/schema/extension-data.ts +38 -0
  92. package/src/schema/extension-migrations.ts +42 -0
  93. package/src/schema/fields.ts +37 -0
  94. package/src/schema/globals.ts +35 -0
  95. package/src/schema/identities.ts +27 -0
  96. package/src/schema/index.ts +51 -0
  97. package/src/schema/jobs.ts +104 -0
  98. package/src/schema/knowledge.ts +85 -0
  99. package/src/schema/media.ts +52 -0
  100. package/src/schema/member-grants.ts +34 -0
  101. package/src/schema/members.ts +49 -0
  102. package/src/schema/migration-runs.ts +46 -0
  103. package/src/schema/object-types.ts +41 -0
  104. package/src/schema/objects.ts +38 -0
  105. package/src/schema/paused-jobs.ts +25 -0
  106. package/src/schema/playground.ts +43 -0
  107. package/src/schema/porter-transfer.ts +47 -0
  108. package/src/schema/realtime.ts +45 -0
  109. package/src/schema/releases.ts +95 -0
  110. package/src/schema/roles.ts +27 -0
  111. package/src/schema/saved-views.ts +33 -0
  112. package/src/schema/schema-pushes.ts +43 -0
  113. package/src/schema/search-documents.ts +47 -0
  114. package/src/schema/search-query-log.ts +24 -0
  115. package/src/schema/search-recommendations.ts +27 -0
  116. package/src/schema/search-synonyms.ts +23 -0
  117. package/src/schema/webhook-deliveries.ts +37 -0
  118. package/src/schema/webhooks.ts +36 -0
  119. package/src/schema/workspace-activity.ts +35 -0
  120. package/src/schema/workspace-domains.ts +24 -0
  121. package/src/schema/workspace-installed-apps.ts +48 -0
  122. package/src/schema/workspaces.ts +24 -0
  123. package/src/schema-kit.ts +64 -0
  124. package/src/sealed.ts +84 -0
  125. package/src/seed/skeleton.ts +77 -0
  126. package/src/sql/apply-gin-indexes.ts +31 -0
  127. package/src/sql/gin-indexes.sql +11 -0
@@ -0,0 +1,426 @@
1
+ {
2
+ "version": "6",
3
+ "dialect": "postgresql",
4
+ "entries": [
5
+ {
6
+ "idx": 0,
7
+ "version": "6",
8
+ "when": 1782310803741,
9
+ "tag": "0000_flimsy_shotgun",
10
+ "breakpoints": true
11
+ },
12
+ {
13
+ "idx": 1,
14
+ "version": "6",
15
+ "when": 1782400000000,
16
+ "tag": "0001_app_data",
17
+ "breakpoints": true
18
+ },
19
+ {
20
+ "idx": 2,
21
+ "version": "6",
22
+ "when": 1782405813836,
23
+ "tag": "0002_add_indexes",
24
+ "breakpoints": true
25
+ },
26
+ {
27
+ "idx": 3,
28
+ "version": "6",
29
+ "when": 1782410000000,
30
+ "tag": "0003_extension_data_rename",
31
+ "breakpoints": true
32
+ },
33
+ {
34
+ "idx": 4,
35
+ "version": "6",
36
+ "when": 1782420000000,
37
+ "tag": "0004_extension_data_published_globals",
38
+ "breakpoints": true
39
+ },
40
+ {
41
+ "idx": 5,
42
+ "version": "6",
43
+ "when": 1782500000000,
44
+ "tag": "0005_auth",
45
+ "breakpoints": true
46
+ },
47
+ {
48
+ "idx": 6,
49
+ "version": "6",
50
+ "when": 1782600000000,
51
+ "tag": "0006_auth_identities",
52
+ "breakpoints": true
53
+ },
54
+ {
55
+ "idx": 7,
56
+ "version": "6",
57
+ "when": 1782700000000,
58
+ "tag": "0007_ai",
59
+ "breakpoints": true
60
+ },
61
+ {
62
+ "idx": 8,
63
+ "version": "6",
64
+ "when": 1782800000000,
65
+ "tag": "0008_ai_chat_threads",
66
+ "breakpoints": true
67
+ },
68
+ {
69
+ "idx": 9,
70
+ "version": "6",
71
+ "when": 1782900000000,
72
+ "tag": "0009_forms_source_slug",
73
+ "breakpoints": true
74
+ },
75
+ {
76
+ "idx": 10,
77
+ "version": "6",
78
+ "when": 1783000000000,
79
+ "tag": "0010_rbac_roles_status",
80
+ "breakpoints": true
81
+ },
82
+ {
83
+ "idx": 11,
84
+ "version": "6",
85
+ "when": 1783900000000,
86
+ "tag": "0011_jobs_v2",
87
+ "breakpoints": true
88
+ },
89
+ {
90
+ "idx": 12,
91
+ "version": "6",
92
+ "when": 1784000000000,
93
+ "tag": "0012_search_platform",
94
+ "breakpoints": true
95
+ },
96
+ {
97
+ "idx": 13,
98
+ "version": "6",
99
+ "when": 1784100000000,
100
+ "tag": "0013_search_synonym_label",
101
+ "breakpoints": true
102
+ },
103
+ {
104
+ "idx": 14,
105
+ "version": "6",
106
+ "when": 1784200000000,
107
+ "tag": "0014_object_types_disabled",
108
+ "breakpoints": true
109
+ },
110
+ {
111
+ "idx": 15,
112
+ "version": "6",
113
+ "when": 1784300000000,
114
+ "tag": "0015_playground_collections",
115
+ "breakpoints": true
116
+ },
117
+ {
118
+ "idx": 16,
119
+ "version": "6",
120
+ "when": 1784390000000,
121
+ "tag": "0016_catchup_activity_promotion",
122
+ "breakpoints": true
123
+ },
124
+ {
125
+ "idx": 17,
126
+ "version": "6",
127
+ "when": 1784480000000,
128
+ "tag": "0017_fields_disabled",
129
+ "breakpoints": true
130
+ },
131
+ {
132
+ "idx": 18,
133
+ "version": "6",
134
+ "when": 1784490000000,
135
+ "tag": "0018_object_types_plural",
136
+ "breakpoints": true
137
+ },
138
+ {
139
+ "idx": 19,
140
+ "version": "6",
141
+ "when": 1784500000000,
142
+ "tag": "0019_member_profile",
143
+ "breakpoints": true
144
+ },
145
+ {
146
+ "idx": 20,
147
+ "version": "6",
148
+ "when": 1784510000000,
149
+ "tag": "0020_paused_jobs",
150
+ "breakpoints": true
151
+ },
152
+ {
153
+ "idx": 21,
154
+ "version": "6",
155
+ "when": 1785000000000,
156
+ "tag": "0021_form_submission_notes",
157
+ "breakpoints": true
158
+ },
159
+ {
160
+ "idx": 22,
161
+ "version": "6",
162
+ "when": 1785100000000,
163
+ "tag": "0022_paused_jobs_env_reason",
164
+ "breakpoints": true
165
+ },
166
+ {
167
+ "idx": 23,
168
+ "version": "6",
169
+ "when": 1785200000000,
170
+ "tag": "0023_scale_indexes",
171
+ "breakpoints": true
172
+ },
173
+ {
174
+ "idx": 24,
175
+ "version": "6",
176
+ "when": 1785300000000,
177
+ "tag": "0024_migration_runs",
178
+ "breakpoints": true
179
+ },
180
+ {
181
+ "idx": 25,
182
+ "version": "6",
183
+ "when": 1785400000000,
184
+ "tag": "0025_porter_transfer",
185
+ "breakpoints": true
186
+ },
187
+ {
188
+ "idx": 26,
189
+ "version": "6",
190
+ "when": 1785500000000,
191
+ "tag": "0026_cron_timezone",
192
+ "breakpoints": true
193
+ },
194
+ {
195
+ "idx": 27,
196
+ "version": "6",
197
+ "when": 1785600000000,
198
+ "tag": "0027_rate_limit_counters",
199
+ "breakpoints": true
200
+ },
201
+ {
202
+ "idx": 28,
203
+ "version": "6",
204
+ "when": 1785700000000,
205
+ "tag": "0028_realtime_events",
206
+ "breakpoints": true
207
+ },
208
+ {
209
+ "idx": 29,
210
+ "version": "6",
211
+ "when": 1785900000000,
212
+ "tag": "0030_delivery_artifacts",
213
+ "breakpoints": true
214
+ },
215
+ {
216
+ "idx": 30,
217
+ "version": "6",
218
+ "when": 1786000000000,
219
+ "tag": "0031_realtime_presence",
220
+ "breakpoints": true
221
+ },
222
+ {
223
+ "idx": 31,
224
+ "version": "6",
225
+ "when": 1786100000000,
226
+ "tag": "0032_schema_pushes",
227
+ "breakpoints": true
228
+ },
229
+ {
230
+ "idx": 32,
231
+ "version": "6",
232
+ "when": 1786200000000,
233
+ "tag": "0033_studio_design_capability",
234
+ "breakpoints": true
235
+ },
236
+ {
237
+ "idx": 33,
238
+ "version": "6",
239
+ "when": 1786300000000,
240
+ "tag": "0034_object_types_provided_by",
241
+ "breakpoints": true
242
+ },
243
+ {
244
+ "idx": 34,
245
+ "version": "6",
246
+ "when": 1786400000000,
247
+ "tag": "0035_custom_table_owners",
248
+ "breakpoints": true
249
+ },
250
+ {
251
+ "idx": 35,
252
+ "version": "6",
253
+ "when": 1786500000000,
254
+ "tag": "0036_revision_version_unique",
255
+ "breakpoints": true
256
+ },
257
+ {
258
+ "idx": 36,
259
+ "version": "6",
260
+ "when": 1786600000000,
261
+ "tag": "0037_job_queue_dedupe",
262
+ "breakpoints": true
263
+ },
264
+ {
265
+ "idx": 37,
266
+ "version": "6",
267
+ "when": 1786700000000,
268
+ "tag": "0038_search_documents",
269
+ "breakpoints": true
270
+ },
271
+ {
272
+ "idx": 38,
273
+ "version": "6",
274
+ "when": 1786800000000,
275
+ "tag": "0039_job_prune_indexes",
276
+ "breakpoints": true
277
+ },
278
+ {
279
+ "idx": 39,
280
+ "version": "6",
281
+ "when": 1786900000000,
282
+ "tag": "0040_extension_data_object_idx",
283
+ "breakpoints": true
284
+ },
285
+ {
286
+ "idx": 40,
287
+ "version": "6",
288
+ "when": 1787000000000,
289
+ "tag": "0041_knowledge",
290
+ "breakpoints": true
291
+ },
292
+ {
293
+ "idx": 41,
294
+ "version": "6",
295
+ "when": 1787100000000,
296
+ "tag": "0042_schema_pushes_checksum",
297
+ "breakpoints": true
298
+ },
299
+ {
300
+ "idx": 42,
301
+ "version": "6",
302
+ "when": 1787200000000,
303
+ "tag": "0043_guard_adoption",
304
+ "breakpoints": true
305
+ },
306
+ {
307
+ "idx": 43,
308
+ "version": "6",
309
+ "when": 1787300000000,
310
+ "tag": "0044_retention_indexes",
311
+ "breakpoints": true
312
+ },
313
+ {
314
+ "idx": 44,
315
+ "version": "6",
316
+ "when": 1787400000000,
317
+ "tag": "0045_extension_install_per_env",
318
+ "breakpoints": true
319
+ },
320
+ {
321
+ "idx": 45,
322
+ "version": "6",
323
+ "when": 1787400100000,
324
+ "tag": "0046_extension_migrations",
325
+ "breakpoints": true
326
+ },
327
+ {
328
+ "idx": 46,
329
+ "version": "6",
330
+ "when": 1787400200000,
331
+ "tag": "0047_environment_role",
332
+ "breakpoints": true
333
+ },
334
+ {
335
+ "idx": 47,
336
+ "version": "6",
337
+ "when": 1787400300000,
338
+ "tag": "0048_environment_role_adoption",
339
+ "breakpoints": true
340
+ },
341
+ {
342
+ "idx": 48,
343
+ "version": "6",
344
+ "when": 1787400400000,
345
+ "tag": "0049_release_lifecycle",
346
+ "breakpoints": true
347
+ },
348
+ {
349
+ "idx": 49,
350
+ "version": "6",
351
+ "when": 1787400500000,
352
+ "tag": "0050_member_env_membership",
353
+ "breakpoints": true
354
+ },
355
+ {
356
+ "idx": 50,
357
+ "version": "6",
358
+ "when": 1787400600000,
359
+ "tag": "0051_remove_environments",
360
+ "breakpoints": true
361
+ },
362
+ {
363
+ "idx": 51,
364
+ "version": "6",
365
+ "when": 1787400700000,
366
+ "tag": "0052_release_actor_columns",
367
+ "breakpoints": true
368
+ },
369
+ {
370
+ "idx": 52,
371
+ "version": "6",
372
+ "when": 1787400800000,
373
+ "tag": "0053_kernel_event_record",
374
+ "breakpoints": true
375
+ },
376
+ {
377
+ "idx": 53,
378
+ "version": "6",
379
+ "when": 1787400900000,
380
+ "tag": "0054_kernel_run_identity",
381
+ "breakpoints": true
382
+ },
383
+ {
384
+ "idx": 54,
385
+ "version": "6",
386
+ "when": 1787401000000,
387
+ "tag": "0055_kernel_identities",
388
+ "breakpoints": true
389
+ },
390
+ {
391
+ "idx": 55,
392
+ "version": "6",
393
+ "when": 1787500000000,
394
+ "tag": "0056_forms_leave_core",
395
+ "breakpoints": true
396
+ },
397
+ {
398
+ "idx": 56,
399
+ "version": "6",
400
+ "when": 1787500100000,
401
+ "tag": "0057_auth_leave_core",
402
+ "breakpoints": true
403
+ },
404
+ {
405
+ "idx": 57,
406
+ "version": "6",
407
+ "when": 1787500200000,
408
+ "tag": "0058_member_grants",
409
+ "breakpoints": true
410
+ },
411
+ {
412
+ "idx": 58,
413
+ "version": "6",
414
+ "when": 1787500260000,
415
+ "tag": "0059_extension_granted_grants",
416
+ "breakpoints": true
417
+ },
418
+ {
419
+ "idx": 59,
420
+ "version": "6",
421
+ "when": 1787500320000,
422
+ "tag": "0060_connections",
423
+ "breakpoints": true
424
+ }
425
+ ]
426
+ }
package/package.json CHANGED
@@ -1,6 +1,87 @@
1
1
  {
2
2
  "name": "@tangen/db",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.54",
4
+ "description": "Database client and system schema (Drizzle + postgres.js) for the Tangen headless CMS",
5
+ "homepage": "https://github.com/raghunathj/tangen#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/raghunathj/tangen/issues"
8
+ },
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/raghunathj/tangen.git",
13
+ "directory": "packages/db"
14
+ },
15
+ "type": "module",
16
+ "main": "./src/index.ts",
17
+ "types": "./src/index.ts",
18
+ "exports": {
19
+ ".": {
20
+ "default": "./src/index.ts"
21
+ },
22
+ "./migrate": {
23
+ "default": "./src/migrate.ts"
24
+ },
25
+ "./content-tables": {
26
+ "default": "./src/content-tables.ts"
27
+ },
28
+ "./custom-tables": {
29
+ "default": "./src/custom-tables-loader.ts"
30
+ },
31
+ "./schema-kit": {
32
+ "default": "./src/schema-kit.ts"
33
+ },
34
+ "./reconcile": {
35
+ "default": "./src/reconcile.ts"
36
+ }
37
+ },
38
+ "files": [
39
+ "src",
40
+ "!src/**/__tests__/**",
41
+ "!src/**/*.test.ts",
42
+ "drizzle",
43
+ "README.md"
44
+ ],
45
+ "publishConfig": {
46
+ "access": "public",
47
+ "main": "./src/index.ts",
48
+ "exports": {
49
+ ".": {
50
+ "default": "./src/index.ts"
51
+ },
52
+ "./migrate": {
53
+ "default": "./src/migrate.ts"
54
+ },
55
+ "./content-tables": {
56
+ "default": "./src/content-tables.ts"
57
+ },
58
+ "./custom-tables": {
59
+ "default": "./src/custom-tables-loader.ts"
60
+ },
61
+ "./schema-kit": {
62
+ "default": "./src/schema-kit.ts"
63
+ },
64
+ "./reconcile": {
65
+ "default": "./src/reconcile.ts"
66
+ }
67
+ }
68
+ },
69
+ "scripts": {
70
+ "build": "bun build ./src/index.ts --outdir dist --target node --format esm --packages external && tsc --emitDeclarationOnly -p tsconfig.build.json",
71
+ "db:gin-indexes": "bun --env-file=../../.env run src/sql/apply-gin-indexes.ts",
72
+ "db:generate": "bun --env-file=../../.env drizzle-kit generate",
73
+ "db:migrate": "bun --env-file=../../.env run src/migrate.ts",
74
+ "db:reconcile": "bun --env-file=../../.env run src/reconcile.ts",
75
+ "db:studio": "bun --env-file=../../.env drizzle-kit studio",
76
+ "seed:skeleton": "bun --env-file=../../.env run src/seed/skeleton.ts",
77
+ "test": "bun test"
78
+ },
79
+ "dependencies": {
80
+ "drizzle-orm": "^0.45.2",
81
+ "postgres": "^3.4.4",
82
+ "@tangen/types": "^0.2.0"
83
+ },
84
+ "devDependencies": {
85
+ "drizzle-kit": "^0.21.4"
86
+ }
87
+ }
@@ -0,0 +1,43 @@
1
+ /**
2
+ * File Name: cache-keys.ts
3
+ * Purpose: The ONE place the Redis key namespace is defined. Cache keys (schema,
4
+ * features, stats) are env-id scoped and env ids are global slugs (env_development),
5
+ * so two different projects sharing one Redis would collide. Every key is namespaced
6
+ * by a fingerprint of the project's database URL. Both the API (which writes/reads the
7
+ * cache) and the CLI (which invalidates it on push) MUST derive the prefix identically —
8
+ * if they drift, push's invalidation silently misses and the API serves a stale schema
9
+ * until the TTL expires.
10
+ *
11
+ * Author: Raghunath J
12
+ * License: MIT
13
+ *
14
+ * Dependencies/Notes: Uses Bun.hash — both the API and the CLI run under Bun.
15
+ */
16
+
17
+ /**
18
+ * The Redis key prefix for a project, derived from its database URL. Applied by the
19
+ * API's ioredis client as `keyPrefix`, and prepended by the CLI when it invalidates.
20
+ */
21
+ export function projectRedisPrefix(dbUrl: string | undefined | null): string {
22
+ const fingerprint = Bun.hash(dbUrl || 'local').toString(36).slice(0, 10)
23
+ return `tgn:${fingerprint}:`
24
+ }
25
+
26
+ /** The (un-prefixed) schema cache key for an env — the prefix is applied separately. */
27
+ export function schemaCacheKey(): string {
28
+ return 'tangen:schema'
29
+ }
30
+
31
+ /** The fully-qualified schema cache key (prefix + key) — used where the client has no keyPrefix. */
32
+ export function fullSchemaCacheKey(dbUrl: string | undefined | null): string {
33
+ return projectRedisPrefix(dbUrl) + schemaCacheKey()
34
+ }
35
+
36
+ /**
37
+ * The NOTIFY channel a schema push announces itself on, after its transaction
38
+ * has committed. The payload is the environment id whose schema changed - the
39
+ * one dimension the schema query is keyed by - so a listening API drops
40
+ * exactly that entry. Shared here because the CLI sends and the API listens,
41
+ * and neither may import the other.
42
+ */
43
+ export const SCHEMA_CHANNEL = 'tangen_schema'
package/src/client.ts ADDED
@@ -0,0 +1,136 @@
1
+ /**
2
+ * File Name: client.ts
3
+ * Purpose: Creates and returns a typed Drizzle ORM database client backed by a
4
+ * postgres.js connection pool, with an `end()` helper for clean shutdown.
5
+ *
6
+ * Author: Raghunath J
7
+ * License: MIT
8
+ *
9
+ * Dependencies/Notes: Uses drizzle-orm/postgres-js adapter. NOTICE-level messages
10
+ * from PostgreSQL are suppressed; higher-severity messages are
11
+ * forwarded to console.warn.
12
+ */
13
+ import postgres from 'postgres'
14
+ import { drizzle } from 'drizzle-orm/postgres-js'
15
+ import * as schema from './schema'
16
+ import { instrumentSql } from './instrument'
17
+ import { applyPreparePolicy } from './prepare-policy'
18
+
19
+ /** Connection pool tuning options for createDb. */
20
+ export interface DbPoolOptions {
21
+ /** Maximum pool connections (postgres.js default: 10). */
22
+ max?: number
23
+ /** Seconds an idle connection is kept before being closed. */
24
+ idleTimeout?: number
25
+ /** Seconds to wait when establishing a connection. */
26
+ connectTimeout?: number
27
+ /**
28
+ * Prepared execution for ordinary parameterized queries. Defaults to TRUE.
29
+ * Set false only for a proxy or pooler that cannot carry named statements -
30
+ * see `resolvePrepare` for the deployment contract.
31
+ */
32
+ prepare?: boolean
33
+ }
34
+
35
+ /** Where the effective preparation setting came from. */
36
+ export type PrepareSource = 'option' | 'env' | 'default'
37
+
38
+ /**
39
+ * The preparation policy, and why. Tangen owns this contract rather than
40
+ * inheriting postgres.js's default, so an unset variable has one documented
41
+ * meaning instead of whatever the driver happens to do.
42
+ *
43
+ * Precedence, unchanged by the default flip: an explicit `poolOptions.prepare`
44
+ * from a caller wins, then `TANGEN_DB_PREPARE`, then the default.
45
+ *
46
+ * The default is TRUE. Prepared execution removes one PostgreSQL protocol
47
+ * round trip from every repeated parameterized statement, which on a remote
48
+ * database is a large share of every read and write. Direct PostgreSQL and
49
+ * PgBouncer session pooling support it; so does PgBouncer 1.21+ in transaction
50
+ * pooling with `max_prepared_statements` above zero. A pooler or proxy that
51
+ * cannot carry named statements needs `TANGEN_DB_PREPARE=false`, which is a
52
+ * supported and permanent escape hatch, never overridden.
53
+ *
54
+ * Only the exact strings `true` and `false` are recognised; anything else is
55
+ * treated as unset, and `tangen doctor` reports the effective value with its
56
+ * source so a typo is visible rather than silent.
57
+ *
58
+ * @param opts - Explicit caller options.
59
+ * @param env - Environment map (injectable for tests).
60
+ * @returns The effective value and where it came from.
61
+ */
62
+ export function resolvePrepare(
63
+ opts: Pick<DbPoolOptions, 'prepare'> = {},
64
+ env: Record<string, string | undefined> = process.env,
65
+ ): { prepare: boolean; source: PrepareSource } {
66
+ if (opts.prepare !== undefined) return { prepare: opts.prepare, source: 'option' }
67
+ if (env.TANGEN_DB_PREPARE === 'false') return { prepare: false, source: 'env' }
68
+ if (env.TANGEN_DB_PREPARE === 'true') return { prepare: true, source: 'env' }
69
+ return { prepare: true, source: 'default' }
70
+ }
71
+
72
+ /**
73
+ * Resolves postgres.js pool options from explicit options with env-var fallbacks
74
+ * (TANGEN_DB_POOL_MAX, TANGEN_DB_IDLE_TIMEOUT, TANGEN_DB_CONNECT_TIMEOUT,
75
+ * TANGEN_DB_PREPARE). Unset SIZE values are omitted so postgres.js defaults
76
+ * apply; `prepare` is always set, because Tangen decides that one.
77
+ * @param opts - Explicit pool options; win over environment variables.
78
+ * @param env - Environment map (injectable for tests; defaults to process.env).
79
+ * @returns Options object in postgres.js naming (max, idle_timeout, ...).
80
+ */
81
+ export function resolvePoolOptions(
82
+ opts: DbPoolOptions,
83
+ env: Record<string, string | undefined> = process.env,
84
+ ): Record<string, number | boolean> {
85
+ const out: Record<string, number | boolean> = {}
86
+
87
+ const num = (explicit: number | undefined, envKey: string): number | undefined => {
88
+ if (explicit !== undefined) return explicit
89
+ const raw = env[envKey]
90
+ if (raw === undefined) return undefined
91
+ const n = Number(raw)
92
+ return Number.isFinite(n) ? n : undefined
93
+ }
94
+
95
+ const max = num(opts.max, 'TANGEN_DB_POOL_MAX')
96
+ if (max !== undefined) out.max = max
97
+ const idle = num(opts.idleTimeout, 'TANGEN_DB_IDLE_TIMEOUT')
98
+ if (idle !== undefined) out.idle_timeout = idle
99
+ const connect = num(opts.connectTimeout, 'TANGEN_DB_CONNECT_TIMEOUT')
100
+ if (connect !== undefined) out.connect_timeout = connect
101
+
102
+ out.prepare = resolvePrepare(opts, env).prepare
103
+
104
+ return out
105
+ }
106
+
107
+ /**
108
+ * Creates a Drizzle database client wrapping a postgres.js connection pool.
109
+ * @param databaseUrl - PostgreSQL connection string
110
+ * @param poolOptions - Optional pool tuning; env vars used as fallback
111
+ * @returns A Drizzle client extended with an `end()` method to close the pool
112
+ */
113
+ export function createDb(databaseUrl: string, poolOptions: DbPoolOptions = {}) {
114
+ const resolved = resolvePoolOptions(poolOptions)
115
+ const client = postgres(databaseUrl, {
116
+ ...resolved,
117
+ onnotice: (n) => {
118
+ if (n.severity !== 'NOTICE') console.warn(`[db] ${n.severity}: ${n.message}`)
119
+ },
120
+ })
121
+ // Query Monitor tap: in-place, identity-preserving, inert until a hook is
122
+ // installed via setQueryHook (one ref read per query when off).
123
+ instrumentSql(client)
124
+ // Preparation policy: postgres.js executes every drizzle query unprepared
125
+ // whatever the pool says, so asking for it has to be threaded through by
126
+ // hand. Engages ONLY on an explicit `true`, so an unset TANGEN_DB_PREPARE
127
+ // keeps the behaviour it has today. See prepare-policy.ts.
128
+ applyPreparePolicy(client, resolved.prepare === true)
129
+ const db = drizzle(client, { schema })
130
+ // $rawClient: the underlying postgres.js client — for features that need
131
+ // parameterized raw SQL (e.g. @tangen/knowledge pgvector ops) without
132
+ // reaching into drizzle internals.
133
+ return Object.assign(db, { end: () => client.end(), $rawClient: client })
134
+ }
135
+
136
+ export type DbClient = ReturnType<typeof createDb>