pipework 0.10.8 → 0.12.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 (74) hide show
  1. package/CHANGELOG.md +11 -0
  2. package/REFERENCE.md +167 -7
  3. package/dist/REFERENCE.md +1033 -0
  4. package/dist/async/jobs/execute.d.ts.map +1 -1
  5. package/dist/async/jobs/execute.js +13 -4
  6. package/dist/async/jobs/execute.js.map +1 -1
  7. package/dist/auth/auth/chain.js +2 -2
  8. package/dist/auth/auth/chain.js.map +1 -1
  9. package/dist/core/errors/base.d.ts +7 -0
  10. package/dist/core/errors/base.d.ts.map +1 -1
  11. package/dist/core/errors/base.js +22 -0
  12. package/dist/core/errors/base.js.map +1 -1
  13. package/dist/core/errors/index.d.ts +1 -1
  14. package/dist/core/errors/index.d.ts.map +1 -1
  15. package/dist/core/errors/index.js +1 -1
  16. package/dist/core/errors/index.js.map +1 -1
  17. package/dist/core/pipework.d.ts +1 -0
  18. package/dist/core/pipework.d.ts.map +1 -1
  19. package/dist/core/pipework.js.map +1 -1
  20. package/dist/core/runtime/broker.d.ts +10 -0
  21. package/dist/core/runtime/broker.d.ts.map +1 -0
  22. package/dist/core/runtime/broker.js +57 -0
  23. package/dist/core/runtime/broker.js.map +1 -0
  24. package/dist/data/db/expressions.d.ts +3 -0
  25. package/dist/data/db/expressions.d.ts.map +1 -1
  26. package/dist/data/db/expressions.js.map +1 -1
  27. package/dist/data/db/serializable.d.ts +1 -0
  28. package/dist/data/db/serializable.d.ts.map +1 -1
  29. package/dist/data/db/serializable.js.map +1 -1
  30. package/dist/data/db/values.d.ts +1 -0
  31. package/dist/data/db/values.d.ts.map +1 -1
  32. package/dist/data/db/values.js.map +1 -1
  33. package/dist/data/write/delete.d.ts +2 -2
  34. package/dist/data/write/delete.d.ts.map +1 -1
  35. package/dist/data/write/delete.js.map +1 -1
  36. package/dist/data/write/insert.d.ts +2 -2
  37. package/dist/data/write/insert.d.ts.map +1 -1
  38. package/dist/data/write/insert.js.map +1 -1
  39. package/dist/data/write/update.d.ts +2 -2
  40. package/dist/data/write/update.d.ts.map +1 -1
  41. package/dist/data/write/update.js.map +1 -1
  42. package/dist/data/write/upsert.d.ts +2 -2
  43. package/dist/data/write/upsert.d.ts.map +1 -1
  44. package/dist/data/write/upsert.js.map +1 -1
  45. package/dist/index.d.ts +2 -2
  46. package/dist/index.d.ts.map +1 -1
  47. package/dist/index.js +1 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/request/context/store.d.ts +1 -2
  50. package/dist/request/context/store.d.ts.map +1 -1
  51. package/dist/request/context/store.js +6 -2
  52. package/dist/request/context/store.js.map +1 -1
  53. package/dist/request/di/builder.d.ts +45 -21
  54. package/dist/request/di/builder.d.ts.map +1 -1
  55. package/dist/request/di/builder.js +23 -0
  56. package/dist/request/di/builder.js.map +1 -1
  57. package/dist/request/di/index.d.ts +2 -2
  58. package/dist/request/di/index.d.ts.map +1 -1
  59. package/dist/request/di/resolve.d.ts +26 -1
  60. package/dist/request/di/resolve.d.ts.map +1 -1
  61. package/dist/request/di/resolve.js +42 -4
  62. package/dist/request/di/resolve.js.map +1 -1
  63. package/dist/request/di/types.d.ts +23 -0
  64. package/dist/request/di/types.d.ts.map +1 -1
  65. package/dist/request/fitting/namespace.d.ts +1 -1
  66. package/dist/request/fitting/namespace.d.ts.map +1 -1
  67. package/dist/request/fixture/to-handlers.d.ts.map +1 -1
  68. package/dist/request/fixture/to-handlers.js +1 -0
  69. package/dist/request/fixture/to-handlers.js.map +1 -1
  70. package/dist/request/http/server.js +2 -2
  71. package/dist/request/http/server.js.map +1 -1
  72. package/dist/trace/runtime/step-meta.d.ts +1 -0
  73. package/dist/trace/runtime/step-meta.d.ts.map +1 -1
  74. package/package.json +2 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,16 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.12.0
