pipework 0.252.0 → 0.253.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 (695) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/dist/infra/support/handle.d.ts +8 -1
  3. package/dist/infra/support/handle.d.ts.map +1 -1
  4. package/dist/infra/support/handle.js +9 -2
  5. package/dist/infra/support/handle.js.map +1 -1
  6. package/dist/infra/support/namespace.d.ts.map +1 -1
  7. package/package.json +4 -3
  8. package/src/async/jobs/abortable-delay.ts +18 -0
  9. package/src/async/jobs/claims.ts +52 -0
  10. package/src/async/jobs/cron.ts +179 -0
  11. package/src/async/jobs/errors.ts +56 -0
  12. package/src/async/jobs/exclusion.ts +117 -0
  13. package/src/async/jobs/execute.ts +219 -0
  14. package/src/async/jobs/index.ts +10 -0
  15. package/src/async/jobs/listener.ts +135 -0
  16. package/src/async/jobs/namespace.ts +20 -0
  17. package/src/async/jobs/queue-door.ts +44 -0
  18. package/src/async/jobs/queue.ts +1012 -0
  19. package/src/async/jobs/run-script.ts +67 -0
  20. package/src/async/jobs/scheduler.ts +218 -0
  21. package/src/async/jobs/schedules-table.ts +37 -0
  22. package/src/async/jobs/serialize-error.ts +48 -0
  23. package/src/async/jobs/table.ts +191 -0
  24. package/src/async/pipeline/define.ts +53 -0
  25. package/src/async/pipeline/errors.ts +35 -0
  26. package/src/async/pipeline/execute.ts +53 -0
  27. package/src/async/pipeline/index.ts +13 -0
  28. package/src/async/pipeline/namespace.ts +12 -0
  29. package/src/async/pipeline/resume.ts +67 -0
  30. package/src/async/pipeline/run-steps.ts +98 -0
  31. package/src/async/pipeline/types.ts +57 -0
  32. package/src/async/state-machine/define.ts +64 -0
  33. package/src/async/state-machine/errors.ts +29 -0
  34. package/src/async/state-machine/index.ts +3 -0
  35. package/src/async/state-machine/namespace.ts +12 -0
  36. package/src/async/state-machine/transition.ts +90 -0
  37. package/src/async/state-machine/types.ts +37 -0
  38. package/src/async/steps/errors.ts +31 -0
  39. package/src/async/steps/graph.ts +1214 -0
  40. package/src/async/steps/hash.ts +40 -0
  41. package/src/async/steps/index.ts +7 -0
  42. package/src/async/steps/namespace.ts +7 -0
  43. package/src/async/steps/table.ts +97 -0
  44. package/src/async/steps/types.ts +318 -0
  45. package/src/audit/chain/index.ts +9 -0
  46. package/src/audit/chain/install.ts +113 -0
  47. package/src/audit/chain/retire.ts +52 -0
  48. package/src/audit/chain/verify.ts +257 -0
  49. package/src/audit/query/cursor.ts +37 -0
  50. package/src/audit/query/index.ts +9 -0
  51. package/src/audit/query/query.ts +223 -0
  52. package/src/audit/schema/audit-record-fields.ts +68 -0
  53. package/src/audit/schema/audit-record.ts +23 -0
  54. package/src/auth/auth/audit.ts +31 -0
  55. package/src/auth/auth/chain.ts +84 -0
  56. package/src/auth/auth/cookie.ts +136 -0
  57. package/src/auth/auth/email-links.ts +311 -0
  58. package/src/auth/auth/guard.ts +128 -0
  59. package/src/auth/auth/index.ts +18 -0
  60. package/src/auth/auth/multi-org.ts +337 -0
  61. package/src/auth/auth/namespace.ts +34 -0
  62. package/src/auth/auth/parse-expiry.ts +13 -0
  63. package/src/auth/auth/passwords.ts +110 -0
  64. package/src/auth/auth/schema.ts +110 -0
  65. package/src/auth/auth/sessions.ts +565 -0
  66. package/src/auth/auth/sign-in.ts +156 -0
  67. package/src/auth/auth/signing-keys.ts +69 -0
  68. package/src/auth/auth/types.ts +26 -0
  69. package/src/auth/auth/uuid.ts +10 -0
  70. package/src/auth/rbac/cache.ts +55 -0
  71. package/src/auth/rbac/check.ts +30 -0
  72. package/src/auth/rbac/create.ts +35 -0
  73. package/src/auth/rbac/index.ts +5 -0
  74. package/src/auth/rbac/middleware.ts +20 -0
  75. package/src/auth/rbac/namespace.ts +15 -0
  76. package/src/auth/rbac/resolve.ts +31 -0
  77. package/src/auth/rbac/schema.sql +31 -0
  78. package/src/auth/rbac/seed.ts +67 -0
  79. package/src/auth/rbac/tables.ts +32 -0
  80. package/src/auth/rbac/types.ts +65 -0
  81. package/src/auth/tenant/extract.ts +33 -0
  82. package/src/auth/tenant/in-tenant.ts +49 -0
  83. package/src/auth/tenant/index.ts +7 -0
  84. package/src/auth/tenant/namespace.ts +23 -0
  85. package/src/auth/tenant/propagate.ts +20 -0
  86. package/src/auth/tenant/rls.ts +120 -0
  87. package/src/auth/tenant/scope.ts +33 -0
  88. package/src/auth/tenant/scoped-db.ts +581 -0
  89. package/src/auth/tenant/types.ts +14 -0
  90. package/src/auth/tenant/validate.ts +27 -0
  91. package/src/cli/commands/add.ts +158 -0
  92. package/src/cli/commands/alerts.ts +98 -0
  93. package/src/cli/commands/check.ts +1418 -0
  94. package/src/cli/commands/codemod.ts +78 -0
  95. package/src/cli/commands/db.ts +356 -0
  96. package/src/cli/commands/dev.ts +101 -0
  97. package/src/cli/commands/generate.ts +75 -0
  98. package/src/cli/commands/init.ts +36 -0
  99. package/src/cli/commands/install.ts +28 -0
  100. package/src/cli/commands/lint.ts +62 -0
  101. package/src/cli/commands/migrate.ts +132 -0
  102. package/src/cli/commands/openapi.ts +63 -0
  103. package/src/cli/commands/remove.ts +72 -0
  104. package/src/cli/commands/run.ts +50 -0
  105. package/src/cli/commands/scope.ts +54 -0
  106. package/src/cli/commands/serve.ts +68 -0
  107. package/src/cli/commands/shard.ts +31 -0
  108. package/src/cli/commands/shared/config-pipework-version.ts +28 -0
  109. package/src/cli/commands/shared/find-config.ts +22 -0
  110. package/src/cli/commands/shared/load-config.ts +35 -0
  111. package/src/cli/commands/shared/pnpm-install.ts +95 -0
  112. package/src/cli/commands/shared/report-removed.ts +8 -0
  113. package/src/cli/commands/sync.ts +32 -0
  114. package/src/cli/commands/test.ts +307 -0
  115. package/src/cli/commands/upgrade.ts +48 -0
  116. package/src/cli/dev-bootstrap.ts +24 -0
  117. package/src/cli/errors.ts +10 -0
  118. package/src/cli/index.ts +303 -0
  119. package/src/cli/output.ts +17 -0
  120. package/src/cli/port.ts +15 -0
  121. package/src/cli/surface-kinds.ts +20 -0
  122. package/src/client/index.ts +4 -0
  123. package/src/client/poll.ts +130 -0
  124. package/src/client/report-fault.ts +119 -0
  125. package/src/core/config/app-pool.ts +19 -0
  126. package/src/core/config/aws-env.ts +29 -0
  127. package/src/core/config/base-dir.ts +24 -0
  128. package/src/core/config/discover.ts +64 -0
  129. package/src/core/config/env-resolve.ts +87 -0
  130. package/src/core/config/env-types.ts +116 -0
  131. package/src/core/config/env.ts +55 -0
  132. package/src/core/config/errors.ts +9 -0
  133. package/src/core/config/import-config.ts +40 -0
  134. package/src/core/config/index.ts +8 -0
  135. package/src/core/config/load.ts +417 -0
  136. package/src/core/config/namespace.ts +14 -0
  137. package/src/core/config/resolve.ts +195 -0
  138. package/src/core/config/schema.ts +488 -0
  139. package/src/core/config/statement-observer.ts +48 -0
  140. package/src/core/config/ts-inline-hooks.ts +75 -0
  141. package/src/core/config/ts-register.ts +4 -0
  142. package/src/core/config/ts-resolve-hooks.ts +101 -0
  143. package/src/core/errors/base.ts +49 -0
  144. package/src/core/errors/http.ts +146 -0
  145. package/src/core/errors/index.ts +2 -0
  146. package/src/core/errors/namespace.ts +31 -0
  147. package/src/core/lifecycle/drain.ts +33 -0
  148. package/src/core/lifecycle/health.ts +72 -0
  149. package/src/core/lifecycle/index.ts +4 -0
  150. package/src/core/lifecycle/namespace.ts +7 -0
  151. package/src/core/lifecycle/startup.ts +102 -0
  152. package/src/core/lifecycle/types.ts +36 -0
  153. package/src/core/logging/context.ts +60 -0
  154. package/src/core/logging/error-chain.ts +68 -0
  155. package/src/core/logging/index.ts +5 -0
  156. package/src/core/logging/logger.ts +65 -0
  157. package/src/core/logging/namespace.ts +16 -0
  158. package/src/core/logging/proxy.ts +22 -0
  159. package/src/core/logging/redact-match.ts +24 -0
  160. package/src/core/logging/types.ts +47 -0
  161. package/src/core/pipework.ts +566 -0
  162. package/src/core/runtime/broker.ts +69 -0
  163. package/src/core/surface/http.ts +133 -0
  164. package/src/core/surface/index.ts +11 -0
  165. package/src/core/surface/job-timeout.ts +40 -0
  166. package/src/core/surface/namespace.ts +13 -0
  167. package/src/core/surface/script.ts +33 -0
  168. package/src/core/surface/start.ts +142 -0
  169. package/src/core/surface/types.ts +225 -0
  170. package/src/core/surface/validate.ts +88 -0
  171. package/src/core/surface/worker.ts +528 -0
  172. package/src/core/validation/connectivity.ts +49 -0
  173. package/src/core/validation/domain.ts +99 -0
  174. package/src/core/validation/index.ts +5 -0
  175. package/src/core/validation/queue-tables.ts +312 -0
  176. package/src/core/validation/rls-role.ts +138 -0
  177. package/src/core/validation/startup.ts +51 -0
  178. package/src/core/validation/surfaces.ts +65 -0
  179. package/src/core/validation/types.ts +25 -0
  180. package/src/data/db/advisory-lock.ts +55 -0
  181. package/src/data/db/aggregates.ts +172 -0
  182. package/src/data/db/array-elements.ts +111 -0
  183. package/src/data/db/bulk-set.ts +53 -0
  184. package/src/data/db/col/index.ts +14 -0
  185. package/src/data/db/composed.ts +53 -0
  186. package/src/data/db/db.ts +276 -0
  187. package/src/data/db/excluded.ts +16 -0
  188. package/src/data/db/expressions.ts +473 -0
  189. package/src/data/db/filter.ts +90 -0
  190. package/src/data/db/function-source.ts +114 -0
  191. package/src/data/db/identifiers.ts +16 -0
  192. package/src/data/db/idx/index.ts +9 -0
  193. package/src/data/db/in-list.ts +49 -0
  194. package/src/data/db/index.ts +9 -0
  195. package/src/data/db/insert-split.ts +137 -0
  196. package/src/data/db/instrumentation.ts +82 -0
  197. package/src/data/db/maintenance.ts +28 -0
  198. package/src/data/db/namespace.ts +107 -0
  199. package/src/data/db/owner-labels.ts +9 -0
  200. package/src/data/db/pool.ts +187 -0
  201. package/src/data/db/query-label.ts +13 -0
  202. package/src/data/db/query-watch.ts +116 -0
  203. package/src/data/db/raw-client-warning.ts +37 -0
  204. package/src/data/db/readonly.ts +23 -0
  205. package/src/data/db/reservation.ts +151 -0
  206. package/src/data/db/schema-features.ts +14 -0
  207. package/src/data/db/serializable.ts +83 -0
  208. package/src/data/db/sql.ts +1 -0
  209. package/src/data/db/timeouts.ts +76 -0
  210. package/src/data/db/types.ts +18 -0
  211. package/src/data/db/unbound.ts +33 -0
  212. package/src/data/db/values.ts +29 -0
  213. package/src/data/domain/brand.ts +15 -0
  214. package/src/data/domain/build-factory.ts +193 -0
  215. package/src/data/domain/build-schema.ts +161 -0
  216. package/src/data/domain/build-table.ts +355 -0
  217. package/src/data/domain/define.ts +239 -0
  218. package/src/data/domain/external-registry.ts +34 -0
  219. package/src/data/domain/external.ts +77 -0
  220. package/src/data/domain/field.ts +279 -0
  221. package/src/data/domain/index.ts +13 -0
  222. package/src/data/domain/naming.ts +6 -0
  223. package/src/data/domain/projections.ts +57 -0
  224. package/src/data/domain/trace-safe-projection.ts +31 -0
  225. package/src/data/domain/traits.ts +108 -0
  226. package/src/data/domain/types.ts +289 -0
  227. package/src/data/jsonb/composed.ts +18 -0
  228. package/src/data/jsonb/index.ts +1 -0
  229. package/src/data/jsonb/namespace.ts +30 -0
  230. package/src/data/jsonb/query.ts +86 -0
  231. package/src/data/migrate/append-only-generate.ts +47 -0
  232. package/src/data/migrate/applied.ts +47 -0
  233. package/src/data/migrate/apply.ts +309 -0
  234. package/src/data/migrate/check.ts +119 -0
  235. package/src/data/migrate/diff.ts +357 -0
  236. package/src/data/migrate/door-generate.ts +130 -0
  237. package/src/data/migrate/framework-rls.ts +58 -0
  238. package/src/data/migrate/generate.ts +201 -0
  239. package/src/data/migrate/history.ts +215 -0
  240. package/src/data/migrate/index.ts +9 -0
  241. package/src/data/migrate/internal-definitions.ts +175 -0
  242. package/src/data/migrate/post-process.ts +73 -0
  243. package/src/data/migrate/pragma.ts +73 -0
  244. package/src/data/migrate/provision-framework.ts +147 -0
  245. package/src/data/migrate/rls-generate.ts +61 -0
  246. package/src/data/migrate/run.ts +124 -0
  247. package/src/data/migrate/safety.ts +167 -0
  248. package/src/data/migrate/snapshot.ts +368 -0
  249. package/src/data/migrate/sql-emit.ts +392 -0
  250. package/src/data/migrate/statements.ts +312 -0
  251. package/src/data/query/alias.ts +126 -0
  252. package/src/data/query/batch.ts +8 -0
  253. package/src/data/query/cache/core/cache.ts +78 -0
  254. package/src/data/query/cache/core/index.ts +1 -0
  255. package/src/data/query/cache/core/types.ts +29 -0
  256. package/src/data/query/cache/readme.md +230 -0
  257. package/src/data/query/casing.ts +76 -0
  258. package/src/data/query/collation.ts +11 -0
  259. package/src/data/query/column-builder.ts +370 -0
  260. package/src/data/query/column.ts +150 -0
  261. package/src/data/query/entity.ts +42 -0
  262. package/src/data/query/errors.ts +33 -0
  263. package/src/data/query/index.ts +14 -0
  264. package/src/data/query/logger.ts +47 -0
  265. package/src/data/query/operations.ts +45 -0
  266. package/src/data/query/pg-core/alias.ts +12 -0
  267. package/src/data/query/pg-core/checks.ts +32 -0
  268. package/src/data/query/pg-core/columns/all.ts +71 -0
  269. package/src/data/query/pg-core/columns/bigint.ts +110 -0
  270. package/src/data/query/pg-core/columns/bigserial.ts +128 -0
  271. package/src/data/query/pg-core/columns/boolean.ts +43 -0
  272. package/src/data/query/pg-core/columns/char.ts +85 -0
  273. package/src/data/query/pg-core/columns/cidr.ts +43 -0
  274. package/src/data/query/pg-core/columns/common.ts +366 -0
  275. package/src/data/query/pg-core/columns/custom.ts +232 -0
  276. package/src/data/query/pg-core/columns/date.common.ts +15 -0
  277. package/src/data/query/pg-core/columns/date.ts +112 -0
  278. package/src/data/query/pg-core/columns/double-precision.ts +55 -0
  279. package/src/data/query/pg-core/columns/enum.ts +201 -0
  280. package/src/data/query/pg-core/columns/index.ts +35 -0
  281. package/src/data/query/pg-core/columns/inet.ts +43 -0
  282. package/src/data/query/pg-core/columns/int.common.ts +57 -0
  283. package/src/data/query/pg-core/columns/integer.ts +53 -0
  284. package/src/data/query/pg-core/columns/interval.ts +83 -0
  285. package/src/data/query/pg-core/columns/json.ts +58 -0
  286. package/src/data/query/pg-core/columns/jsonb.ts +56 -0
  287. package/src/data/query/pg-core/columns/line.ts +117 -0
  288. package/src/data/query/pg-core/columns/macaddr.ts +43 -0
  289. package/src/data/query/pg-core/columns/macaddr8.ts +43 -0
  290. package/src/data/query/pg-core/columns/numeric.ts +227 -0
  291. package/src/data/query/pg-core/columns/point.ts +127 -0
  292. package/src/data/query/pg-core/columns/postgis_extension/geometry.ts +119 -0
  293. package/src/data/query/pg-core/columns/postgis_extension/utils.ts +47 -0
  294. package/src/data/query/pg-core/columns/real.ts +58 -0
  295. package/src/data/query/pg-core/columns/serial.ts +55 -0
  296. package/src/data/query/pg-core/columns/smallint.ts +53 -0
  297. package/src/data/query/pg-core/columns/smallserial.ts +60 -0
  298. package/src/data/query/pg-core/columns/text.ts +67 -0
  299. package/src/data/query/pg-core/columns/time.ts +72 -0
  300. package/src/data/query/pg-core/columns/timestamp.ts +163 -0
  301. package/src/data/query/pg-core/columns/uuid.ts +51 -0
  302. package/src/data/query/pg-core/columns/varchar.ts +89 -0
  303. package/src/data/query/pg-core/columns/vector_extension/bit.ts +68 -0
  304. package/src/data/query/pg-core/columns/vector_extension/halfvec.ts +80 -0
  305. package/src/data/query/pg-core/columns/vector_extension/sparsevec.ts +67 -0
  306. package/src/data/query/pg-core/columns/vector_extension/vector.ts +80 -0
  307. package/src/data/query/pg-core/db.ts +674 -0
  308. package/src/data/query/pg-core/dialect.ts +1404 -0
  309. package/src/data/query/pg-core/expressions.ts +25 -0
  310. package/src/data/query/pg-core/foreign-keys.ts +130 -0
  311. package/src/data/query/pg-core/index.ts +21 -0
  312. package/src/data/query/pg-core/indexes.ts +276 -0
  313. package/src/data/query/pg-core/policies.ts +56 -0
  314. package/src/data/query/pg-core/primary-keys.ts +62 -0
  315. package/src/data/query/pg-core/query-builders/count.ts +86 -0
  316. package/src/data/query/pg-core/query-builders/delete.ts +293 -0
  317. package/src/data/query/pg-core/query-builders/index.ts +7 -0
  318. package/src/data/query/pg-core/query-builders/insert.ts +452 -0
  319. package/src/data/query/pg-core/query-builders/query-builder.ts +123 -0
  320. package/src/data/query/pg-core/query-builders/query.ts +157 -0
  321. package/src/data/query/pg-core/query-builders/raw.ts +49 -0
  322. package/src/data/query/pg-core/query-builders/refresh-materialized-view.ts +108 -0
  323. package/src/data/query/pg-core/query-builders/select.ts +1346 -0
  324. package/src/data/query/pg-core/query-builders/select.types.ts +437 -0
  325. package/src/data/query/pg-core/query-builders/update.ts +626 -0
  326. package/src/data/query/pg-core/roles.ts +41 -0
  327. package/src/data/query/pg-core/schema.ts +72 -0
  328. package/src/data/query/pg-core/sequence.ts +41 -0
  329. package/src/data/query/pg-core/session.ts +292 -0
  330. package/src/data/query/pg-core/subquery.ts +46 -0
  331. package/src/data/query/pg-core/table.ts +252 -0
  332. package/src/data/query/pg-core/unique-constraint.ts +73 -0
  333. package/src/data/query/pg-core/utils/array.ts +95 -0
  334. package/src/data/query/pg-core/utils/index.ts +1 -0
  335. package/src/data/query/pg-core/utils.ts +104 -0
  336. package/src/data/query/pg-core/view-base.ts +14 -0
  337. package/src/data/query/pg-core/view-common.ts +1 -0
  338. package/src/data/query/pg-core/view.ts +435 -0
  339. package/src/data/query/pg-core/with-builder.ts +41 -0
  340. package/src/data/query/postgres-js/README.md +46 -0
  341. package/src/data/query/postgres-js/array-parser.ts +45 -0
  342. package/src/data/query/postgres-js/driver-error-boundary.ts +149 -0
  343. package/src/data/query/postgres-js/driver.ts +163 -0
  344. package/src/data/query/postgres-js/index.ts +2 -0
  345. package/src/data/query/postgres-js/session.ts +234 -0
  346. package/src/data/query/primary-key.ts +11 -0
  347. package/src/data/query/query-builders/query-builder.ts +19 -0
  348. package/src/data/query/query-builders/select.types.ts +175 -0
  349. package/src/data/query/query-promise.ts +35 -0
  350. package/src/data/query/relations.ts +725 -0
  351. package/src/data/query/runnable-query.ts +12 -0
  352. package/src/data/query/selection-proxy.ts +121 -0
  353. package/src/data/query/session.ts +8 -0
  354. package/src/data/query/sql/expressions/conditions.ts +741 -0
  355. package/src/data/query/sql/expressions/index.ts +2 -0
  356. package/src/data/query/sql/expressions/select.ts +45 -0
  357. package/src/data/query/sql/functions/aggregate.ts +129 -0
  358. package/src/data/query/sql/functions/index.ts +2 -0
  359. package/src/data/query/sql/functions/vector.ts +178 -0
  360. package/src/data/query/sql/index.ts +3 -0
  361. package/src/data/query/sql/sql.ts +794 -0
  362. package/src/data/query/subquery.ts +53 -0
  363. package/src/data/query/table.ts +208 -0
  364. package/src/data/query/table.utils.ts +2 -0
  365. package/src/data/query/tracing-utils.ts +3 -0
  366. package/src/data/query/tracing.ts +11 -0
  367. package/src/data/query/utils.ts +357 -0
  368. package/src/data/query/version.ts +4 -0
  369. package/src/data/query/view-common.ts +1 -0
  370. package/src/data/schema/branded.ts +13 -0
  371. package/src/data/schema/generated/coerce.ts +5 -0
  372. package/src/data/schema/generated/combinator.ts +6 -0
  373. package/src/data/schema/generated/encoding.ts +5 -0
  374. package/src/data/schema/generated/error.ts +6 -0
  375. package/src/data/schema/generated/format.ts +5 -0
  376. package/src/data/schema/generated/type-constructor.ts +10 -0
  377. package/src/data/schema/generated/type.ts +6 -0
  378. package/src/data/schema/index.ts +6 -0
  379. package/src/data/schema/json-schema.ts +33 -0
  380. package/src/data/schema/namespace.ts +55 -0
  381. package/src/data/schema/parse.ts +39 -0
  382. package/src/data/schema/types.ts +39 -0
  383. package/src/data/temporal/append-only.ts +134 -0
  384. package/src/data/temporal/close.ts +27 -0
  385. package/src/data/temporal/columns.ts +10 -0
  386. package/src/data/temporal/definition-queries.ts +70 -0
  387. package/src/data/temporal/errors.ts +10 -0
  388. package/src/data/temporal/get-current.ts +22 -0
  389. package/src/data/temporal/index.ts +9 -0
  390. package/src/data/temporal/namespace.ts +36 -0
  391. package/src/data/temporal/query.ts +25 -0
  392. package/src/data/temporal/resolve.ts +17 -0
  393. package/src/data/temporal/revise.ts +67 -0
  394. package/src/data/temporal/types.ts +22 -0
  395. package/src/data/valve/artifact.ts +33 -0
  396. package/src/data/valve/contract-diff.ts +21 -0
  397. package/src/data/valve/cross.ts +57 -0
  398. package/src/data/valve/crossing-ledger.ts +50 -0
  399. package/src/data/valve/decision.ts +44 -0
  400. package/src/data/valve/durable-crossing.ts +52 -0
  401. package/src/data/valve/execute-crossing.ts +77 -0
  402. package/src/data/valve/faucet.ts +56 -0
  403. package/src/data/valve/gate.ts +114 -0
  404. package/src/data/valve/graph.ts +77 -0
  405. package/src/data/valve/index.ts +53 -0
  406. package/src/data/valve/namespace.ts +57 -0
  407. package/src/data/valve/registry.ts +50 -0
  408. package/src/data/valve/schema/valve-crossing-fields.ts +23 -0
  409. package/src/data/valve/schema/valve-crossing.ts +9 -0
  410. package/src/data/valve/supply.ts +154 -0
  411. package/src/data/valve/types.ts +143 -0
  412. package/src/data/vector/columns.ts +27 -0
  413. package/src/data/vector/index.ts +4 -0
  414. package/src/data/vector/namespace.ts +33 -0
  415. package/src/data/vector/query.ts +85 -0
  416. package/src/data/vector/validate.ts +16 -0
  417. package/src/data/write/audit/actor.ts +15 -0
  418. package/src/data/write/audit/audit-columns.ts +18 -0
  419. package/src/data/write/audit/audit-select.ts +111 -0
  420. package/src/data/write/audit/bound-transaction.ts +14 -0
  421. package/src/data/write/audit/cte-compile.ts +32 -0
  422. package/src/data/write/audit/decode-returning.ts +47 -0
  423. package/src/data/write/audit/final-select.ts +43 -0
  424. package/src/data/write/audit/infra-audit.ts +36 -0
  425. package/src/data/write/audit/primary-key.ts +65 -0
  426. package/src/data/write/audit/projection.ts +41 -0
  427. package/src/data/write/audit/read-touches.ts +86 -0
  428. package/src/data/write/audit/touch-match.ts +78 -0
  429. package/src/data/write/audit/verb-delete.ts +62 -0
  430. package/src/data/write/audit/verb-insert.ts +36 -0
  431. package/src/data/write/audit/verb-select.ts +47 -0
  432. package/src/data/write/audit/verb-update.ts +60 -0
  433. package/src/data/write/audit/verb-upsert.ts +104 -0
  434. package/src/data/write/conflict.ts +43 -0
  435. package/src/data/write/delete.ts +38 -0
  436. package/src/data/write/execute.ts +243 -0
  437. package/src/data/write/index.ts +11 -0
  438. package/src/data/write/insert.ts +63 -0
  439. package/src/data/write/state.ts +43 -0
  440. package/src/data/write/update.ts +54 -0
  441. package/src/data/write/upsert.ts +58 -0
  442. package/src/data/write/values.ts +19 -0
  443. package/src/index.ts +230 -0
  444. package/src/infra/access-links/code.ts +70 -0
  445. package/src/infra/access-links/handle.ts +892 -0
  446. package/src/infra/access-links/index.ts +9 -0
  447. package/src/infra/access-links/namespace.ts +30 -0
  448. package/src/infra/access-links/person-check.ts +177 -0
  449. package/src/infra/access-links/schema.ts +71 -0
  450. package/src/infra/alerts/async-sources.ts +231 -0
  451. package/src/infra/alerts/audit.ts +36 -0
  452. package/src/infra/alerts/beat.ts +123 -0
  453. package/src/infra/alerts/client-faults.ts +111 -0
  454. package/src/infra/alerts/faults.ts +156 -0
  455. package/src/infra/alerts/handle.ts +778 -0
  456. package/src/infra/alerts/http.ts +192 -0
  457. package/src/infra/alerts/index.ts +12 -0
  458. package/src/infra/alerts/infra-sources.ts +180 -0
  459. package/src/infra/alerts/namespace.ts +81 -0
  460. package/src/infra/alerts/redact.ts +21 -0
  461. package/src/infra/alerts/request-faults.ts +41 -0
  462. package/src/infra/alerts/schema.ts +124 -0
  463. package/src/infra/alerts/sinks.ts +125 -0
  464. package/src/infra/alerts/source-options.ts +26 -0
  465. package/src/infra/alerts/sources.ts +503 -0
  466. package/src/infra/alerts/subsystem-sources.ts +137 -0
  467. package/src/infra/audit/emit.ts +87 -0
  468. package/src/infra/audit/index.ts +2 -0
  469. package/src/infra/audit/namespace.ts +22 -0
  470. package/src/infra/audit/schema.sql +35 -0
  471. package/src/infra/audit/types.ts +18 -0
  472. package/src/infra/aws/credentials.ts +147 -0
  473. package/src/infra/aws/sigv4.ts +140 -0
  474. package/src/infra/cache/create.ts +58 -0
  475. package/src/infra/cache/entry.ts +81 -0
  476. package/src/infra/cache/index.ts +4 -0
  477. package/src/infra/cache/namespace.ts +10 -0
  478. package/src/infra/cache/tenant.ts +93 -0
  479. package/src/infra/cache/types.ts +24 -0
  480. package/src/infra/crawl/audit.ts +35 -0
  481. package/src/infra/crawl/errors.ts +25 -0
  482. package/src/infra/crawl/handle.ts +693 -0
  483. package/src/infra/crawl/index.ts +9 -0
  484. package/src/infra/crawl/links.ts +160 -0
  485. package/src/infra/crawl/namespace.ts +36 -0
  486. package/src/infra/crawl/robots.ts +100 -0
  487. package/src/infra/crawl/schema.ts +89 -0
  488. package/src/infra/credit-line/handle.ts +883 -0
  489. package/src/infra/credit-line/index.ts +6 -0
  490. package/src/infra/credit-line/namespace.ts +7 -0
  491. package/src/infra/credit-line/schema.ts +96 -0
  492. package/src/infra/credits/handle.ts +706 -0
  493. package/src/infra/credits/index.ts +5 -0
  494. package/src/infra/credits/namespace.ts +7 -0
  495. package/src/infra/credits/schema.ts +83 -0
  496. package/src/infra/email/audit.ts +36 -0
  497. package/src/infra/email/inbound.ts +622 -0
  498. package/src/infra/email/index.ts +11 -0
  499. package/src/infra/email/mail.ts +517 -0
  500. package/src/infra/email/mime.ts +447 -0
  501. package/src/infra/email/namespace.ts +22 -0
  502. package/src/infra/email/providers.ts +111 -0
  503. package/src/infra/email/schema.ts +96 -0
  504. package/src/infra/email/sns.ts +200 -0
  505. package/src/infra/email/unsubscribe.ts +44 -0
  506. package/src/infra/fetch/address.ts +88 -0
  507. package/src/infra/fetch/connect-retry.ts +87 -0
  508. package/src/infra/fetch/handle.ts +329 -0
  509. package/src/infra/fetch/index.ts +7 -0
  510. package/src/infra/fetch/namespace.ts +70 -0
  511. package/src/infra/idempotency/index.ts +3 -0
  512. package/src/infra/idempotency/middleware.ts +87 -0
  513. package/src/infra/idempotency/store.ts +92 -0
  514. package/src/infra/idempotency/types.ts +30 -0
  515. package/src/infra/join-requests/handle.ts +312 -0
  516. package/src/infra/join-requests/index.ts +2 -0
  517. package/src/infra/join-requests/namespace.ts +7 -0
  518. package/src/infra/join-requests/schema.ts +33 -0
  519. package/src/infra/outbox/core.ts +165 -0
  520. package/src/infra/quota/errors.ts +32 -0
  521. package/src/infra/quota/handle.ts +215 -0
  522. package/src/infra/quota/index.ts +5 -0
  523. package/src/infra/quota/namespace.ts +7 -0
  524. package/src/infra/quota/schema.ts +15 -0
  525. package/src/infra/storage/backend.ts +34 -0
  526. package/src/infra/storage/handle.ts +513 -0
  527. package/src/infra/storage/index.ts +9 -0
  528. package/src/infra/storage/local.ts +104 -0
  529. package/src/infra/storage/namespace.ts +13 -0
  530. package/src/infra/storage/s3.ts +257 -0
  531. package/src/infra/storage/schema.ts +24 -0
  532. package/src/infra/support/handle.ts +869 -0
  533. package/src/infra/support/http.ts +229 -0
  534. package/src/infra/support/index.ts +7 -0
  535. package/src/infra/support/namespace.ts +12 -0
  536. package/src/infra/support/schema.ts +96 -0
  537. package/src/infra/webhook/inbound.ts +45 -0
  538. package/src/infra/webhook/index.ts +5 -0
  539. package/src/infra/webhook/namespace.ts +24 -0
  540. package/src/infra/webhook/outbound.ts +315 -0
  541. package/src/infra/webhook/sign.ts +32 -0
  542. package/src/infra/webhook/verify.ts +82 -0
  543. package/src/invariants/assert.ts +26 -0
  544. package/src/invariants/index.ts +1 -0
  545. package/src/lint/config.ts +56 -0
  546. package/src/lint/doctrine.ts +163 -0
  547. package/src/lint/include.ts +97 -0
  548. package/src/lint/index.ts +3 -0
  549. package/src/lint/preset.ts +10 -0
  550. package/src/lint/resolver.ts +218 -0
  551. package/src/lint/rules/no-reexports.ts +46 -0
  552. package/src/request/behavior/audited.ts +85 -0
  553. package/src/request/behavior/cached.ts +62 -0
  554. package/src/request/behavior/compose.ts +28 -0
  555. package/src/request/behavior/index.ts +5 -0
  556. package/src/request/behavior/namespace.ts +16 -0
  557. package/src/request/behavior/types.ts +26 -0
  558. package/src/request/behavior/versioned.ts +54 -0
  559. package/src/request/context/create.ts +103 -0
  560. package/src/request/context/guard.ts +49 -0
  561. package/src/request/context/index.ts +4 -0
  562. package/src/request/context/run-in-transaction.ts +200 -0
  563. package/src/request/context/store.ts +28 -0
  564. package/src/request/context/types.ts +50 -0
  565. package/src/request/di/builder.ts +535 -0
  566. package/src/request/di/index.ts +8 -0
  567. package/src/request/di/resolve.ts +271 -0
  568. package/src/request/di/types.ts +262 -0
  569. package/src/request/fitting/namespace.ts +7 -0
  570. package/src/request/fixture/batch.ts +71 -0
  571. package/src/request/fixture/builder.ts +26 -0
  572. package/src/request/fixture/errors.ts +10 -0
  573. package/src/request/fixture/index.ts +8 -0
  574. package/src/request/fixture/namespace.ts +23 -0
  575. package/src/request/fixture/paginate.ts +44 -0
  576. package/src/request/fixture/to-handlers.ts +260 -0
  577. package/src/request/fixture/types.ts +79 -0
  578. package/src/request/flow/act-as.ts +78 -0
  579. package/src/request/flow/namespace.ts +17 -0
  580. package/src/request/http/error-step.ts +42 -0
  581. package/src/request/http/file.ts +141 -0
  582. package/src/request/http/index.ts +4 -0
  583. package/src/request/http/middleware.ts +141 -0
  584. package/src/request/http/namespace.ts +5 -0
  585. package/src/request/http/rate-limit-classes.ts +170 -0
  586. package/src/request/http/response.ts +82 -0
  587. package/src/request/http/server.ts +916 -0
  588. package/src/request/http/sniff.ts +261 -0
  589. package/src/request/http/types.ts +250 -0
  590. package/src/request/http/upload.ts +284 -0
  591. package/src/request/internal/text.ts +6 -0
  592. package/src/request/openapi/generate.ts +205 -0
  593. package/src/request/openapi/index.ts +2 -0
  594. package/src/request/openapi/namespace.ts +7 -0
  595. package/src/test/auth.ts +13 -0
  596. package/src/test/auto-setup.ts +45 -0
  597. package/src/test/context.ts +91 -0
  598. package/src/test/crawl-fixture.ts +157 -0
  599. package/src/test/discover-fork.ts +17 -0
  600. package/src/test/fingerprint.ts +135 -0
  601. package/src/test/globals.d.ts +7 -0
  602. package/src/test/index.ts +19 -0
  603. package/src/test/namespace.ts +87 -0
  604. package/src/test/plugin.ts +189 -0
  605. package/src/test/progress.ts +185 -0
  606. package/src/test/reap-after-run.ts +28 -0
  607. package/src/test/reap.ts +286 -0
  608. package/src/test/reset.ts +253 -0
  609. package/src/test/savepoints.ts +172 -0
  610. package/src/test/setup.ts +1422 -0
  611. package/src/test/system-channel.ts +50 -0
  612. package/src/test/vitest-plugin.ts +142 -0
  613. package/src/test/vitest-workspace.ts +507 -0
  614. package/src/test/vitest.ts +333 -0
  615. package/src/test/watch.ts +105 -0
  616. package/src/trace/capture/bounded-serializer.ts +146 -0
  617. package/src/trace/cardinality/analyze.ts +173 -0
  618. package/src/trace/cardinality/branch-context.ts +214 -0
  619. package/src/trace/cardinality/check.ts +33 -0
  620. package/src/trace/cardinality/classify-edge.ts +64 -0
  621. package/src/trace/cardinality/structural-max.ts +107 -0
  622. package/src/trace/check/coverage-check.ts +188 -0
  623. package/src/trace/check/flow-check.ts +103 -0
  624. package/src/trace/check/flow-step-site.ts +234 -0
  625. package/src/trace/check/io-pattern.ts +116 -0
  626. package/src/trace/codemod/add-cardinality.ts +152 -0
  627. package/src/trace/codemod/run.ts +80 -0
  628. package/src/trace/entry/child-trace.ts +21 -0
  629. package/src/trace/entry/close-trace.ts +87 -0
  630. package/src/trace/entry/finalize-trace.ts +28 -0
  631. package/src/trace/entry/open-trace.ts +138 -0
  632. package/src/trace/entry/parent-trace.ts +20 -0
  633. package/src/trace/entry/parse-traceparent.ts +39 -0
  634. package/src/trace/entry/process-start.ts +38 -0
  635. package/src/trace/entry/trace-context.ts +26 -0
  636. package/src/trace/entry/tracing-enabled.ts +10 -0
  637. package/src/trace/external/record.ts +195 -0
  638. package/src/trace/external/report.ts +66 -0
  639. package/src/trace/namespace.ts +19 -0
  640. package/src/trace/partition/day.ts +47 -0
  641. package/src/trace/partition/index.ts +14 -0
  642. package/src/trace/partition/partition-name.ts +23 -0
  643. package/src/trace/partition/plan.ts +82 -0
  644. package/src/trace/partition/schedule.ts +26 -0
  645. package/src/trace/partition/startup-hook.ts +39 -0
  646. package/src/trace/partition/sync.ts +154 -0
  647. package/src/trace/partition/tables.ts +53 -0
  648. package/src/trace/read/tree.ts +83 -0
  649. package/src/trace/retain/copy-on-error.ts +76 -0
  650. package/src/trace/runtime/expire-stale-traces.ts +54 -0
  651. package/src/trace/runtime/flow-step.ts +147 -0
  652. package/src/trace/runtime/flush-steps.ts +31 -0
  653. package/src/trace/runtime/pending-writes.ts +54 -0
  654. package/src/trace/runtime/step-buffer.ts +298 -0
  655. package/src/trace/runtime/step-error.ts +21 -0
  656. package/src/trace/runtime/step-meta.ts +55 -0
  657. package/src/trace/runtime/thenable.ts +12 -0
  658. package/src/trace/runtime/truncate-trace.ts +62 -0
  659. package/src/trace/schema/trace-fields.ts +52 -0
  660. package/src/trace/schema/trace-retained.ts +22 -0
  661. package/src/trace/schema/trace-step-fields.ts +44 -0
  662. package/src/trace/schema/trace-step-retained.ts +25 -0
  663. package/src/trace/schema/trace-step.ts +18 -0
  664. package/src/trace/schema/trace.ts +12 -0
  665. package/src/trace/transformer/apply.ts +79 -0
  666. package/src/trace/transformer/build-meta-literal.ts +30 -0
  667. package/src/trace/transformer/find-flow-step-sites.ts +85 -0
  668. package/src/trace/transformer/inject-flow-step-import.ts +39 -0
  669. package/src/trace/transformer/transform-error.ts +14 -0
  670. package/src/trace/transformer/transform-flow-steps.ts +123 -0
  671. package/src/trace/transformer/wrap-function-declaration.ts +62 -0
  672. package/src/trace/transformer/wrap-variable-declaration.ts +67 -0
  673. package/src/workspace/affected.ts +110 -0
  674. package/src/workspace/changed-files.ts +163 -0
  675. package/src/workspace/check-deps.ts +213 -0
  676. package/src/workspace/checksum.ts +20 -0
  677. package/src/workspace/dependency-graph.ts +67 -0
  678. package/src/workspace/detect-pipework-copies.ts +93 -0
  679. package/src/workspace/discover.ts +206 -0
  680. package/src/workspace/generate.ts +401 -0
  681. package/src/workspace/index.ts +7 -0
  682. package/src/workspace/module-imports-pipework.ts +39 -0
  683. package/src/workspace/module-package-name.ts +12 -0
  684. package/src/workspace/scrub-git-env.ts +23 -0
  685. package/src/workspace/source-paths.ts +36 -0
  686. package/src/workspace/sync.ts +182 -0
  687. package/dist/REFERENCE.md +0 -1477
  688. package/dist/infra/access-links/rate-limit.d.ts +0 -10
  689. package/dist/infra/access-links/rate-limit.d.ts.map +0 -1
  690. package/dist/infra/access-links/rate-limit.js +0 -28
  691. package/dist/infra/access-links/rate-limit.js.map +0 -1
  692. package/dist/infra/email/sigv4.d.ts +0 -24
  693. package/dist/infra/email/sigv4.d.ts.map +0 -1
  694. package/dist/infra/email/sigv4.js +0 -55
  695. package/dist/infra/email/sigv4.js.map +0 -1
