@owlmeans/postgres-resource 0.1.18-rc.3 → 0.1.18-rc.31
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/README.md +185 -101
- package/agent-meta/manifest.json +2 -2
- package/agent-meta/skills/postgres-resource/SKILL.md +82 -18
- package/build/consts.d.ts +8 -1
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +8 -1
- package/build/consts.js.map +1 -1
- package/build/declarations.js +5 -5
- package/build/resource.d.ts +2 -2
- package/build/resource.d.ts.map +1 -1
- package/build/resource.js +95 -108
- package/build/resource.js.map +1 -1
- package/build/types.d.ts +27 -19
- package/build/types.d.ts.map +1 -1
- package/build/utils/criteria.d.ts +10 -6
- package/build/utils/criteria.d.ts.map +1 -1
- package/build/utils/criteria.js +13 -5
- package/build/utils/criteria.js.map +1 -1
- package/build/utils/diff.d.ts.map +1 -1
- package/build/utils/diff.js +31 -1
- package/build/utils/diff.js.map +1 -1
- package/build/utils/migrations.d.ts +2 -2
- package/build/utils/migrations.js +2 -2
- package/build/utils/name.d.ts +3 -3
- package/build/utils/name.js +3 -3
- package/build/utils/sql.d.ts +10 -2
- package/build/utils/sql.d.ts.map +1 -1
- package/build/utils/sql.js.map +1 -1
- package/build/utils/table.d.ts +4 -0
- package/build/utils/table.d.ts.map +1 -1
- package/build/utils/table.js +4 -0
- package/build/utils/table.js.map +1 -1
- package/package.json +7 -7
- package/src/consts.ts +8 -1
- package/src/declarations.ts +5 -5
- package/src/resource.ts +118 -135
- package/src/types.ts +29 -18
- package/src/utils/criteria.ts +25 -17
- package/src/utils/diff.ts +31 -1
- package/src/utils/migrations.ts +2 -2
- package/src/utils/name.ts +3 -3
- package/src/utils/sql.ts +0 -0
- package/src/utils/table.ts +5 -1
- package/tests/criteria.spec.ts +187 -0
- package/tests/diff.spec.ts +60 -0
- package/tests/name.spec.ts +3 -3
- package/tests/sql.spec.ts +4 -3
package/README.md
CHANGED
|
@@ -1,32 +1,48 @@
|
|
|
1
1
|
# @owlmeans/postgres-resource
|
|
2
2
|
|
|
3
|
-
PostgreSQL-backed `Resource<T
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
- `
|
|
9
|
-
- `
|
|
10
|
-
-
|
|
11
|
-
|
|
12
|
-
- A `pg:` keyword vocabulary inside that schema covers what JSON Schema can't express: column types and
|
|
13
|
-
lengths, foreign keys, composite indexes, checks, partial indexes
|
|
14
|
-
- Tables are created and reconciled automatically at `init()`; migrations run around that in `pre`/`post` stages
|
|
3
|
+
PostgreSQL-backed `Resource<T>`, and the default store for the records a product owns and queries:
|
|
4
|
+
users, orders, projects, invoices, settings, audit rows. The resource's AJV schema is the single
|
|
5
|
+
source of truth for the table, and the package creates and reconciles that table at boot. It also
|
|
6
|
+
runs code migrations around the reconciliation and resolves resource-alias placeholders in custom
|
|
7
|
+
SQL. Other stores fit other shapes:
|
|
8
|
+
- [`@owlmeans/mongo-resource`](../mongo-resource): documents whose shape varies per record and are read whole.
|
|
9
|
+
- [`@owlmeans/redis-resource`](../redis-resource): data defined by expiry, a lock, a counter or fan-out.
|
|
10
|
+
- [`@owlmeans/storage-resource`](../storage-resource): file bytes. The record describing the file stays here.
|
|
11
|
+
- [`@owlmeans/state`](../state): client-side records.
|
|
15
12
|
|
|
16
13
|
## Installation
|
|
17
14
|
|
|
18
15
|
```bash
|
|
19
|
-
bun add @owlmeans/postgres-resource @owlmeans/postgres pg
|
|
16
|
+
bun add @owlmeans/postgres-resource@^0.1.18-rc.30 @owlmeans/postgres@^0.1.18-rc.31 pg
|
|
20
17
|
```
|
|
21
18
|
|
|
22
19
|
`pg` and `ajv` are peer dependencies of this package. `@owlmeans/postgres` provides the connection
|
|
23
20
|
service this package resolves through.
|
|
24
21
|
|
|
22
|
+
## Concepts
|
|
23
|
+
|
|
24
|
+
- **The schema is the table**: columns, types, nullability, defaults, primary key, indexes and
|
|
25
|
+
foreign keys all derive from `resource.schema`. Nothing else writes DDL: no `pgTable`, no
|
|
26
|
+
`drizzle-kit`, no hand-written `CREATE TABLE`.
|
|
27
|
+
- **`pg:` overrides**: a keyword inside the schema for what JSON Schema cannot say. That covers
|
|
28
|
+
column type and length, `unique`, `references`, composite indexes, checks, `unmanaged` columns
|
|
29
|
+
and per-table `autoSync`.
|
|
30
|
+
- **Reconciliation** (`PgAutoSync`): at `init()` the resource introspects the live table, diffs it
|
|
31
|
+
against the compiled `TableSpec` and applies the DDL plan in one transaction under an advisory
|
|
32
|
+
lock. `Full` adds, retypes and drops. `Additive` only adds. `Off` emits no DDL.
|
|
33
|
+
- **Migrations**: code bodies registered with `migration(name, apply, stage?)`. `Pre` runs before
|
|
34
|
+
reconciliation and can rescue data it would drop. `Post` runs after it and can use new columns.
|
|
35
|
+
Each runs once, in its own transaction, recorded in `_owlmeans_migrations`.
|
|
36
|
+
- **Placeholders**: `{{}}`, `{{alias}}`, `{{alias.property}}`, `{{#alias}}` and `{{$}}` resolve
|
|
37
|
+
*identifiers only* in custom SQL. Values always travel as `$1..$n` parameters.
|
|
38
|
+
- **Paged by default**: `list(where)` returns at most `DEFAULT_PAGE_SIZE` (100) rows, with the
|
|
39
|
+
primary key appended to every sort as a tiebreak.
|
|
40
|
+
|
|
25
41
|
## Usage
|
|
26
42
|
|
|
27
|
-
Define a resource
|
|
43
|
+
### Define and register a resource
|
|
28
44
|
|
|
29
|
-
```
|
|
45
|
+
```ts
|
|
30
46
|
import { makePostgresResource } from '@owlmeans/postgres-resource'
|
|
31
47
|
import type { PostgresResource } from '@owlmeans/postgres-resource'
|
|
32
48
|
import type { ResourceMaker } from '@owlmeans/resource'
|
|
@@ -35,45 +51,60 @@ export interface ProjectResource extends PostgresResource<ProjectRecord> {}
|
|
|
35
51
|
|
|
36
52
|
export const makeProjectResource: ResourceMaker<ProjectRecord, ProjectResource> = (dbAlias, serviceAlias) => {
|
|
37
53
|
const resource = makePostgresResource<ProjectRecord, ProjectResource>(
|
|
38
|
-
RES_PROJECT, dbAlias, serviceAlias
|
|
54
|
+
RES_PROJECT, dbAlias, serviceAlias
|
|
39
55
|
)
|
|
40
56
|
resource.schema = ProjectSchema
|
|
41
57
|
resource.index('idx_project_entity', { columns: ['entityId'] })
|
|
42
58
|
|
|
43
59
|
return resource
|
|
44
60
|
}
|
|
45
|
-
```
|
|
46
61
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```typescript
|
|
62
|
+
// in the server context factory, next to appendPostgres(context)
|
|
50
63
|
context.registerResource(makeProjectResource())
|
|
51
64
|
```
|
|
52
65
|
|
|
53
|
-
|
|
66
|
+
### Read and write from a handler
|
|
54
67
|
|
|
55
|
-
```
|
|
68
|
+
```ts
|
|
56
69
|
const projects = context.resource<ProjectResource>(RES_PROJECT)
|
|
57
|
-
const record = await projects.create({ entityId, alias, title })
|
|
58
|
-
|
|
70
|
+
const record = await projects.create({ entityId, alias, title }) // the database assigns the id
|
|
71
|
+
|
|
72
|
+
const one = await projects.load({ entityId, alias }) // null when absent
|
|
73
|
+
const { items, total } = await projects.list(
|
|
74
|
+
{ entityId, status: ['draft', 'active'] }, // an array means "any of these"
|
|
75
|
+
{ page: 0, size: 20, sort: [{ field: 'createdAt', order: 'desc' }] }
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
await projects.patch({ id: record.id, status: 'active' }) // merge
|
|
79
|
+
await projects.update({ ...record, title: 'Renamed' }) // replace the whole row
|
|
59
80
|
```
|
|
60
81
|
|
|
61
|
-
|
|
82
|
+
`size` defaults to `DEFAULT_PAGE_SIZE` (100); `list(where, { size: 0 })` lifts the limit. `total`
|
|
83
|
+
always describes the whole match, independently of the page. `entityId` is the organization's
|
|
84
|
+
stable record id from `requireEntityKey(req)`, never a value read off the token.
|
|
85
|
+
|
|
86
|
+
### Schema to table
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { DateSchema } from '@owlmeans/auth'
|
|
90
|
+
import type { JSONSchemaType } from 'ajv'
|
|
62
91
|
|
|
63
|
-
|
|
64
|
-
const ProjectSchema: JSONSchemaType<ProjectRecord> = {
|
|
92
|
+
const InvoiceSchema: JSONSchemaType<InvoiceRecord> = {
|
|
65
93
|
type: 'object',
|
|
66
94
|
properties: {
|
|
67
|
-
id: { type: 'string', format: 'uuid' },
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
95
|
+
id: { type: 'string', format: 'uuid' }, // primary key; never in required
|
|
96
|
+
entityId: { type: 'string' },
|
|
97
|
+
number: { type: 'string', pg: { type: 'varchar', length: 32, unique: true } },
|
|
98
|
+
customerId: { type: 'string', pg: { references: { resource: 'customers', onDelete: 'cascade' } } },
|
|
99
|
+
status: { type: 'string', enum: ['draft', 'sent', 'paid'] }, // text + CHECK
|
|
100
|
+
tags: { type: 'array', items: { type: 'string' } }, // text[]
|
|
101
|
+
lines: { type: 'array', items: { type: 'object' } }, // jsonb
|
|
102
|
+
note: { type: 'string', nullable: true },
|
|
103
|
+
createdAt: DateSchema // timestamptz
|
|
72
104
|
},
|
|
73
|
-
required: ['
|
|
105
|
+
required: ['entityId', 'number', 'customerId', 'status'],
|
|
74
106
|
pg: {
|
|
75
|
-
|
|
76
|
-
indexes: [{ name: 'idx_owner_created', columns: ['ownerId', 'createdAt'] }]
|
|
107
|
+
indexes: [{ name: 'idx_invoice_entity_created', columns: ['entityId', 'createdAt'] }]
|
|
77
108
|
}
|
|
78
109
|
}
|
|
79
110
|
```
|
|
@@ -84,51 +115,40 @@ const ProjectSchema: JSONSchemaType<ProjectRecord> = {
|
|
|
84
115
|
| `string` + `format: 'uuid'` | `uuid` |
|
|
85
116
|
| `DateSchema` (`{type:'object', format:'date-time'}`) | `timestamptz` |
|
|
86
117
|
| `integer` / `number` / `boolean` | `integer` / `double precision` / `boolean` |
|
|
87
|
-
| `array
|
|
118
|
+
| `array` of plain `string`/`integer`/`number`/`boolean` items | native `<scalar>[]`, e.g. `text[]` |
|
|
119
|
+
| any other `array`, nested `object` | `jsonb` |
|
|
88
120
|
| string `enum` | `text` + `CHECK` |
|
|
89
121
|
| `nullable: true` | nullable column |
|
|
90
122
|
| in `required[]` | `NOT NULL` |
|
|
91
123
|
| `secure: true` | ciphertext column, `lock`/`unlock` aware |
|
|
92
124
|
| `id` property | primary key, `gen_random_uuid()::text` default |
|
|
93
125
|
|
|
94
|
-
AJV in strict mode rejects unknown keywords
|
|
126
|
+
AJV in strict mode rejects unknown keywords, so register `pgKeyword` to allow `pg:`:
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
import { pgKeyword } from '@owlmeans/postgres-resource'
|
|
95
130
|
|
|
96
|
-
```typescript
|
|
97
131
|
ajv.addKeyword(pgKeyword)
|
|
98
132
|
```
|
|
99
133
|
|
|
100
|
-
###
|
|
101
|
-
|
|
102
|
-
At `init()` the resource introspects `information_schema` / `pg_catalog`, diffs against the compiled
|
|
103
|
-
spec, and applies the DDL plan in one transaction under a `pg_advisory_xact_lock`, so concurrent
|
|
104
|
-
replicas never race. Policy comes from `DbConfig.meta.autoSync`:
|
|
105
|
-
|
|
106
|
-
| `PgAutoSync` | Behaviour |
|
|
107
|
-
|---|---|
|
|
108
|
-
| `Full` (default) | add / retype / backfill / drop columns, reconcile indexes and constraints |
|
|
109
|
-
| `Additive` | add only — never retypes, never drops. The adoption path for a pre-existing table |
|
|
110
|
-
| `Off` | create the table if absent, otherwise leave it alone |
|
|
134
|
+
### Migrations
|
|
111
135
|
|
|
112
|
-
|
|
113
|
-
|
|
136
|
+
```ts
|
|
137
|
+
import { MigrationStage } from '@owlmeans/resource'
|
|
114
138
|
|
|
115
|
-
|
|
139
|
+
resource.migration('0001-rescue-legacy-slug', async tx => {
|
|
140
|
+
await tx.execute(`UPDATE {{}} SET slug = legacy_slug WHERE slug IS NULL`)
|
|
141
|
+
}, MigrationStage.Pre)
|
|
116
142
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
await tx.execute(`UPDATE {{}} SET slug = lower(title) WHERE slug IS NULL`)
|
|
143
|
+
resource.migration('0002-backfill-status', async tx => {
|
|
144
|
+
await tx.execute(`UPDATE {{}} SET status = $1 WHERE status IS NULL`, ['draft'])
|
|
120
145
|
}, MigrationStage.Post)
|
|
121
146
|
```
|
|
122
147
|
|
|
123
|
-
Stages bracket the structure sync: `Pre` runs **before** the table is reshaped (so a migration can
|
|
124
|
-
rescue data reconciliation would drop), `Post` **after** (so it can use the new columns). Applied
|
|
125
|
-
once, in registration order, each in its own transaction, recorded in `_owlmeans_migrations`.
|
|
126
148
|
Migrations declared on a table this package just created are **baselined**, not executed. An edited
|
|
127
|
-
body raises `MigrationConflict`; a failing one raises `MigrationError` and aborts `init()`.
|
|
149
|
+
applied body raises `MigrationConflict`; a failing one raises `MigrationError` and aborts `init()`.
|
|
128
150
|
|
|
129
|
-
### Custom SQL
|
|
130
|
-
|
|
131
|
-
Placeholders resolve **identifiers only** — values stay in `params` and are bound as `$1..$n`:
|
|
151
|
+
### Custom SQL and transactions
|
|
132
152
|
|
|
133
153
|
| Placeholder | Resolves to |
|
|
134
154
|
|---|---|
|
|
@@ -138,11 +158,22 @@ Placeholders resolve **identifiers only** — values stay in `params` and are bo
|
|
|
138
158
|
| `{{#alias}}` | the bare quoted table name (for `ON CONFLICT ON CONSTRAINT`) |
|
|
139
159
|
| `{{$}}` | the owning resource's quoted schema |
|
|
140
160
|
|
|
141
|
-
```
|
|
142
|
-
const
|
|
143
|
-
`SELECT
|
|
144
|
-
|
|
161
|
+
```ts
|
|
162
|
+
const overdue = await invoices.select(
|
|
163
|
+
`SELECT {{}}.* FROM {{}} JOIN {{customers}} ON {{customers.id}} = {{self.customerId}}
|
|
164
|
+
WHERE {{self.entityId}} = $1 AND {{self.status}} = $2 AND {{customers.active}} = $3`,
|
|
165
|
+
[entityId, 'sent', true]
|
|
145
166
|
)
|
|
167
|
+
|
|
168
|
+
const settled = await invoices.transaction(async tx => {
|
|
169
|
+
const count = await tx.execute(
|
|
170
|
+
`UPDATE {{}} SET status = $1 WHERE {{self.customerId}} = $2 AND {{self.status}} = $3`,
|
|
171
|
+
['paid', customerId, 'sent']
|
|
172
|
+
)
|
|
173
|
+
await tx.execute(`UPDATE {{payments}} SET settled = true WHERE {{payments.customerId}} = $1`, [customerId])
|
|
174
|
+
|
|
175
|
+
return count
|
|
176
|
+
})
|
|
146
177
|
```
|
|
147
178
|
|
|
148
179
|
An unknown alias or property raises `PostgresPlaceholderError` at parse time rather than being
|
|
@@ -150,56 +181,109 @@ substituted blindly.
|
|
|
150
181
|
|
|
151
182
|
## API
|
|
152
183
|
|
|
153
|
-
### `makePostgresResource<R, T>(alias, dbAlias?, serviceAlias?,
|
|
184
|
+
### `makePostgresResource<R, T>(alias, dbAlias?, serviceAlias?, tableName?): T`
|
|
154
185
|
|
|
155
186
|
Creates a Postgres resource. `dbAlias` and `serviceAlias` default to `DEFAULT_DB_ALIAS`
|
|
156
187
|
(`'postgres'`). `tableName` overrides the physical table name, which otherwise derives from the alias.
|
|
188
|
+
`schema`, `index()` and `migration()` land in a module-scope declaration keyed by alias.
|
|
157
189
|
|
|
158
190
|
### `PostgresResource<T>`
|
|
159
191
|
|
|
160
|
-
Extends `Resource<T>` with:
|
|
192
|
+
Extends `Resource<T>`, `LockableResource<T>` and `MigratableResource<PostgresTx, PostgresResource<T>>` with:
|
|
161
193
|
|
|
162
|
-
- `schema?: AnySchema
|
|
163
|
-
- `table: TableSpec` / `entity: PgRuntimeTable
|
|
194
|
+
- `schema?: AnySchema`, the source of truth for the table structure
|
|
195
|
+
- `table: TableSpec` / `entity: PgRuntimeTable`, the compiled spec and the runtime Drizzle table (after `init()`)
|
|
164
196
|
- `db(): Promise<PostgresDb>` / `client(): Promise<Pool>`
|
|
165
|
-
- `index(name, spec
|
|
166
|
-
- `query` / `queryOne` / `execute
|
|
167
|
-
- `select` / `selectOne
|
|
168
|
-
- `ref(alias?): string
|
|
169
|
-
- `transaction(fn)`
|
|
170
|
-
- `insert`
|
|
171
|
-
- `lock(record, fields?)` / `unlock(record, fields?)
|
|
197
|
+
- `index(name, spec: PgIndexSpec)` / `migration(name, apply, stage?)`, chainable declarations
|
|
198
|
+
- `query(text, params?)` / `queryOne(text, params?)` / `execute(text, params?)`, which return raw rows or the affected count
|
|
199
|
+
- `select(text, params?)` / `selectOne(text, params?)`, which return rows marshalled back into `T`
|
|
200
|
+
- `ref(alias?): string`, the fully qualified identifier of this or another resource
|
|
201
|
+
- `transaction(fn)`, which runs `fn` with a `PostgresTx` (`client`, `query`/`queryOne`/`execute`, `ref`)
|
|
202
|
+
- `insert(record)` (a caller-supplied id), `upsert(record, conflict?)`, `patch(record, opts?)` (merge)
|
|
203
|
+
- `lock(record, fields?)` / `unlock(record, fields?)`, which encrypt/decrypt `secure` fields
|
|
172
204
|
- `getDefaults(): Partial<T>`
|
|
173
205
|
|
|
174
206
|
### `Resource<T>` methods (all implemented)
|
|
175
207
|
|
|
176
|
-
`get`, `load`, `create`, `update`, `save`, `delete`, `
|
|
177
|
-
|
|
178
|
-
`create` refuses a caller-supplied id (use `insert`); `update` replaces the whole record (use `patch`
|
|
179
|
-
to merge); `pick` deletes the record it returns, atomically.
|
|
180
|
-
|
|
181
|
-
### Errors
|
|
182
|
-
|
|
183
|
-
`PostgresError` and its subclasses — `PostgresSyncError`, `PostgresCastRequired`,
|
|
184
|
-
`PostgresConstraintError`, `PostgresForeignKeyError`, `PostgresCheckError`, `PostgresDeadlockError`,
|
|
185
|
-
`PostgresPlaceholderError`, `PostgresConnectionError`, `PostgresBootstrapError`. Driver errors are
|
|
186
|
-
translated by `pgErrorToResourceError`, which unwraps Drizzle's `DrizzleQueryError` to reach the
|
|
187
|
-
`pg` error underneath and preserves the raw `code`/`detail`/`hint`/`severity`. Unique violations
|
|
188
|
-
surface as `RecordExists` and not-null violations as `MisshapedRecord`.
|
|
208
|
+
`get`, `load`, `list`, `count`, `create`, `update`, `save`, `delete`, `take`, `purge`
|
|
189
209
|
|
|
190
|
-
|
|
210
|
+
`get` and `load` take either an id or a `Criteria<T>` (with an optional `sort`), so reading one
|
|
211
|
+
record by several fields is a single statement. `create` refuses a caller-supplied id (use
|
|
212
|
+
`insert`); `update` replaces the whole record (use `patch` to merge); `take` deletes the record it
|
|
213
|
+
returns, atomically, and raises when there is nothing to take where `delete` answers `null`; `purge`
|
|
214
|
+
refuses an empty criteria rather than emptying the table. `ttl` raises `UnsupportedArgumentError`,
|
|
215
|
+
since Postgres has no row expiry.
|
|
191
216
|
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
- `PgAutoSync`, `PgIndexMethod`, `PgReferentialAction`, `PgErrorCode`
|
|
217
|
+
Criteria follow the shared vocabulary from [`@owlmeans/resource`](../resource). A key naming no
|
|
218
|
+
column raises `UnsupportedArgumentError`. A dotted key reaches into a `jsonb` column, a criteria
|
|
219
|
+
object against a `jsonb` column becomes containment (`@>`), and `$contains`/`$contained`/`$overlaps`
|
|
220
|
+
are `@>`/`<@`/`&&` on array columns.
|
|
197
221
|
|
|
198
|
-
|
|
222
|
+
### Errors
|
|
199
223
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
224
|
+
`PostgresError` and its subclasses are `PostgresSyncError`, `PostgresCastRequired`,
|
|
225
|
+
`PostgresConstraintError`, `PostgresForeignKeyError`, `PostgresCheckError`, `PostgresDeadlockError`
|
|
226
|
+
(`retryable`), `PostgresPlaceholderError`, `PostgresConnectionError` and `PostgresBootstrapError`.
|
|
227
|
+
Driver errors are translated by `pgErrorToResourceError`, which unwraps Drizzle's
|
|
228
|
+
`DrizzleQueryError` to reach the `pg` error underneath and preserves the raw
|
|
229
|
+
`code`/`detail`/`hint`/`severity` in the message. Unique violations surface as `RecordExists` and
|
|
230
|
+
not-null violations as `MisshapedRecord`.
|
|
231
|
+
|
|
232
|
+
### Exports
|
|
233
|
+
|
|
234
|
+
| Symbol | Kind | Purpose |
|
|
235
|
+
|---|---|---|
|
|
236
|
+
| `makePostgresResource` | function | The resource factory |
|
|
237
|
+
| `PostgresResource<T>` | type | The resource interface described above |
|
|
238
|
+
| `PostgresDbService`, `PostgresDb`, `PostgresTx`, `PostgresMeta` | type | Connection service contract (implemented by `@owlmeans/postgres`), db handle, transaction façade, `DbConfig.meta` shape |
|
|
239
|
+
| `TableSpec`, `ColumnSpec`, `ColumnJsonType`, `PgRuntimeTable` | type | The compiled table description |
|
|
240
|
+
| `PgPropertyOverride`, `PgRootOverride`, `PgIndexSpec`, `PgUniqueSpec`, `PgCheckSpec`, `PgReferenceSpec` | type | The `pg:` vocabulary and `index()` spec |
|
|
241
|
+
| `LiveTable`, `LiveColumn`, `LiveIndex`, `LiveConstraint`, `DdlPlan`, `DdlStatement`, `DdlKind` | type | Introspection results and the reconciliation plan |
|
|
242
|
+
| `pgKeyword` | const | `{ keyword: 'pg', valid: true }` for AJV strict mode |
|
|
243
|
+
| `schemaToTableSpec`, `toFormatType` | function | The schema compiler |
|
|
244
|
+
| `criteriaToSql`, `sortToSql` | function | `Criteria<T>` to a WHERE clause and `Sort<T>` to ORDER BY over the same table |
|
|
245
|
+
| `refOf`, `resolvePlaceholders`, `resetPlaceholderCache`, `PlaceholderContext` | function / type | `{{alias}}` resolution, identifiers only |
|
|
246
|
+
| `pgTableName`, `pgIdentifier`, `assertSqlIdentifier`, `quoteIdent`, `quoteLiteral`, `qualify`, `advisoryKey` | function | Identifier helpers |
|
|
247
|
+
| `rowToRecord`, `resultToRecord`, `recordToValues`, `recordToFullValues`, `specToTable` | function | Row and record marshalling, Drizzle table construction |
|
|
248
|
+
| `initializeTable`, `applyForeignKeys`, `TableInit` | function / type | The `init()` lifecycle |
|
|
249
|
+
| `introspectTable`, `countNonNull`, `planSync`, `planForeignKeys`, `applyPlan`, `ensureSchema`, `acquireLock`, `releaseLock` | function | Reconciliation machinery |
|
|
250
|
+
| `makeTx`, `makeMigrationStore` | function | The migration façade and ledger |
|
|
251
|
+
| `getDeclaration`, `resetDeclarations`, `PostgresDeclaration` | function / type | Module-scope declarations per alias; `resetDeclarations` is the testing seam |
|
|
252
|
+
| `getSchemaSecureFeilds` | function | `secure: true` properties used by `lock`/`unlock` |
|
|
253
|
+
| `pgErrorToResourceError`, `describePgError`, `PostgresError` family | function / class | Driver error translation |
|
|
254
|
+
| `DEFAULT_DB_ALIAS`, `DEFAULT_PAGE_SIZE`, `DEF_MIGRATIONS_TABLE` | const | `'postgres'`, `100`, `'_owlmeans_migrations'` |
|
|
255
|
+
| `PG_KEYWORD`, `PG_MAX_IDENTIFIER`, `ID_FIELD`, `DEF_SQL_TYPE`, `DEF_JSON_TYPE`, `DEF_ID_DEFAULT`, `STRING_RETURNING_TYPES` | const | Compiler constants |
|
|
256
|
+
| `PgAutoSync`, `PgIndexMethod`, `PgReferentialAction`, `PgErrorCode`, `PgTypeOid` | enum | Reconciliation policy, index methods, FK actions, driver codes, type OIDs |
|
|
257
|
+
|
|
258
|
+
## Common pitfalls
|
|
259
|
+
|
|
260
|
+
- **A second owner of the DDL.** Drizzle table definitions, `drizzle-kit` or hand-written
|
|
261
|
+
`CREATE TABLE` compete with reconciliation, which drops what the other owner added.
|
|
262
|
+
- **`Full` sync on an adopted table drops undeclared columns.** Boot once with `Additive`, confirm
|
|
263
|
+
the plan comes out empty, then switch to `Full`. Or list the columns in `pg.unmanaged`.
|
|
264
|
+
- **`autoSync: 'off'` never creates the table.** The first query then dies with `42P01`.
|
|
265
|
+
- **Listing `id` in `required`**, or generating ids in application code. The database assigns them,
|
|
266
|
+
and `create` refuses a supplied id; use `insert` when you really have one.
|
|
267
|
+
- **Interpolating values into SQL.** Placeholders are for identifiers. Values go in `params`.
|
|
268
|
+
- **Aliasing a table that a placeholder still names.** `{{self.x}}` expands to the qualified
|
|
269
|
+
`"schema"."table"."x"`, which Postgres rejects next to `FROM {{}} p`.
|
|
270
|
+
- **Relying on registration order for `{{alias}}`.** It reads the other resource's *initialized*
|
|
271
|
+
table spec. Foreign keys to a later resource are deferred by the `appendPostgres` middleware, so
|
|
272
|
+
do not reorder registrations.
|
|
273
|
+
- **The same index declared twice under different names** (schema root, property override and
|
|
274
|
+
`index()`). Duplicates are collapsed only by name.
|
|
275
|
+
- **Expecting `update` to merge.** Use `patch`.
|
|
276
|
+
- **Reading "everything" with `list(where)`.** It stops at 100. Use `{ size: 0 }`, or page with a
|
|
277
|
+
unique tiebreak.
|
|
278
|
+
- **A migration body closed over a loop variable.** Its checksum fingerprints the wrapper and drifts.
|
|
279
|
+
|
|
280
|
+
## Related packages
|
|
281
|
+
|
|
282
|
+
- [`@owlmeans/postgres`](../postgres): the connection service required by this package
|
|
283
|
+
- [`@owlmeans/resource`](../resource): `Resource<T>`, `ResourceRecord`, criteria, migrations, error family
|
|
284
|
+
- [`@owlmeans/mongo-resource`](../mongo-resource): the MongoDB counterpart
|
|
285
|
+
- [`@owlmeans/redis-resource`](../redis-resource): caches, locks and expiring records beside the table
|
|
286
|
+
- [`@owlmeans/queue`](../queue): long imports and batch writes that should not run inside a request
|
|
203
287
|
|
|
204
288
|
<!-- owlmeans:agent-guidance:start -->
|
|
205
289
|
## Agent guidance
|
|
@@ -209,7 +293,7 @@ This package ships embedded agent skills under `agent-meta/`. After installing y
|
|
|
209
293
|
your project's skill store (`.agents/skills/`):
|
|
210
294
|
|
|
211
295
|
```sh
|
|
212
|
-
npx @owlmeans/agent-skills
|
|
296
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.33
|
|
213
297
|
```
|
|
214
298
|
|
|
215
299
|
The embedded files are version-matched to this package release. Do not edit them
|
package/agent-meta/manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 2,
|
|
3
3
|
"package": "@owlmeans/postgres-resource",
|
|
4
|
-
"version": "0.1.18-rc.
|
|
5
|
-
"generatedAt": "2026-
|
|
4
|
+
"version": "0.1.18-rc.31",
|
|
5
|
+
"generatedAt": "2026-09-22T17:37:10.918Z",
|
|
6
6
|
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
7
|
"entries": [
|
|
8
8
|
{
|
|
@@ -8,7 +8,7 @@ user-invocable: false
|
|
|
8
8
|
# @owlmeans/postgres-resource
|
|
9
9
|
|
|
10
10
|
**Layer:** Infra
|
|
11
|
-
**Install:** `"@owlmeans/postgres-resource": "^0.1.18-rc.
|
|
11
|
+
**Install:** `"@owlmeans/postgres-resource": "^0.1.18-rc.31"` in `dependencies` (peers `pg`, `ajv`)
|
|
12
12
|
|
|
13
13
|
The Postgres counterpart of [[mongo-resource]]. The difference that governs everything else: a
|
|
14
14
|
Mongo collection has no structure, a Postgres table does — so **the resource layer owns the DDL**
|
|
@@ -18,15 +18,18 @@ and derives it from the resource's AJV schema.
|
|
|
18
18
|
|
|
19
19
|
| Export | Description |
|
|
20
20
|
|--------|-------------|
|
|
21
|
-
| `makePostgresResource<R, T>(alias, dbAlias?, serviceAlias?,
|
|
22
|
-
| `PostgresResource<T>` | `Resource<T>` + `table`/`entity`, custom SQL
|
|
23
|
-
| `PostgresDbService`, `PostgresDb`, `PostgresTx` | Service contract implemented by `@owlmeans/postgres`; the db handle `{ drizzle, pool, schema, database }`; the transaction façade. |
|
|
21
|
+
| `makePostgresResource<R, T>(alias, dbAlias?, serviceAlias?, tableName?)` | The resource factory. Aliases default to `DEFAULT_DB_ALIAS` (`'postgres'`); `tableName` overrides the physical table (else the sanitized alias). |
|
|
22
|
+
| `PostgresResource<T>` | `Resource<T>` + `table`/`entity`, `db()`/`client()`, `index`, `ref`, `getDefaults`, custom SQL (`query`/`queryOne`/`execute`/`select`/`selectOne`), `transaction`, `insert`/`upsert`/`patch`, `lock`/`unlock`, `migration`/`migrations`. |
|
|
23
|
+
| `PostgresDbService`, `PostgresDb`, `PostgresTx` | Service contract implemented by `@owlmeans/postgres`; the db handle `{ drizzle, pool, schema, database }`; the transaction façade (`client`, `query`/`queryOne`/`execute`, `ref`). |
|
|
24
|
+
| `PostgresMeta` | The `DbConfig.meta` shape this package reads — `database`, `autoSync`, `url`, and the pool/probe knobs. |
|
|
24
25
|
| `TableSpec`, `ColumnSpec`, `PgPropertyOverride`, `PgRootOverride`, `DdlPlan` | The compiled table description and the `pg:` vocabulary types. |
|
|
26
|
+
| `PgIndexSpec`, `PgUniqueSpec`, `PgCheckSpec`, `PgReferenceSpec` | What `resource.index()` and the `pg:` overrides are written with. |
|
|
27
|
+
| `criteriaToSql`, `sortToSql` | `Criteria<T>` → a WHERE clause and `Sort<T>` → the ORDER BY, for code that builds its own statement over the same table. |
|
|
25
28
|
| `pgKeyword` | `{ keyword: 'pg', valid: true }` — register it when running AJV in strict mode. |
|
|
26
29
|
| `schemaToTableSpec`, `pgTableName`, `pgIdentifier`, `quoteIdent`, `qualify`, `advisoryKey` | The compiler and identifier helpers. |
|
|
27
30
|
| `refOf`, `resolvePlaceholders` | `{{alias}}` resolution — identifiers only. |
|
|
28
31
|
| `PostgresError` family, `pgErrorToResourceError`, `describePgError` | Driver-error translation. |
|
|
29
|
-
| `getDeclaration`, `resetDeclarations` | Module-scope index/migration declarations, keyed by alias. |
|
|
32
|
+
| `getDeclaration`, `resetDeclarations` | Module-scope schema/index/migration declarations, keyed by alias. |
|
|
30
33
|
| `DEFAULT_DB_ALIAS`, `DEFAULT_PAGE_SIZE`, `DEF_MIGRATIONS_TABLE`, `PgAutoSync`, `PgIndexMethod`, `PgReferentialAction`, `PgErrorCode` | Constants. |
|
|
31
34
|
|
|
32
35
|
## The schema is the table — never write DDL
|
|
@@ -34,7 +37,7 @@ and derives it from the resource's AJV schema.
|
|
|
34
37
|
```typescript
|
|
35
38
|
export const makeProjectResource: ResourceMaker<ProjectRecord, ProjectResource> = (dbAlias, serviceAlias) => {
|
|
36
39
|
const resource = makePostgresResource<ProjectRecord, ProjectResource>(
|
|
37
|
-
RES_PROJECT, dbAlias, serviceAlias
|
|
40
|
+
RES_PROJECT, dbAlias, serviceAlias
|
|
38
41
|
)
|
|
39
42
|
resource.schema = ProjectSchema
|
|
40
43
|
resource.index('idx_project_entity', { columns: ['entityId'] })
|
|
@@ -48,12 +51,18 @@ Never call `pgTable`/`pgSchema`, never call `drizzle()`, never run `drizzle-kit`
|
|
|
48
51
|
`CREATE TABLE`. Two owners of the same DDL is the failure mode this package exists to remove:
|
|
49
52
|
reconciliation would drop what the other owner added.
|
|
50
53
|
|
|
54
|
+
`schema`, `index()` and `migration()` all land in the module-scope declaration for the alias rather
|
|
55
|
+
than on the resource object, so a maker that runs more than once for the same alias — a custom
|
|
56
|
+
maker wrapping the built-in one, a spec calling it again — reads and extends the same declaration
|
|
57
|
+
instead of starting a fresh, emptier one.
|
|
58
|
+
|
|
51
59
|
| JSON Schema | Postgres |
|
|
52
60
|
|---|---|
|
|
53
61
|
| `string` · `string`+`format:'uuid'` | `text` · `uuid` |
|
|
54
62
|
| `DateSchema` (`{type:'object', format:'date-time'}`) | `timestamptz` |
|
|
55
63
|
| `integer` · `number` · `boolean` | `integer` · `double precision` · `boolean` |
|
|
56
|
-
| `array`,
|
|
64
|
+
| `array` of plain `string`/`integer`/`number`/`boolean` items (no `format`, no `enum`) | native `<scalar>[]`, e.g. `text[]` |
|
|
65
|
+
| any other `array` (objects, enums, formatted items, no `items`), nested `object` | `jsonb` |
|
|
57
66
|
| string `enum` | `text` + `CHECK` |
|
|
58
67
|
| `nullable: true` · in `required[]` | nullable · `NOT NULL` |
|
|
59
68
|
| `secure: true` | ciphertext column, `lock`/`unlock` aware |
|
|
@@ -90,24 +99,41 @@ declaration sites merge into one `TableSpec` — the schema root, a per-property
|
|
|
90
99
|
not an error. Two entries under one name would emit the same `CREATE INDEX` twice in a single DDL
|
|
91
100
|
transaction, and Postgres answers the second with `42P07`, rolling back the plan that created the
|
|
92
101
|
table: the resource then fails every boot with an error naming an index that does not exist.
|
|
93
|
-
|
|
102
|
+
The compiler collapses the duplicates itself, keeps the first declaration and warns on the console.
|
|
94
103
|
|
|
95
104
|
## Reconciliation is authoritative — `PgAutoSync`
|
|
96
105
|
|
|
97
|
-
At `init()`:
|
|
98
|
-
`
|
|
106
|
+
At `init()`: take a **session** advisory lock on the qualified table name → introspect → diff →
|
|
107
|
+
apply the whole `DdlPlan` in one transaction → release the lock. The lock is session level, not
|
|
108
|
+
`xact`, because it has to span the migrations as well as the DDL, and those run in transactions of
|
|
109
|
+
their own inside it — only the plan itself is one `BEGIN`/`COMMIT`. Concurrent replicas therefore
|
|
110
|
+
serialize on the whole initialization, not just on the DDL. `DbConfig.meta.autoSync`:
|
|
99
111
|
|
|
100
112
|
| Value | Behaviour |
|
|
101
113
|
|---|---|
|
|
102
114
|
| `Full` (default) | add / retype+backfill / drop columns, reconcile indexes and constraints |
|
|
103
115
|
| `Additive` | add only — never retypes, never drops |
|
|
104
|
-
| `Off` |
|
|
116
|
+
| `Off` | **no table DDL** — the structure is never diffed and no statement is emitted, so a missing table is never created and the first query dies with `42P01`. Only for a table something else already provisions |
|
|
117
|
+
|
|
118
|
+
The schema root override `pg: { autoSync }` is a boolean and wins per table: it turns reconciliation
|
|
119
|
+
back on for one table under `meta.autoSync: 'off'`, and off for one table under the other two modes.
|
|
120
|
+
`Full` versus `Additive` still comes from the config.
|
|
105
121
|
|
|
106
122
|
**`Full` DROPs columns the schema doesn't declare.** Adopting a table this package didn't create:
|
|
107
123
|
boot once with `Additive`, confirm the plan comes out empty, then flip to `Full`. Columns listed in
|
|
108
124
|
`pg.unmanaged` stay outside reconciliation's authority permanently. A cast Postgres cannot perform
|
|
109
125
|
raises `PostgresCastRequired` instead of truncating.
|
|
110
126
|
|
|
127
|
+
**A retype drops the column's default first and restores it after.** Postgres refuses
|
|
128
|
+
`ALTER COLUMN … TYPE` outright when the column carries a DEFAULT it cannot cast to the new type
|
|
129
|
+
(`42804 default for column "x" cannot be cast automatically`), and it refuses before reading a
|
|
130
|
+
single row, so `USING` never gets a chance to help. Since the plan is one transaction, that refusal
|
|
131
|
+
aborts the whole reconciliation and the resource then fails *every* boot with an error naming the
|
|
132
|
+
column it is trying to fix. The plan therefore emits `DROP DEFAULT` → `ALTER … TYPE … USING` →
|
|
133
|
+
`SET DEFAULT`, restoring the default from the spec rather than from what the column was carrying.
|
|
134
|
+
The `id` column is where this shows up in practice: it is created with a `gen_random_uuid()::text`
|
|
135
|
+
default, so any change to its declared type takes this path.
|
|
136
|
+
|
|
111
137
|
## Migrations bracket the sync
|
|
112
138
|
|
|
113
139
|
`migration`/`migrations()` implement the shared `MigratableResource` capability from
|
|
@@ -141,10 +167,16 @@ resource.migration('0001-rescue-legacy', async tx => {
|
|
|
141
167
|
|
|
142
168
|
```typescript
|
|
143
169
|
await projects.select(
|
|
144
|
-
`SELECT
|
|
170
|
+
`SELECT {{}}.* FROM {{}} JOIN {{users}} u ON u.id = {{self.ownerId}} WHERE u.active = $1`, [true]
|
|
145
171
|
)
|
|
146
172
|
```
|
|
147
173
|
|
|
174
|
+
**Do not alias a table a placeholder still names.** Every `{{…}}` expands to the *qualified*
|
|
175
|
+
`"schema"."table"` — `{{self.ownerId}}` to `"schema"."table"."ownerId"` — so aliasing the same table
|
|
176
|
+
as `p` in the `FROM` makes Postgres reject the expansion with `invalid reference to FROM-clause
|
|
177
|
+
entry`. Either write the qualified form throughout, as above, or alias the table and stop using
|
|
178
|
+
`{{self.…}}` for its columns.
|
|
179
|
+
|
|
148
180
|
Postgres cannot bind an identifier as a parameter — that is the entire reason this mechanism exists.
|
|
149
181
|
Values have no such excuse: they stay in `params` as `$1..$n`. An unknown alias or property raises
|
|
150
182
|
`PostgresPlaceholderError` at parse time.
|
|
@@ -158,13 +190,45 @@ middleware `appendPostgres` installs — use that rather than reordering registr
|
|
|
158
190
|
|
|
159
191
|
| Method | Semantics |
|
|
160
192
|
|---|---|
|
|
161
|
-
| `create` | refuses a caller-supplied id (`RecordExists`) — use `insert` |
|
|
193
|
+
| `create` | refuses a caller-supplied id (`RecordExists`) — use `insert`; rejects `opts.ttl` with `UnsupportedArgumentError` (mongo parity) |
|
|
162
194
|
| `update` | **replaces** the whole record — use `patch` to merge |
|
|
163
|
-
| `
|
|
164
|
-
| `
|
|
195
|
+
| `load(where, { sort })` / `get(where, { sort })` | one `SELECT … ORDER BY … LIMIT 1`, so "the newest matching row" is one statement rather than a list whose head is taken |
|
|
196
|
+
| `delete` / `take` | one `DELETE … RETURNING`: the row is handed back by the statement that removed it. `take` **deletes** and throws `UnknownRecordError` on a miss |
|
|
197
|
+
| `purge` | `DELETE … RETURNING` over the criteria; refuses an empty criteria object (`UnsupportedArgumentError('purge:no-criteria')`) rather than truncating the table |
|
|
198
|
+
| `count` | `count(*)` over the criteria, no rows carried back |
|
|
165
199
|
| `upsert` | `INSERT … ON CONFLICT DO UPDATE`, conflicting on the primary key by default |
|
|
166
200
|
| `select`/`selectOne` | custom SQL marshalled back into `T`; `query`/`queryOne` return raw rows |
|
|
167
201
|
|
|
202
|
+
## Paging
|
|
203
|
+
|
|
204
|
+
Postgres is **PAGED**: `DEFAULT_PAGE_SIZE` is `100`, so `list(where)` with no `size` returns the
|
|
205
|
+
first 100 rows — a table is unbounded, and an unpaged read is a production incident waiting for the
|
|
206
|
+
row count to grow. `total` is counted separately, so it describes the whole match rather than the
|
|
207
|
+
window, and `list(where, { size: 0 })` lifts the limit: the explicit, greppable way to read a whole
|
|
208
|
+
table.
|
|
209
|
+
|
|
210
|
+
`sort` becomes the `ORDER BY`, a bare field name ascending, and **the primary key is always
|
|
211
|
+
appended as a tiebreak**. Postgres has no implicit row order, so paginating on a non-unique sort
|
|
212
|
+
key silently duplicates and skips rows between pages — a difference from mongo that would surface
|
|
213
|
+
as a data bug rather than an error.
|
|
214
|
+
|
|
215
|
+
## Criteria against a table
|
|
216
|
+
|
|
217
|
+
`criteriaToSql` answers the shared vocabulary ([[resource]]) in SQL, so one criteria object selects
|
|
218
|
+
the same rows here as it does against a collection or in memory. What is specific to a table:
|
|
219
|
+
|
|
220
|
+
- **A key naming no column raises `UnsupportedArgumentError`.** A typo that silently widened a
|
|
221
|
+
query to the whole table is worth being loud about — the schemaless stores cannot detect one.
|
|
222
|
+
- **A dotted key reaches into a jsonb column** (`#>>`), and a criteria object against a jsonb
|
|
223
|
+
column becomes containment (`@>`). A dotted key over a non-jsonb column is refused, and `sort`
|
|
224
|
+
refuses dotted paths outright: ORDER BY names a column the caller actually declared.
|
|
225
|
+
- `$contains`/`$contained`/`$overlaps` are the array operators `@>`, `<@` and `&&`;
|
|
226
|
+
`$like`/`$ilike` are `LIKE`/`ILIKE`; `$exists: true` and `$null: false` are `IS NOT NULL`, their
|
|
227
|
+
negations `IS NULL`.
|
|
228
|
+
- **`{ $in: [null, …] }` is widened explicitly.** SQL `IN` never matches NULL, so a null in the
|
|
229
|
+
list would silently disappear; the condition becomes `IN (…) OR IS NULL` (and the `$nin` form
|
|
230
|
+
`NOT IN (…) AND IS NOT NULL`), which is what the other stores answer.
|
|
231
|
+
|
|
168
232
|
## Errors
|
|
169
233
|
|
|
170
234
|
Drizzle raises `DrizzleQueryError` and hangs the `pg` error off `cause`, so the driver `code` is not
|
|
@@ -175,9 +239,9 @@ translation — consumers classify retryable DDL races on `42P01`/`42703`.
|
|
|
175
239
|
|
|
176
240
|
## Config
|
|
177
241
|
|
|
178
|
-
`DbConfig.schema` is the Postgres **SCHEMA** (
|
|
179
|
-
from `meta.database
|
|
180
|
-
|
|
242
|
+
`DbConfig.schema` is the Postgres **SCHEMA** (the service's `name(alias?)` returns it as given); the
|
|
243
|
+
**DATABASE** comes from `meta.database`. Values starting with `/` are read as files by the existing
|
|
244
|
+
`fileConfigReader` middleware.
|
|
181
245
|
|
|
182
246
|
## Tests
|
|
183
247
|
|
package/build/consts.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export declare const DEFAULT_DB_ALIAS = "postgres";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Rows `list()` returns when the caller names no `size`.
|
|
4
|
+
*
|
|
5
|
+
* A relational table is unbounded, so an unpaged read is a production incident waiting for
|
|
6
|
+
* the row count to grow. Asking for everything stays possible — and greppable — as
|
|
7
|
+
* `list(where, { size: 0 })`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const DEFAULT_PAGE_SIZE = 100;
|
|
3
10
|
/**
|
|
4
11
|
* Table that records which code-registered migrations have already been applied.
|
|
5
12
|
* Lives in the same Postgres schema as the resources it tracks, so dropping the
|
package/build/consts.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB,aAAa,CAAA;AAE1C,eAAO,MAAM,iBAAiB,
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AACA,eAAO,MAAM,gBAAgB,aAAa,CAAA;AAE1C;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,MAAM,CAAA;AAEpC;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,yBAAyB,CAAA;AAE1D,oEAAoE;AACpE,eAAO,MAAM,UAAU,OAAO,CAAA;AAE9B,8FAA8F;AAC9F,eAAO,MAAM,iBAAiB,KAAK,CAAA;AAEnC,qFAAqF;AACrF,eAAO,MAAM,QAAQ,OAAO,CAAA;AAE5B,iFAAiF;AACjF,eAAO,MAAM,YAAY,SAAS,CAAA;AAElC,eAAO,MAAM,aAAa,UAAU,CAAA;AAEpC,sEAAsE;AACtE,eAAO,MAAM,cAAc,4BAA4B,CAAA;AAEvD;;;;;;GAMG;AACH,oBAAY,UAAU;IACpB,IAAI,SAAS;IACb,QAAQ,aAAa;IACrB,GAAG,QAAQ;CACZ;AAED,oBAAY,aAAa;IACvB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,GAAG,QAAQ;IACX,IAAI,SAAS;IACb,IAAI,SAAS;IACb,MAAM,WAAW;CAClB;AAED,oBAAY,mBAAmB;IAC7B,QAAQ,cAAc;IACtB,QAAQ,aAAa;IACrB,OAAO,YAAY;IACnB,OAAO,aAAa;IACpB,UAAU,gBAAgB;CAC3B;AAED;;;GAGG;AACH,oBAAY,WAAW;IACrB,eAAe,UAAU;IACzB,mBAAmB,UAAU;IAC7B,gBAAgB,UAAU;IAC1B,cAAc,UAAU;IACxB,cAAc,UAAU;IACxB,eAAe,UAAU;IACzB,cAAc,UAAU;IACxB,eAAe,UAAU;IACzB,eAAe,UAAU;IACzB,YAAY,UAAU;IACtB,gBAAgB,UAAU;IAC1B,yBAAyB,UAAU;IACnC,yBAAyB,UAAU;IACnC,sBAAsB,UAAU;IAChC,oBAAoB,UAAU;IAC9B,gBAAgB,UAAU;IAC1B,mBAAmB,UAAU;CAC9B;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,YAAI,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,CAAU,CAAA;AAEhG,uFAAuF;AACvF,oBAAY,SAAS;IACnB,IAAI,OAAO;IACX,SAAS,OAAO;IAChB,WAAW,OAAO;CACnB"}
|
package/build/consts.js
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export const DEFAULT_DB_ALIAS = 'postgres';
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Rows `list()` returns when the caller names no `size`.
|
|
4
|
+
*
|
|
5
|
+
* A relational table is unbounded, so an unpaged read is a production incident waiting for
|
|
6
|
+
* the row count to grow. Asking for everything stays possible — and greppable — as
|
|
7
|
+
* `list(where, { size: 0 })`.
|
|
8
|
+
*/
|
|
9
|
+
export const DEFAULT_PAGE_SIZE = 100;
|
|
3
10
|
/**
|
|
4
11
|
* Table that records which code-registered migrations have already been applied.
|
|
5
12
|
* Lives in the same Postgres schema as the resources it tracks, so dropping the
|