4
+
5
+ - **Fix: a `PipeworkError` thrown by a shared library that resolved its own copy of pipework now maps to its real HTTP status (403/401/…) instead of a generic 500.** When a self-contained pipework workspace consumes a shared leaf library that also imports pipework at runtime, pnpm resolves two physical copies of pipework — so the library's `ForbiddenError` and the workspace's `ForbiddenError` are distinct class objects and `instanceof` between them is `false`. The HTTP error mapper keyed on `instanceof PipeworkError`, so a leaf-thrown error was unrecognized and fell through to 500 (and `.rejects.toThrow(ForbiddenError)` across the boundary failed even though the right error was thrown). Error identity is now carried on a global-`Symbol` brand (`Symbol.for('pipework.error')`) — the same cross-realm technique drizzle uses for table identity — and the mapper checks the brand, not the constructor. A new public predicate **`isPipeworkError(error)`** is exported for consumers whose own handlers have the same `instanceof` exposure. Issue #333.
6
+ - **Fix: a shared leaf library and its consuming workspace now share one request context (AsyncLocalStorage), and two incompatible pipework versions in one process fail loudly instead of silently.** The request ALS was a plain module singleton, so the two physical copies above each had their own `AsyncLocalStorage` — a context entered by the workspace was invisible to `pipe()` called from the leaf ("No active context" on a live request). The ALS (and the version that owns it) is now pinned to a process-global slot keyed by `Symbol.for('pipework.runtime')`: same-version copies share one ALS (N copies behave as one), and a second copy of a *different* version throws an actionable error naming both versions rather than forking silently. The connection pool was never a separate singleton (it hangs off the context's instance), so it is fixed transitively.
7
+ - **Fix: `pipe.insert` / `pipe.update` / `pipe.upsert` / `pipe.remove` now accept a concrete table.** Their generic was constrained to `DefinedTable<FieldRecord> & PgTable`, but a concrete `pipe.define(...)` result (`DefinedTable<{ specific fields }>`) is not assignable to `DefinedTable<FieldRecord>` (the projected columns are invariant under the field generics) — so `pipe.insert(myTable)` failed to type-check for callers. The constraint is relaxed to `DefinedTable<any> & PgTable`, matching the pattern already used elsewhere for the same reason. Row-type inference is unchanged (the concrete table type still flows through).
8
+ - **Fix: manual-transaction jobs (`.transaction('manual')`) no longer expose the database handles at the top level of the handler deps, and handler deps no longer carry a `[string]: never` index signature.** The base builder deps type was `Record<string, never>`, an index signature that (a) made `Omit<TDeps, keyof TDb>` unable to remove the declared databases — so a manual-mode handler's deps type still appeared to carry `app` (a non-transactional handle that is a footgun, the exact thing the mode prevents) — and (b) made `deps.anyTypo` resolve to `never` instead of a compile error for every handler. The base is now `Record<never, never>`: manual mode correctly strips the db deps (they appear only inside `checkpoint`'s callback), and a typo'd dependency is now a type error.
9
+
10
+ ## 0.11.0
11
+
12
+ - **Feature: jobs can own their transaction boundaries — `fitting.job(...).transaction('manual')` opts a job out of the single framework-owned outer transaction.** Every tenant job was wrapped, by the framework, in one outer transaction around its whole `.fit` run (`src/async/jobs/execute.ts`), making the job strictly all-or-nothing: a long sequence of independently-valid work either committed as one transaction or was lost entirely — a failure near the end rolled back the whole run, and the single long-open transaction pinned a snapshot (blocking vacuum) and held its connection for the job's full duration. A job that genuinely wants **one warm process** (to amortize in-memory state across a long run) but **many transactions** (to checkpoint progress durably) had no way to express it: an in-handler `pipe.transaction()` nests as a savepoint inside the outer tx, so it only commits when the whole job commits. `.transaction('manual')` separates the transaction axis from the process axis. In manual mode the framework builds the job context (tenant, trace, resolved databases) but opens **no** outer transaction; instead the handler receives a `checkpoint(fn)` primitive — each call opens a fresh top-level, tenant-scoped transaction (re-establishing the `SET LOCAL pipework.tenant_id` / custom session vars so database-side RLS sees the tenant on every checkpoint), runs `fn` with the declared databases bound as **raw** transactions, and commits it independently. The databases declared with `.use()` move out of the top-level deps (a non-transactional handle there would be a footgun) and into `checkpoint`'s callback; non-database deps (`auth`, `input`, …) stay at the top level. Multi-DB jobs get one independent transaction per database inside each checkpoint, and an outbox enqueue issued inside a checkpoint (`queue.enqueue(db, …)` with the checkpoint's handle) rides that checkpoint's commit. The contract shifts deliberately: a manual-mode job is **at-least-once / idempotent-on-resume**, not atomic — a crash after committing checkpoints 0..N leaves them committed, the job is re-claimed, and the handler must **resume** (it owns its idempotency key), not restart. Default jobs are unchanged (single outer transaction). Misuse fails at definition: `.transaction('manual')` without `.job(...)`, or without any `.use(...)`, throws an actionable error from `.fit()`. New `JobTransactionMode` and `Checkpoint<TDb>` exported types. Issue #332.
13
+
3
14
  ## 0.10.8
4
15
 
5
16
  - **Fix: a multi-database app could not make a bare `db()` / `pipe()` call — every no-name database resolution threw `ConfigError: Multiple databases configured` at runtime.** With >1 database declared, `defaultDatabaseName()` and `database(undefined)` had no way to know which database was primary, so they threw on *any* ambiguity. That throw is correct safety for a genuinely-ambiguous config, but there was no way to *resolve* the ambiguity short of naming the database at every call site — turning the addition of a second (e.g. read-only) database into a sweep across every bare `db()`/`pipe()` in the app. Configs now declare the default once: a top-level `defaultDatabase: '<name>'` (a key of `databases`). When set, both no-name resolvers return that database; the throw is preserved only for multi-DB configs with *no* default declared (still genuinely ambiguous) and single-DB configs are unchanged (the sole database is always the default, no declaration needed). `defaultDatabase` naming an undeclared database is a load-time `ConfigError` with a closest-match suggestion. Complements 0.10.6, which fixed the parallel throw on the *test-harness* template path; this fixes the runtime resolution path.
package/REFERENCE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Pipework API Reference
2
2
 
3
3
  > Auto-generated from source JSDoc. Do not edit manually.
4
- > Generated: 2026-05-13
4
+ > Generated: 2026-06-29
5
5
 
6
6
  ## Contents
7
7
 
@@ -21,6 +21,7 @@
21
21
  - [jsonb — JSONB Column Helpers](#jsonb-jsonb-column-helpers)
22
22
  - [schema — Validation & Schema](#schema-validation-schema)
23
23
  - [temporal — Bitemporal Data](#temporal-bitemporal-data)
24
+ - [valve](#valve)
24
25
  - [vector — Vector & Full-Text Search](#vector-vector-full-text-search)
25
26
  - [audit — Audit Logging](#audit-audit-logging)
26
27
  - [cache — Caching](#cache-caching)
@@ -31,6 +32,8 @@
31
32
  - [flow — Context (AsyncLocalStorage)](#flow-context-asynclocalstorage)
32
33
  - [http — Server](#http-server)
33
34
  - [openapi — API Documentation](#openapi-api-documentation)
35
+ - [test](#test)
36
+ - [trace](#trace)
34
37
 
35
38
  ## Core
36
39
 
@@ -41,7 +44,7 @@ Application instance and foundational types.
41
44
  Creates the Pipework application instance. This is the entry point for every Pipework app. Define in `pipework.config.ts` and export as default. /
42
45
 
43
46
  ```typescript
44
- function createManifold<const TEnv extends EnvDefs = EnvDefs>( raw: Omit<Blueprint, 'env'> &
47
+ function createManifold<const TEnv extends EnvDefs = EnvDefs>( raw: Omit<BlueprintInput, 'env'> &
45
48
  ```
46
49
 
47
50
  ### Types
@@ -52,7 +55,7 @@ function createManifold<const TEnv extends EnvDefs = EnvDefs>( raw: Omit<Bluepri
52
55
  interface Manifold<TEnv extends EnvDefs = EnvDefs>
53
56
  ```
54
57
 
55
- **`SeedOptions`**
58
+ **`SeedOptions`** — ALS context the {@link Manifold.seed} callback runs under — tenant scope, auth principal, and job type.
56
59
 
57
60
  ```typescript
58
61
  interface SeedOptions
@@ -330,7 +333,7 @@ Returns the SQL string for a tenant isolation RLS policy — use in migrations.
330
333
 
331
334
  ### `tenant.verify`
332
335
 
333
- Cross-checks ALS tenant against Postgres set_config('pipework.tenant_id') — defense-in-depth.
336
+ Cross-checks the ALS principal (tenant, user, asOf) against the Postgres session GUCs — defense-in-depth.
334
337
 
335
338
  ### `tenant.propagate`
336
339
 
@@ -444,10 +447,47 @@ Creates a script surface — a one-shot process that runs and exits (migrations,
444
447
 
445
448
  Database accessor — returns a Drizzle DB instance scoped to the current request/job/test context. Requires active AsyncLocalStorage context.
446
449
 
450
+ ### `pipe.system`
451
+
452
+ The system channel — deliberate cross-tenant access for pre-tenant surfaces (auth membership resolution, public webhook ingress, admin tooling). Never tenant-scoped or tenant-guarded; runs on the owner-role connection outside the request transaction.
453
+
447
454
  ### `pipe.filter`
448
455
 
449
456
  Query operators and aggregates — eq, gt, lt, and, or, count, sum, etc.
450
457
 
458
+ ### `pipe.aliasedRelation`
459
+
460
+
461
+ ### `pipe.aliasedTable`
462
+
463
+
464
+ ### `pipe.aliasedTableColumn`
465
+
466
+
467
+ ### `pipe.getTableColumns`
468
+
469
+
470
+ ### `pipe.getTableName`
471
+
472
+
473
+ ### `pipe.getViewName`
474
+
475
+
476
+ ### `pipe.getViewSelectedFields`
477
+
478
+
479
+ ### `pipe.isTable`
480
+
481
+
482
+ ### `pipe.createMany`
483
+
484
+
485
+ ### `pipe.createOne`
486
+
487
+
488
+ ### `pipe.relations`
489
+
490
+
451
491
  ### `pipe.migrate`
452
492
 
453
493
  Runs migrations for all configured databases.
@@ -488,21 +528,37 @@ Registers a domain definition with table, validators, and factory projections.
488
528
 
489
529
  Returns the global registry of all domain definitions.
490
530
 
531
+ ### `pipe.update`
532
+
533
+ Pipework-owned UPDATE builder — the only consumer-facing way to update pipework-managed tables (Q8).
534
+
535
+ ### `pipe.insert`
536
+
537
+ Pipework-owned INSERT builder — the only consumer-facing way to insert into pipework-managed tables (Q8).
538
+
539
+ ### `pipe.delete`
540
+
541
+ Pipework-owned DELETE builder — the only consumer-facing way to delete from pipework-managed tables (Q8).
542
+
543
+ ### `pipe.upsert`
544
+
545
+ Pipework-owned UPSERT builder — insert with conflict resolution (Q8).
546
+
491
547
  ### Types
492
548
 
493
- **`WindowSpec`**
549
+ **`WindowSpec`** — A window-frame specification — the `PARTITION BY` / `ORDER BY` clauses applied by {@link over}.
494
550
 
495
551
  ```typescript
496
552
  interface WindowSpec
497
553
  ```
498
554
 
499
- **`ValueColumnDef`**
555
+ **`ValueColumnDef`** — One column of a typed `VALUES` clause: its name and the PostgreSQL type each value in that column is cast to. See {@link values}.
500
556
 
501
557
  ```typescript
502
558
  interface ValueColumnDef
503
559
  ```
504
560
 
505
- **`SerializableOptions`**
561
+ **`SerializableOptions`** — Retry policy for {@link serializable}: how many attempts on a serialization failure / deadlock, and the base backoff between them.
506
562
 
507
563
  ```typescript
508
564
  interface SerializableOptions
@@ -626,6 +682,94 @@ Query all versions of temporal records (no date filter).
626
682
 
627
683
  Close the current version and create a new one with updated data.
628
684
 
685
+ ## valve
686
+
687
+ Valve namespace — typed, directional, gated boundaries between code silos.
688
+
689
+ ### `valve.supply`
690
+
691
+ Declare a handoff valve — the producer side of a payload crossing.
692
+
693
+ ### `valve.gate`
694
+
695
+ Declare a transition-gate valve — the producer side of a gated state change.
696
+
697
+ ### `valve.faucet`
698
+
699
+ Declare a faucet — the consumer side of a `supply -> faucet` edge.
700
+
701
+ ### `valve.cross`
702
+
703
+ Decision outcome: cross the valve, delivering the payload.
704
+
705
+ ### `valve.held`
706
+
707
+ Decision outcome: hold the crossing in a provisional, reviewable state.
708
+
709
+ ### `valve.failed`
710
+
711
+ Decision outcome: evaluation failed — retryable, never a thrown exception.
712
+
713
+ ### `valve.crossSupply`
714
+
715
+ Execute a handoff crossing in the caller's transaction (unrecorded mechanics).
716
+
717
+ ### `valve.crossGate`
718
+
719
+ Execute a transition-gate crossing (unrecorded mechanics).
720
+
721
+ ### `valve.executeSupplyCrossing`
722
+
723
+ Execute a handoff crossing with idempotency and provenance recording.
724
+
725
+ ### `valve.executeGateCrossing`
726
+
727
+ Execute a transition-gate crossing with provenance recording.
728
+
729
+ ### `valve.enqueueCrossing`
730
+
731
+ Enqueue a handoff crossing to run durably as a retried, dead-letterable job.
732
+
733
+ ### `valve.runCrossingJob`
734
+
735
+ The durable-crossing job-handler body — wire into a worker.
736
+
737
+ ### `valve.VALVE_CROSSING_JOB_TYPE`
738
+
739
+ Job type for a durable valve crossing.
740
+
741
+ ### `valve.graph`
742
+
743
+ Assemble the directional valve graph from every registered valve and faucet.
744
+
745
+ ### `valve.downstreamFaucets`
746
+
747
+ The faucet nodes a valve feeds.
748
+
749
+ ### `valve.contextDependents`
750
+
751
+ The gates whose enrichment context depends on a valve.
752
+
753
+ ### `valve.diffContract`
754
+
755
+ Diff a valve contract between two versions.
756
+
757
+ ### `valve.emitArtifact`
758
+
759
+ Project a handoff supply to its lightweight contract artifact.
760
+
761
+ ### `valve.valves`
762
+
763
+ All registered producer valves, keyed by name.
764
+
765
+ ### `valve.getValve`
766
+
767
+ Look up a registered valve by name.
768
+
769
+ ### `valve.faucetsOf`
770
+
771
+ The faucets consuming a named valve.
772
+
629
773
  ## vector — Vector & Full-Text Search
630
774
 
631
775
  Vector and full-text search namespace — pgvector column types, similarity queries, and FTS operators.
@@ -690,6 +834,10 @@ Audit logging namespace — structured audit trail for data changes.
690
834
 
691
835
  Creates an Audit instance that writes audit records to a Postgres table.
692
836
 
837
+ ### `audit.query`
838
+
839
+ Queries the framework-managed `audit_record` table. Tenant-scoped via the active Flow.
840
+
693
841
  ## cache — Caching
694
842
 
695
843
  In-memory caching namespace — TTL-based with stats, preload, and tenant-aware variants.
@@ -871,3 +1019,15 @@ OpenAPI spec generation namespace.
871
1019
  ### `openapi.generate`
872
1020
 
873
1021
  Generates an OpenAPI 3.1 document from route registrations — schemas derived from fitting() input/output.
1022
+
1023
+ ## test
1024
+
1025
+ ## trace
1026
+
1027
+ ### `__flowStep`
1028
+
1029
+ Wraps `impl` so that, under an active traced Flow, each call records a `trace_step`.
1030
+
1031
+ ```typescript
1032
+ function __flowStep<F extends AnyFunction>(meta: FlowStepMeta, impl: F): F
1033
+ ```