@@ -0,0 +1,581 @@
1
+ import type { PgTable } from '../../data/query/pg-core/index.js'
2
+ import type { DB } from '../../data/db/types.js'
3
+ import type { SQL } from '../../data/query/index.js'
4
+ import { eq, and, or, isNull, is, SQL as SQLClass, getTableColumns, getTableName } from '../../data/query/index.js'
5
+ import { getScopeConfig } from './scope.js'
6
+
7
+ type AnyFn = (...args: unknown[]) => unknown
8
+ type Obj = Record<string, AnyFn>
9
+ type Column = ReturnType<typeof getTableColumns>[string]
10
+
11
+ /** What a connection does with a table: filter it to the tenant, or refuse it. */
12
+ interface Policy {
13
+ /** Marks the builders this connection made; a CTE body or set operand must carry it. */
14
+ readonly owner: string | symbol
15
+ /** The tenant to inject into inserts; null on the guard, which refuses tenant tables. */
16
+ readonly tenantId: string | null
17
+ /** The tenant filter for a tenant table, null for any other source. The guard throws. */
18
+ scope(table: unknown, operation: string): { cond: SQL; column: Column; columnKey: string } | null
19
+ readonly refuseExecute: boolean
20
+ }
21
+
22
+ const GUARD = Symbol('tenant-guard')
23
+ const made = new WeakMap<object, Policy['owner']>()
24
+ /** Every tenant-scoped handle (and each nested transaction's) → the raw handle under it and its tenant. */
25
+ const scopedHandles = new WeakMap<object, { readonly raw: DB; readonly tenantId: string }>()
26
+
27
+ /**
28
+ * The handle a framework API runs its raw SQL on. A tenant-scoped handle yields the raw handle under it (same
29
+ * connection or transaction, so it commits and rolls back with the caller and keeps its session tenant) and its
30
+ * tenant; any other handle comes back unchanged with a null tenant. Internal: not part of the package surface.
31
+ */
32
+ export function unscopedExecutor(db: DB): { readonly db: DB; readonly tenant: string | null } {
33
+ const scoped = scopedHandles.get(db as object)
34
+ return scoped === undefined ? { db, tenant: null } : { db: scoped.raw, tenant: scoped.tenantId }
35
+ }
36
+
37
+ /** The API's name and why it cannot be confined to one tenant, for the error a scoped handle gets. */
38
+ function refuseScopedMessage(api: string, tenant: string, reason: string): string {
39
+ return (
40
+ `[pipework] ${api} was given a tenant-scoped database connection (tenant "${tenant}"), but ${reason}.\n\n` +
41
+ ' Pass an unscoped handle instead: the worker\'s own, pipe.system(), or\n' +
42
+ ' pool.getOrCreate(...).drizzle.\n'
43
+ )
44
+ }
45
+
46
+ /** Throws unless `db` is an unscoped handle; for APIs that act across tenants and cannot be confined to one. */
47
+ export function requireUnscopedExecutor(db: DB, api: string, reason: string): DB {
48
+ const { db: raw, tenant } = unscopedExecutor(db)
49
+ if (tenant !== null) throw new Error(refuseScopedMessage(api, tenant, reason))
50
+ return raw
51
+ }
52
+
53
+ /**
54
+ * For an API that acts on one tenant's rows and knows which: the raw handle and the tenant to act for. A scoped
55
+ * handle whose tenant differs from `tenant` throws; with no `tenant` given the call is confined to the handle's.
56
+ */
57
+ export function scopedExecutor(db: DB, tenant: string | null, api: string): { readonly db: DB; readonly tenant: string | null } {
58
+ const { db: raw, tenant: own } = unscopedExecutor(db)
59
+ if (own === null) return { db: raw, tenant }
60
+ if (tenant !== null && tenant !== own) {
61
+ throw new Error(
62
+ `[pipework] ${api} was given a handle scoped to tenant "${own}" but acts on tenant "${tenant}".\n\n` +
63
+ ' A tenant-scoped connection reaches only its own tenant. For another tenant use an unscoped handle:\n' +
64
+ ' the worker\'s own, pipe.system(), or pool.getOrCreate(...).drizzle.\n',
65
+ )
66
+ }
67
+ return { db: raw, tenant: own }
68
+ }
69
+
70
+ /**
71
+ * Wraps an API object whose methods act across tenants: a call with a tenant-scoped handle among its arguments
72
+ * throws the actionable error before running, any other call goes through unchanged.
73
+ */
74
+ export function refuseScopedHandle<T extends object>(api: T, name: string, reason: string, except: readonly string[] = []): T {
75
+ return new Proxy(api, {
76
+ get(target, prop, receiver) {
77
+ const value: unknown = Reflect.get(target, prop, receiver)
78
+ if (typeof value !== 'function' || typeof prop !== 'string' || except.includes(prop)) return value
79
+ return (...args: unknown[]): unknown => {
80
+ for (const arg of args) {
81
+ const scoped = typeof arg === 'object' && arg !== null ? scopedHandles.get(arg) : undefined
82
+ if (scoped !== undefined) throw new Error(refuseScopedMessage(`${name}.${prop}()`, scoped.tenantId, reason))
83
+ }
84
+ return (value as AnyFn).apply(target, args)
85
+ }
86
+ },
87
+ })
88
+ }
89
+
90
+ const JOINS: Record<string, string> = {
91
+ leftJoin: 'left', leftJoinLateral: 'left', rightJoin: 'right', innerJoin: 'inner',
92
+ innerJoinLateral: 'inner', fullJoin: 'full', crossJoin: 'cross', crossJoinLateral: 'cross',
93
+ }
94
+ const SET_OPERATORS = ['union', 'unionAll', 'intersect', 'intersectAll', 'except', 'exceptAll']
95
+ const SELECTS = ['select', 'selectDistinct', 'selectDistinctOn']
96
+
97
+ function throwTenantRequired(tableName: string, operation: string): never {
98
+ throw new Error(
99
+ `[pipework] Cannot ${operation} tenant-scoped table "${tableName}" without a tenant in context.\n\n` +
100
+ ' This table has a .tenant() field, which means all operations require\n' +
101
+ ' an active tenant in the request/job context.\n\n' +
102
+ ' If this is a system-level operation that legitimately needs cross-tenant\n' +
103
+ ' access — pre-tenant auth (e.g. resolving org membership during login),\n' +
104
+ ' public webhook ingress, admin tooling — use pipe.system() instead. It is\n' +
105
+ ' the explicit unscoped channel: owner-role connection, outside the request\n' +
106
+ ' transaction, never tenant-filtered. Every query through it must be\n' +
107
+ ' deliberately cross-tenant-safe.\n',
108
+ )
109
+ }
110
+
111
+ function scopedPolicy(tenantId: string): Policy {
112
+ return {
113
+ owner: tenantId,
114
+ tenantId,
115
+ refuseExecute: true,
116
+ scope(table) {
117
+ const config = getScopeConfig(table as PgTable)
118
+ if (!config) return null
119
+ const column = getTableColumns(table as PgTable)[config.columnKey]!
120
+ return { cond: eq(column, tenantId), column, columnKey: config.columnKey }
121
+ },
122
+ }
123
+ }
124
+
125
+ const guardPolicy: Policy = {
126
+ owner: GUARD,
127
+ tenantId: null,
128
+ refuseExecute: false,
129
+ scope(table, operation) {
130
+ if (getScopeConfig(table as PgTable)) throwTenantRequired(getTableName(table as PgTable), operation)
131
+ return null
132
+ },
133
+ }
134
+
135
+ const SYSTEM = Symbol('platform-system')
136
+
137
+ /** The platform-role system channel: it reads no tenant table, whatever the statement. */
138
+ const systemPolicy: Policy = {
139
+ owner: SYSTEM,
140
+ tenantId: null,
141
+ refuseExecute: false,
142
+ scope(table, operation) {
143
+ if (!getScopeConfig(table as PgTable)) return null
144
+ throw new Error(
145
+ `[pipework] Cannot ${operation} tenant-scoped table "${getTableName(table as PgTable)}" through pipe.system().\n\n` +
146
+ ' This database runs its system channel on a platform role (rls.platformRole) that has no\n' +
147
+ ' blanket access to tenant rows. Declare a door for the table under `doors` in\n' +
148
+ ' pipework.config.ts, and read it with pipe.door(name, fn).\n',
149
+ )
150
+ },
151
+ }
152
+
153
+ const DOOR_READS: ReadonlySet<string> = new Set(['select from', 'join', 'count', 'update from'])
154
+
155
+ function doorPolicy(door: { readonly name: string; readonly table: string; readonly ops?: readonly string[] }): Policy {
156
+ const reads = door.ops?.includes('delete') === true ? new Set([...DOOR_READS, 'delete']) : DOOR_READS
157
+ return {
158
+ owner: Symbol(`door:${door.name}`),
159
+ tenantId: null,
160
+ refuseExecute: false,
161
+ scope(table, operation) {
162
+ if (!getScopeConfig(table as PgTable)) return null
163
+ const name = getTableName(table as PgTable)
164
+ if (name !== door.table) {
165
+ throw new Error(
166
+ `[pipework] Cannot ${operation} tenant-scoped table "${name}" inside door "${door.name}".\n\n` +
167
+ ` The door opens "${door.table}" only. Declare a door for "${name}" under \`doors\` in\n` +
168
+ ' pipework.config.ts and read it with its own pipe.door(name, fn).\n',
169
+ )
170
+ }
171
+ if (!reads.has(operation)) {
172
+ throw new Error(
173
+ `[pipework] Cannot ${operation} "${name}" inside door "${door.name}": a door is read-only.\n\n` +
174
+ ' Write tenant rows from a request or job with a tenant in context.\n',
175
+ )
176
+ }
177
+ return null
178
+ },
179
+ }
180
+ }
181
+
182
+ function refuse(what: string, why: string): never {
183
+ throw new Error(
184
+ `[pipework] ${what} is not allowed on this database connection: ${why}\n\n` +
185
+ ' A tenant-scoped connection filters every tenant table its own builders read.\n' +
186
+ ' Build the statement with this connection (select, insert, update or delete, or\n' +
187
+ ' the callback form of $with) so the filter is part of its SQL. A raw sql fragment\n' +
188
+ ' or a builder from another connection (pipe.system(), an unscoped db) cannot be\n' +
189
+ ' filtered, so it is refused.\n',
190
+ )
191
+ }
192
+
193
+ function checkBuilder(policy: Policy, what: string, builder: unknown): void {
194
+ if (builder != null && typeof builder === 'object' && made.get(builder) === policy.owner) return
195
+ if (is(builder, SQLClass)) refuse(what, 'a raw sql fragment cannot be scoped to the tenant.')
196
+ refuse(what, 'it was not built by this connection.')
197
+ }
198
+
199
+ function withCond(on: unknown, cond: SQL): unknown {
200
+ if (typeof on === 'function') return (...args: unknown[]) => and((on as AnyFn)(...args) as SQL | undefined, cond)
201
+ return on === undefined ? cond : and(on as SQL, cond)
202
+ }
203
+
204
+ interface Entry {
205
+ cond: SQL
206
+ column: Column
207
+ position: number
208
+ /** Kept as written even when a later right/full join null-extends the table. */
209
+ strict: boolean
210
+ /** Null-extended by its own right/full join: its null rows are not another tenant's. */
211
+ tolerant: boolean
212
+ }
213
+
214
+ /**
215
+ * The where clause of one statement: the user's condition plus the tenant
216
+ * condition of every tenant table the statement reads. It is rewritten onto the
217
+ * builder after every call that changes it, so the filter is in the SQL as soon
218
+ * as the table is named. A table a right or full join can null-extend accepts
219
+ * its null rows (the tenant column is NOT NULL, so null means no row there).
220
+ */
221
+ class WhereScope {
222
+ private user: SQL | undefined
223
+ private readonly entries: Entry[] = []
224
+ private position = 0
225
+ private widest = -1
226
+
227
+ constructor(private readonly target: Obj, private readonly policy: Policy) {}
228
+
229
+ table(table: unknown, operation: string, strict: boolean): void {
230
+ this.position++
231
+ const scope = this.policy.scope(table, operation)
232
+ if (scope) this.entries.push({ cond: scope.cond, column: scope.column, position: this.position, strict, tolerant: false })
233
+ this.refresh()
234
+ }
235
+
236
+ join(table: unknown, type: string, on: unknown): unknown {
237
+ this.position++
238
+ const wide = type === 'right' || type === 'full'
239
+ const scope = this.policy.scope(table, 'join')
240
+ const conds: SQL[] = []
241
+ if (scope && type !== 'cross') conds.push(scope.cond)
242
+ // A right or full join keeps unmatched rows of the tables before it: a row of
243
+ // another tenant must not match, so those tables' conditions go in its ON too.
244
+ if (wide) conds.push(...this.entries.map(e => or(e.cond, isNull(e.column))!))
245
+ if (scope && (wide || type === 'cross')) {
246
+ this.entries.push({ cond: scope.cond, column: scope.column, position: this.position, strict: false, tolerant: wide })
247
+ }
248
+ if (wide) this.widest = this.position
249
+ return conds.length > 0 ? withCond(on, and(...conds)!) : on
250
+ }
251
+
252
+ where(user: SQL | undefined): void {
253
+ this.user = user
254
+ this.refresh()
255
+ }
256
+
257
+ refresh(): void {
258
+ if (this.user === undefined && this.entries.length === 0) return
259
+ const conds = this.entries.map(e =>
260
+ e.tolerant || (!e.strict && this.widest > e.position) ? or(e.cond, isNull(e.column)) : e.cond)
261
+ this.target['where']!(and(this.user, ...conds))
262
+ }
263
+ }
264
+
265
+ /** Wraps a builder so the calls that return it return the wrapper, and `hooks` replace named methods. */
266
+ function wrap(
267
+ target: object,
268
+ policy: Policy,
269
+ hooksFor: (self: object, t: Obj) => Record<string, AnyFn>,
270
+ ): object {
271
+ let hooks: Record<string, AnyFn> | undefined
272
+ const self: object = new Proxy(target, {
273
+ get(t, prop) {
274
+ if (typeof prop === 'string') {
275
+ hooks ??= hooksFor(self, t as Obj)
276
+ if (Object.hasOwn(hooks, prop)) return hooks[prop]
277
+ }
278
+ const value: unknown = Reflect.get(t, prop, t)
279
+ if (typeof value !== 'function') return value
280
+ return (...args: unknown[]) => {
281
+ const result = (value as AnyFn).apply(t, args)
282
+ return result === t ? self : result
283
+ }
284
+ },
285
+ })
286
+ made.set(self, policy.owner)
287
+ return self
288
+ }
289
+
290
+ function whereHook(scope: WhereScope, self: object): AnyFn {
291
+ return (user) => {
292
+ scope.where(user as SQL | undefined)
293
+ return self
294
+ }
295
+ }
296
+
297
+ function joinHooks(scope: WhereScope, self: object, t: Obj): Record<string, AnyFn> {
298
+ const hooks: Record<string, AnyFn> = {}
299
+ for (const [name, type] of Object.entries(JOINS)) {
300
+ hooks[name] = (table, on) => {
301
+ const scopedOn = scope.join(table, type, on)
302
+ t[name]!(...(scopedOn === undefined ? [table] : [table, scopedOn]))
303
+ scope.refresh()
304
+ return self
305
+ }
306
+ }
307
+ return hooks
308
+ }
309
+
310
+ function selectBuilder(builder: object, table: unknown, policy: Policy): object {
311
+ const t = builder as Obj
312
+ const scope = new WhereScope(t, policy)
313
+ scope.table(table, 'select from', false)
314
+ return wrap(builder, policy, (self) => ({
315
+ where: whereHook(scope, self),
316
+ ...joinHooks(scope, self, t),
317
+ ...Object.fromEntries(SET_OPERATORS.map(name => [name, (right: unknown) => {
318
+ const checked = typeof right === 'function'
319
+ ? (...ops: unknown[]) => {
320
+ const resolved = (right as AnyFn)(...ops)
321
+ checkBuilder(policy, `${name}()`, resolved)
322
+ return resolved
323
+ }
324
+ : right
325
+ if (typeof right !== 'function') checkBuilder(policy, `${name}()`, right)
326
+ t[name]!(checked)
327
+ return self
328
+ }])),
329
+ }))
330
+ }
331
+
332
+ function selectStart(builder: object, policy: Policy): object {
333
+ return new Proxy(builder, {
334
+ get(t, prop) {
335
+ if (prop === 'from') {
336
+ return (table: unknown, ...rest: unknown[]) =>
337
+ selectBuilder((t as Obj)['from']!(table, ...rest) as object, table, policy)
338
+ }
339
+ const value: unknown = Reflect.get(t, prop, t)
340
+ return typeof value === 'function' ? (value as AnyFn).bind(t) : value
341
+ },
342
+ })
343
+ }
344
+
345
+ function updateBuilder(builder: object, table: unknown, policy: Policy): object {
346
+ const t = builder as Obj
347
+ const scope = new WhereScope(t, policy)
348
+ scope.table(table, 'update', true)
349
+ return wrap(builder, policy, (self) => ({
350
+ where: whereHook(scope, self),
351
+ ...joinHooks(scope, self, t),
352
+ from: (source) => {
353
+ t['from']!(source)
354
+ scope.table(source, 'update from', false)
355
+ return self
356
+ },
357
+ }))
358
+ }
359
+
360
+ function updateStart(builder: object, table: unknown, policy: Policy): object {
361
+ return new Proxy(builder, {
362
+ get(t, prop) {
363
+ if (prop === 'set') {
364
+ return (...args: unknown[]) => updateBuilder((t as Obj)['set']!(...args) as object, table, policy)
365
+ }
366
+ const value: unknown = Reflect.get(t, prop, t)
367
+ return typeof value === 'function' ? (value as AnyFn).bind(t) : value
368
+ },
369
+ })
370
+ }
371
+
372
+ function deleteBuilder(builder: object, table: unknown, policy: Policy): object {
373
+ const scope = new WhereScope(builder as Obj, policy)
374
+ scope.table(table, 'delete', true)
375
+ return wrap(builder, policy, (self) => ({ where: whereHook(scope, self) }))
376
+ }
377
+
378
+ function insertBuilder(builder: object, table: unknown, policy: Policy): object {
379
+ const t = builder as Obj
380
+ const scope = policy.scope(table, 'insert')
381
+ // A conflict with another tenant's row must update nothing: the existing row has to be ours.
382
+ const finish = (result: unknown): object =>
383
+ wrap(result as object, policy, (self, base) => ({
384
+ ...(scope && {
385
+ onConflictDoUpdate: (config: unknown) => {
386
+ const { where, setWhere, ...rest } = config as { where?: SQL; setWhere?: SQL }
387
+ base['onConflictDoUpdate']!({ ...rest, setWhere: and(where, setWhere, scope.cond) })
388
+ return self
389
+ },
390
+ }),
391
+ }))
392
+ return wrap(builder, policy, (self) => ({
393
+ values: (values) => {
394
+ if (!scope) return finish(t['values']!(values))
395
+ const { columnKey } = scope
396
+ const tenantId = policy.tenantId!
397
+ const inject = (row: Record<string, unknown>): Record<string, unknown> => {
398
+ const existing = row[columnKey]
399
+ if (existing !== undefined && existing !== tenantId) {
400
+ throw new Error(
401
+ `[pipework] Tenant-scoped insert: value has ${columnKey} "${String(existing)}" ` +
402
+ `but the current context tenant is "${tenantId}".\n\n` +
403
+ ` Remove the explicit ${columnKey} from your insert values — it's set automatically.\n` +
404
+ ' If you need cross-tenant writes, file an issue on the pipework repo.\n',
405
+ )
406
+ }
407
+ return { ...row, [columnKey]: tenantId }
408
+ }
409
+ const scoped = Array.isArray(values)
410
+ ? (values as Record<string, unknown>[]).map(inject)
411
+ : inject(values as Record<string, unknown>)
412
+ return finish(t['values']!(scoped))
413
+ },
414
+ select: (source) => {
415
+ if (scope) {
416
+ refuse(
417
+ `insert().select() into tenant-scoped table "${getTableName(table as PgTable)}"`,
418
+ `the tenant column ${scope.columnKey} cannot be set from a select.`,
419
+ )
420
+ }
421
+ if (typeof source === 'function') {
422
+ return finish(t['select']!((qb: unknown) => {
423
+ const body = (source as AnyFn)(scopedQueryBuilder(qb as object, policy))
424
+ checkBuilder(policy, 'insert().select()', body)
425
+ return body
426
+ }))
427
+ }
428
+ checkBuilder(policy, 'insert().select()', source)
429
+ return finish(t['select']!(source))
430
+ },
431
+ overridingSystemValue: () => {
432
+ t['overridingSystemValue']!()
433
+ return self
434
+ },
435
+ }))
436
+ }
437
+
438
+ function scopedWith(source: Obj, policy: Policy): AnyFn {
439
+ return (alias, selection) => {
440
+ const raw = source['$with']!(alias, selection) as {
441
+ as: AnyFn
442
+ materialized: () => { as: AnyFn }
443
+ notMaterialized: () => { as: AnyFn }
444
+ }
445
+ const build = (as: AnyFn) => (body: unknown) => {
446
+ const cte = typeof body === 'function'
447
+ ? as((qb: unknown) => {
448
+ const result = (body as AnyFn)(scopedQueryBuilder(qb as object, policy))
449
+ checkBuilder(policy, '$with() body', result)
450
+ return result
451
+ })
452
+ : (checkBuilder(policy, '$with() body', body), as(body))
453
+ made.set(cte as object, policy.owner)
454
+ return cte
455
+ }
456
+ return {
457
+ as: build(raw.as),
458
+ materialized: () => ({ as: build(raw.materialized().as) }),
459
+ notMaterialized: () => ({ as: build(raw.notMaterialized().as) }),
460
+ }
461
+ }
462
+ }
463
+
464
+ /** The statement starters of a connection, a `with(...)` result or a query builder. */
465
+ function starters(source: Obj, policy: Policy): Record<string, AnyFn> {
466
+ const hooks: Record<string, AnyFn> = {}
467
+ for (const name of SELECTS) {
468
+ if (typeof source[name] !== 'function') continue
469
+ hooks[name] = (...args) => selectStart(source[name]!(...args) as object, policy)
470
+ }
471
+ if (typeof source['insert'] === 'function') {
472
+ hooks['insert'] = (table) => {
473
+ policy.scope(table, 'insert')
474
+ return insertBuilder(source['insert']!(table) as object, table, policy)
475
+ }
476
+ }
477
+ if (typeof source['update'] === 'function') {
478
+ hooks['update'] = (table) => {
479
+ policy.scope(table, 'update')
480
+ return updateStart(source['update']!(table) as object, table, policy)
481
+ }
482
+ }
483
+ if (typeof source['delete'] === 'function') {
484
+ hooks['delete'] = (table) => {
485
+ policy.scope(table, 'delete')
486
+ return deleteBuilder(source['delete']!(table) as object, table, policy)
487
+ }
488
+ }
489
+ return hooks
490
+ }
491
+
492
+ function withHooks(source: Obj, policy: Policy): Record<string, AnyFn> {
493
+ return {
494
+ $with: scopedWith(source, policy),
495
+ with: (...ctes) => {
496
+ for (const cte of ctes) {
497
+ if (cte == null || typeof cte !== 'object' || made.get(cte) !== policy.owner) {
498
+ refuse('with()', 'every CTE must come from $with() on this connection.')
499
+ }
500
+ }
501
+ return starters(source['with']!(...ctes) as Obj, policy)
502
+ },
503
+ }
504
+ }
505
+
506
+ function scopedQueryBuilder(qb: object, policy: Policy): object {
507
+ const source = qb as Obj
508
+ const hooks = { ...starters(source, policy), ...withHooks(source, policy) }
509
+ return new Proxy(qb, {
510
+ get(t, prop) {
511
+ if (typeof prop === 'string' && Object.hasOwn(hooks, prop)) return hooks[prop]
512
+ const value: unknown = Reflect.get(t, prop, t)
513
+ return typeof value === 'function' ? (value as AnyFn).bind(t) : value
514
+ },
515
+ })
516
+ }
517
+
518
+ function createDb(db: DB, policy: Policy): DB {
519
+ const source = db as unknown as Obj
520
+ const hooks: Record<string, AnyFn> = {
521
+ ...starters(source, policy),
522
+ ...withHooks(source, policy),
523
+ $count: (table, filters) => {
524
+ const scope = policy.scope(table, 'count')
525
+ const combined = scope ? (filters !== undefined ? and(filters as SQL, scope.cond) : scope.cond) : filters
526
+ return source['$count']!(table, combined)
527
+ },
528
+ transaction: (callback, config) =>
529
+ source['transaction']!((tx: unknown) => (callback as AnyFn)(createDb(tx as DB, policy)), config),
530
+ }
531
+ if (policy.refuseExecute) {
532
+ hooks['execute'] = () => {
533
+ throw new Error(
534
+ '[pipework] execute() is not available on a tenant-scoped database connection.\n\n' +
535
+ ' Tenant-scoped connections only support select, insert, update, and delete\n' +
536
+ ' operations, which are automatically filtered by tenant.\n\n' +
537
+ ' If you need to run a query that the builder cannot express, file an issue\n' +
538
+ ' on the pipework repo describing your use case.\n',
539
+ )
540
+ }
541
+ }
542
+ const proxy = new Proxy(db, {
543
+ get(target, prop, receiver) {
544
+ if (typeof prop === 'string') {
545
+ if (Object.hasOwn(hooks, prop)) return hooks[prop]
546
+ if (prop === 'query') {
547
+ const query = Reflect.get(target, prop, receiver) as object | undefined
548
+ if (query && Object.keys(query).length > 0) {
549
+ return new Proxy({}, {
550
+ get() {
551
+ refuse('A relational query (db.query)', 'it reads tables without the tenant filter.')
552
+ },
553
+ })
554
+ }
555
+ return query
556
+ }
557
+ }
558
+ return Reflect.get(target, prop, receiver)
559
+ },
560
+ })
561
+ if (policy.refuseExecute && policy.tenantId !== null) scopedHandles.set(proxy, { raw: db, tenantId: policy.tenantId })
562
+ return proxy
563
+ }
564
+
565
+ export function createTenantGuardDb(db: DB): DB {
566
+ return createDb(db, guardPolicy)
567
+ }
568
+
569
+ export function createScopedDb(db: DB, tenantId: string): DB {
570
+ return createDb(db, scopedPolicy(tenantId))
571
+ }
572
+
573
+ /** The system channel's handle on a platform role: every tenant table is refused at build time. */
574
+ export function createSystemGuardDb(db: DB): DB {
575
+ return createDb(db, systemPolicy)
576
+ }
577
+
578
+ /** A door's handle: SELECT from the door's table and from non-tenant tables; any other tenant table, and any write to the door's table, is refused at build time. */
579
+ export function createDoorDb(db: DB, door: { readonly name: string; readonly table: string; readonly ops?: readonly string[] }): DB {
580
+ return createDb(db, doorPolicy(door))
581
+ }
@@ -0,0 +1,14 @@
1
+ /** Configuration for tenant extraction and isolation strategy. */
2
+ export interface TenantConfig {
3
+ readonly extract: (auth: unknown) => string | null
4
+ readonly sessionVars?: Record<string, (ctx: TenantContext) => string>
5
+ readonly validate?: (tenantId: string) => boolean
6
+ readonly required?: boolean
7
+ }
8
+
9
+ /** Resolved tenant context — tenant ID, request ID, and user ID. */
10
+ export interface TenantContext {
11
+ readonly tenant: string
12
+ readonly requestId: string
13
+ readonly userId: string | null
14
+ }
@@ -0,0 +1,27 @@
1
+ const UUID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
2
+
3
+ const SAFE_VAR_NAME = /^[a-z][a-z0-9_.]*$/
4
+
5
+ export function validateTenantId(value: string): boolean {
6
+ return UUID_PATTERN.test(value)
7
+ }
8
+
9
+ export function validateSessionVarName(name: string): void {
10
+ if (!SAFE_VAR_NAME.test(name)) {
11
+ throw new Error(
12
+ `[pipework] Invalid session variable name: "${name}". ` +
13
+ `Variable names must match /^[a-z][a-z0-9_.]*$/. ` +
14
+ `This prevents SQL injection in SET LOCAL statements.`,
15
+ )
16
+ }
17
+ }
18
+
19
+ export function validateSessionVarValue(value: string): void {
20
+ if (/[;'"\n\r]/.test(value)) {
21
+ throw new Error(
22
+ `[pipework] Unsafe session variable value rejected. ` +
23
+ `Values must not contain semicolons, quotes, or newlines. ` +
24
+ `Received: "${value.slice(0, 40)}${value.length > 40 ? '...' : ''}"`,
25
+ )
26
+ }
27
+ }