pipework 0.189.0 → 0.191.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.191.0
4
+
5
+ - **Added: `$with` on a tenant-scoped connection** (#748). `db.$with('m').materialized().as(db.select(…).from(tenantTable))` followed by `db.with(m).select().from(m)` returns only the context tenant's rows, the same as an inline subquery.
6
+ - A CTE body must be built by the same scoped connection. The callback form `(qb) => …` gets a scoped query builder.
7
+ - A raw `sql` body, or a builder from `pipe.system()` or another connection, is refused with a message saying why.
8
+ - `db.with(…)` accepts only this connection's CTEs and scopes what follows it.
9
+ - **Fixed: the tenant-scoped connection let other tenants' rows through on several paths** (#748). Postgres row-level security still applied wherever it was configured, but pipework's own tenant filter was missing on these:
10
+ - A scoped select used as a subquery, through `inArray(…)`, `.as()`, `getSQL()` or `toSQL()`, had no tenant filter. The filter used to be added only when the query was awaited. It is now in the SQL as soon as the table is named.
11
+ - A joined tenant table (inner, left, right, full or cross) wasn't filtered. Its condition now goes in the join's ON clause, so a left join keeps its unmatched rows. Right and full joins also keep the tables before them to the tenant.
12
+ - `db.with(x)` passed straight through to the unscoped connection.
13
+ - `db.transaction(cb)` handed `cb` an unscoped `tx`.
14
+ - `$count` counted every tenant's rows.
15
+ - `update().from(tenantTable)` read every tenant's rows.
16
+ - A `union` (or other set operation) accepted an operand built elsewhere.
17
+ - An `onConflictDoUpdate` upsert on a unique key that excludes the tenant column could overwrite another tenant's row. Its update is now limited to the tenant's own rows, and a conflict with another tenant's row updates nothing and returns no row. `pipe.upsert` and audited upserts are covered.
18
+ - The no-tenant guard connection refuses a tenant table on all of the same paths.
19
+ - **Changed:**
20
+ - `insert().select()` into a tenant table is refused on a scoped connection, because the tenant column can't be set from a select.
21
+ - `db.query` (relational queries) is refused on a scoped connection that has tables.
22
+
23
+ ## 0.190.0
24
+
25
+ - **Fixed: a NULL element in an array read back wrong** (#747). On every path (column selects, `db.execute`, `arrayAgg`) an unquoted NULL element now reads as `null`, for every array type:
26
+ - Numeric arrays used to give `NaN`, and bigint arrays threw `Cannot convert NULL to a BigInt`.
27
+ - `text[]`, `uuid[]` and enum arrays gave the string `'NULL'`, which a real `'NULL'` string couldn't be told apart from.
28
+ - `bool[]` gave `false`.
29
+ - `jsonb[]` threw on parse.
30
+ - Date and timestamp array columns gave `Invalid Date`.
31
+
32
+ A quoted `"NULL"` string stays a string, and a float `NaN` stays `NaN`.
33
+ - The fix replaces the driver's array parser for every array type it learns at connect, enums and domains included. An enum created after a connection opened is learned by connections opened after it.
34
+ - `db.execute` now returns a `numeric[]` as an array of strings (`['1.5', null]`) instead of the text `'{1.5,NULL}'`.
35
+
3
36
  ## 0.189.0
4
37
 
5
38
  - **Added: a step's own priority** (#744). `steps: { shadow: { priority: -100, … } }` queues that step's jobs at its own priority instead of the run's, in every run and for every fan-out instance. Higher priorities are claimed first, across runs, per worker class. A measurement step set below every run priority waits behind every other step on its workers, and its run still finishes and records as before. The steps it needs, the steps that need it and the run's finish job keep the run's priority.
package/dist/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-10-22
4
+ > Generated: 2026-10-20
5
5
 
6
6
  ## Contents
7
7
 
@@ -816,10 +816,6 @@ Acquires a transaction-scoped advisory lock. Released at commit/rollback.
816
816
 
817
817
  Runs a function in a SERIALIZABLE transaction with automatic retry on serialization failures and deadlocks.
818
818
 
819
- ### `pipe.withTimeouts`
820
-
821
- Sets statement_timeout and/or lock_timeout for the current transaction in one statement, then runs fn; a statement cancelled for them rejects with StatementTimeoutError or LockTimeoutError.
822
-
823
819
  ### `pipe.bulkSet`
824
820
 
825
821
  Bulk UPDATE ... FROM (VALUES ...) — updates multiple rows with per-row values in a single statement.
@@ -828,10 +824,6 @@ Bulk UPDATE ... FROM (VALUES ...) — updates multiple rows with per-row values
828
824
 
829
825
  Produces a typed VALUES clause for bulk operations — the base primitive for bulkSet and bulkUpsert.
830
826
 
831
- ### `pipe.fn`
832
-
833
- A set-returning SQL function as a typed source for `.from()` and joins — `pipe.fn('schema.f', { args: [field.uuid()], columns: { path: field.text() } })`. Arguments are bound and cast to their declared types.
834
-
835
827
  ### `pipe.field`
836
828
 
837
829
  Field builders for domain definitions — uuid(), text(), integer(), etc.
@@ -844,10 +836,6 @@ Registers a domain definition with table, validators, and factory projections. T
844
836
 
845
837
  Returns the global registry of all domain definitions.
846
838
 
847
- ### `pipe.external`
848
-
849
- Declares a table or view another service owns (`pipe.external(name, fields, { schema, table? })`): typed columns and every query operator, read-only, never migrated, not tenant-scoped.
850
-
851
839
  ### `pipe.update`
852
840
 
853
841
  Pipework-owned UPDATE builder — the only consumer-facing way to update pipework-managed tables (Q8).
@@ -878,54 +866,12 @@ interface WindowSpec
878
866
  interface ValueColumnDef
879
867
  ```
880
868
 
881
- **`JsonbElements`** — A jsonb array read as its elements' text. Produced by {@link jsonbElements}.
882
-
883
- ```typescript
884
- interface JsonbElements
885
- ```
886
-
887
- **`ArraySource`** — Something whose elements `arrayAny` / `arrayAll` can test.
888
-
889
- ```typescript
890
- type ArraySource =
891
- ```
892
-
893
- **`ElementOf`** — The element type of an {@link ArraySource}.
894
-
895
- ```typescript
896
- type ElementOf<S> =
897
- ```
898
-
899
- **`FunctionSpec`** — Declares a SQL function's argument and result-column shapes. See {@link fn}.
900
-
901
- ```typescript
902
- interface FunctionSpec<TArgs extends readonly AnyField[], TColumns extends Record<string, AnyField>>
903
- ```
904
-
905
- **`FunctionSourceFields`** — The selected fields of a function source: each result column as a typed SQL expression.
906
-
907
- ```typescript
908
- type FunctionSourceFields<TColumns extends Record<string, AnyField>> =
909
- ```
910
-
911
- **`FunctionSource`** — A function call usable in `.from()` and joins; its result columns are properties of it.
912
-
913
- ```typescript
914
- type FunctionSource<TAlias extends string, TColumns extends Record<string, AnyField>> =
915
- ```
916
-
917
869
  **`SerializableOptions`** — Retry policy for {@link serializable}: how many attempts on a serialization failure / deadlock, and the base backoff between them.
918
870
 
919
871
  ```typescript
920
872
  interface SerializableOptions
921
873
  ```
922
874
 
923
- **`TimeoutOptions`** — Which timeouts {@link withTimeouts} sets; each a positive integer number of milliseconds.
924
-
925
- ```typescript
926
- interface TimeoutOptions
927
- ```
928
-
929
875
  **`ManagedConnection`** — A standalone pooled connection: the raw postgres.js client, its drizzle handle, and a `close()` that ends the pool.
930
876
 
931
877
  ```typescript
@@ -946,10 +892,6 @@ JSONB query operators — typed helpers for PostgreSQL JSONB containment, key ch
946
892
 
947
893
  The @> containment operator — tests whether a JSONB column contains the given value.
948
894
 
949
- ### `jsonb.merge`
950
-
951
- Merges an object into a JSONB column in `.set()`: `coalesce(col, '{}') || value`. Top-level keys overwrite; others are kept.
952
-
953
895
  ### `jsonb.containedBy`
954
896
 
955
897
  The <@ contained-by operator — tests whether a JSONB column is contained by the given value.
@@ -1016,60 +958,6 @@ type Schema<T = unknown> = z.ZodType<T>
1016
958
  type Infer<T extends Schema> = z.infer<T>
1017
959
  ```
1018
960
 
1019
- **`StringSchema`** — A string validator — what check.string() returns, nameable without importing zod.
1020
-
1021
- ```typescript
1022
- interface StringSchema extends z.ZodString
1023
- ```
1024
-
1025
- **`NumberSchema`** — A number validator — what check.number() returns.
1026
-
1027
- ```typescript
1028
- interface NumberSchema extends z.ZodNumber
1029
- ```
1030
-
1031
- **`BooleanSchema`** — A boolean validator — what check.boolean() returns.
1032
-
1033
- ```typescript
1034
- interface BooleanSchema extends z.ZodBoolean
1035
- ```
1036
-
1037
- **`EnumSchema`** — An enum validator over string literals — what check.enum([...]) returns.
1038
-
1039
- ```typescript
1040
- interface EnumSchema<V extends string = string> extends z.ZodEnum<
1041
- ```
1042
-
1043
- **`LiteralSchema`** — A literal validator — what check.literal(v) returns.
1044
-
1045
- ```typescript
1046
- interface LiteralSchema<V extends string | number | boolean | null = string> extends z.ZodLiteral<V>
1047
- ```
1048
-
1049
- **`ArraySchema`** — An array validator — what check.array(item) returns.
1050
-
1051
- ```typescript
1052
- interface ArraySchema<Item extends z.core.SomeType = z.core.SomeType> extends z.ZodArray<Item>
1053
- ```
1054
-
1055
- **`OptionalSchema`** — An optional validator — what check.optional(inner) returns.
1056
-
1057
- ```typescript
1058
- interface OptionalSchema<Inner extends z.core.SomeType = z.core.SomeType> extends z.ZodOptional<Inner>
1059
- ```
1060
-
1061
- **`NullableSchema`** — A nullable validator — what check.nullable(inner) returns.
1062
-
1063
- ```typescript
1064
- interface NullableSchema<Inner extends z.core.SomeType = z.core.SomeType> extends z.ZodNullable<Inner>
1065
- ```
1066
-
1067
- **`ObjectSchema`** — An object validator whose .extend, .pick, .omit and .partial results stay ObjectSchema — what check.object({...}) returns.
1068
-
1069
- ```typescript
1070
- type ObjectSchema<S extends Shape = Shape> =
1071
- ```
1072
-
1073
961
  ## temporal — Bitemporal Data
1074
962
 
1075
963
  Temporal (bitemporal) data namespace — version history with effective date ranges.
@@ -1586,11 +1474,7 @@ interface RateLimitConfig
1586
1474
  interface RateLimitWindow
1587
1475
  ```
1588
1476
 
1589
- **`RateLimitClass`**
1590
-
1591
- A named limit class: any of a per-user, per-tenant, per-address and total window, each counted on its own.
1592
- Each key takes one window or an array of windows (distinct `windowSeconds`); a request is counted against
1593
- all of a key's windows and refused if any is over.
1477
+ **`RateLimitClass`** — A named limit class: any of a per-user, per-tenant and per-address window, each counted on its own.
1594
1478
 
1595
1479
  ```typescript
1596
1480
  interface RateLimitClass
@@ -1606,8 +1490,8 @@ interface RateLimitRefusal
1606
1490
 
1607
1491
  Named limit classes, checked after auth and counted in Postgres (`pipework_rate_limit_counts`, opted in
1608
1492
  with the blueprint's `rateLimits: {}`), so every server process on the same database shares one count.
1609
- Keys are counted perUser, perTenant, perAddress, total in turn, up to the first key with a window over its max, so one user's
1610
- refused requests don't spend the tenant's budget. The 429's Retry-After is the longest wait of the windows that refused. A route is in `signedIn` (auth required, or optional with
1493
+ Windows are counted perUser, perTenant, perAddress in turn, up to the first one over its max, so one user's
1494
+ refused requests don't spend the tenant's budget. A route is in `signedIn` (auth required, or optional with
1611
1495
  a user), `public` (no auth, or optional without a user) or the class its fitting names with `.rateLimit(name)`.
1612
1496
 
1613
1497
  ```typescript
@@ -1759,20 +1643,15 @@ interface TraceTree
1759
1643
 
1760
1644
  ## data
1761
1645
 
1762
- ### `StatementTimeoutError`
1763
-
1764
- A statement was cancelled by Postgres for running past the statement timeout a {@link withTimeouts} call set. `cause` is the driver error.
1765
-
1766
- ```typescript
1767
- class StatementTimeoutError extends Error constructor(readonly statementTimeoutMs: number, cause: unknown)
1768
- ```
1769
-
1770
- ### `LockTimeoutError`
1646
+ ### `tap`
1771
1647
 
1772
- A statement gave up waiting for a lock past the lock timeout a {@link withTimeouts} call set. `cause` is the driver error.
1648
+ Opens a standalone managed connection outside the manifold's pools — the
1649
+ sanctioned door for contexts where `pipe()` structurally cannot resolve:
1650
+ `worker_threads`, detached monitors, one-off scripts. Callers own the
1651
+ lifecycle: always `await conn.close()`.
1773
1652
 
1774
1653
  ```typescript
1775
- class LockTimeoutError extends Error constructor(readonly lockTimeoutMs: number, cause: unknown)
1654
+ function tap(url: string, options?: ConnectOptions): ManagedConnection
1776
1655
  ```
1777
1656
 
1778
1657
  ## request
@@ -1 +1 @@
1
- {"version":3,"file":"scoped-db.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/scoped-db.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAA;AAiKhD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAsB9C;AAmBD,wBAAgB,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,CAgE3D"}
1
+ {"version":3,"file":"scoped-db.d.ts","sourceRoot":"","sources":["../../../src/auth/tenant/scoped-db.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,EAAE,EAAE,MAAM,wBAAwB,CAAA;AAichD,wBAAgB,mBAAmB,CAAC,EAAE,EAAE,EAAE,GAAG,EAAE,CAE9C;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,EAAE,CAE3D"}