@owlmeans/planning-postgres 0.1.18-rc.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/README.md +88 -0
- package/agent-meta/manifest.json +16 -0
- package/agent-meta/skills/planning-postgres/SKILL.md +292 -0
- package/build/consts.d.ts +45 -0
- package/build/consts.d.ts.map +1 -0
- package/build/consts.js +45 -0
- package/build/consts.js.map +1 -0
- package/build/errors.d.ts +10 -0
- package/build/errors.d.ts.map +1 -0
- package/build/errors.js +13 -0
- package/build/errors.js.map +1 -0
- package/build/index.d.ts +9 -0
- package/build/index.d.ts.map +1 -0
- package/build/index.js +8 -0
- package/build/index.js.map +1 -0
- package/build/resource.d.ts +32 -0
- package/build/resource.d.ts.map +1 -0
- package/build/resource.js +94 -0
- package/build/resource.js.map +1 -0
- package/build/schemas.d.ts +20 -0
- package/build/schemas.d.ts.map +1 -0
- package/build/schemas.js +64 -0
- package/build/schemas.js.map +1 -0
- package/build/service.d.ts +16 -0
- package/build/service.d.ts.map +1 -0
- package/build/service.js +34 -0
- package/build/service.js.map +1 -0
- package/build/sql.d.ts +61 -0
- package/build/sql.d.ts.map +1 -0
- package/build/sql.js +90 -0
- package/build/sql.js.map +1 -0
- package/build/store/bus.d.ts +52 -0
- package/build/store/bus.d.ts.map +1 -0
- package/build/store/bus.js +167 -0
- package/build/store/bus.js.map +1 -0
- package/build/store/cards.d.ts +35 -0
- package/build/store/cards.d.ts.map +1 -0
- package/build/store/cards.js +112 -0
- package/build/store/cards.js.map +1 -0
- package/build/store/fold.d.ts +56 -0
- package/build/store/fold.d.ts.map +1 -0
- package/build/store/fold.js +453 -0
- package/build/store/fold.js.map +1 -0
- package/build/store/index.d.ts +22 -0
- package/build/store/index.d.ts.map +1 -0
- package/build/store/index.js +206 -0
- package/build/store/index.js.map +1 -0
- package/build/store/links.d.ts +18 -0
- package/build/store/links.d.ts.map +1 -0
- package/build/store/links.js +54 -0
- package/build/store/links.js.map +1 -0
- package/build/store/schemas.d.ts +32 -0
- package/build/store/schemas.d.ts.map +1 -0
- package/build/store/schemas.js +143 -0
- package/build/store/schemas.js.map +1 -0
- package/build/store/specs.d.ts +10 -0
- package/build/store/specs.d.ts.map +1 -0
- package/build/store/specs.js +47 -0
- package/build/store/specs.js.map +1 -0
- package/build/store/transitions.d.ts +44 -0
- package/build/store/transitions.d.ts.map +1 -0
- package/build/store/transitions.js +164 -0
- package/build/store/transitions.js.map +1 -0
- package/build/types.d.ts +72 -0
- package/build/types.d.ts.map +1 -0
- package/build/types.js +2 -0
- package/build/types.js.map +1 -0
- package/package.json +56 -0
- package/src/consts.ts +54 -0
- package/src/errors.ts +15 -0
- package/src/index.ts +9 -0
- package/src/resource.ts +133 -0
- package/src/schemas.ts +84 -0
- package/src/service.ts +45 -0
- package/src/sql.ts +135 -0
- package/src/store/bus.ts +217 -0
- package/src/store/cards.ts +153 -0
- package/src/store/fold.ts +560 -0
- package/src/store/index.ts +237 -0
- package/src/store/links.ts +77 -0
- package/src/store/schemas.ts +174 -0
- package/src/store/specs.ts +72 -0
- package/src/store/transitions.ts +221 -0
- package/src/types.ts +73 -0
- package/tests/bus.spec.ts +142 -0
- package/tests/conformance.spec.ts +37 -0
- package/tests/context.ts +143 -0
- package/tests/fold.spec.ts +213 -0
- package/tests/schema.spec.ts +89 -0
- package/tests/sql.spec.ts +50 -0
- package/tests/sync.spec.ts +61 -0
- package/tests/tsconfig.json +17 -0
- package/tsconfig.json +11 -0
package/README.md
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# @owlmeans/planning-postgres
|
|
2
|
+
|
|
3
|
+
A durable Postgres `PlanningStore` for `@owlmeans/server-planning`: projects, cards and
|
|
4
|
+
specifications in one table, the append-only transition log, typed links, and data-defined card
|
|
5
|
+
types and flows — four tables compiled from the planning record schemas by
|
|
6
|
+
`@owlmeans/postgres-resource`. A write folds inline, in one transaction per card under a Postgres
|
|
7
|
+
advisory lock; commits reach other processes over LISTEN/NOTIFY. Use it for a backend that keeps
|
|
8
|
+
its planning data in Postgres; a Mongo deployment uses its own store, and tests and single-process
|
|
9
|
+
tools the in-memory store of `@owlmeans/server-planning`.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
bun add @owlmeans/planning-postgres@^0.1.18-rc.0
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Peers: `pg`, `ajv`, `ajv-formats`.
|
|
18
|
+
|
|
19
|
+
## Concepts
|
|
20
|
+
|
|
21
|
+
- **Four resources, four tables** — `planning-card` (projects, cards and specifications, routed by
|
|
22
|
+
`kind`), `planning-transition` (the log), `planning-link` (relationships), `planning-schema`
|
|
23
|
+
(data-defined types and flows). Timestamps stay `text`, so ISO-8601 order is row order.
|
|
24
|
+
- **Inline fold** — `project()` folds at once, inside one transaction under
|
|
25
|
+
`pg_advisory_xact_lock`; the settled events are delivered to waiters and `after` hooks only
|
|
26
|
+
after it commits.
|
|
27
|
+
- **A gap is not a failure** — `nextSeq` and `append` are two round trips, so a fold waits for a
|
|
28
|
+
young gap and fills an old one with a failed `lost-allocation` placeholder.
|
|
29
|
+
- **Every waiter heals** — a status read of a pending row older than `healAfterMs` folds its card
|
|
30
|
+
in the background; `recover()` folds what nobody is folding.
|
|
31
|
+
- **Data-defined types and flows** — the store implements the schema port, so the planning facade
|
|
32
|
+
exposes `definitions`.
|
|
33
|
+
|
|
34
|
+
## Usage
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { appendPostgres } from '@owlmeans/postgres'
|
|
38
|
+
import { appendPostgresPlanning } from '@owlmeans/planning-postgres'
|
|
39
|
+
|
|
40
|
+
appendPostgres(context)
|
|
41
|
+
appendPostgresPlanning(context, { plugins: [libraryPlugin] }) // four resources + the service
|
|
42
|
+
|
|
43
|
+
const planning = context.planning().for({ entityId, profileId })
|
|
44
|
+
const receipt = await planning.execute({
|
|
45
|
+
card: { kind: 'project', type: 'library:branch', title: 'Riverside branch' },
|
|
46
|
+
action: 'create',
|
|
47
|
+
}, { wait: true })
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
A generated target registers each piece from its own file instead — `resources/planning/card.ts`
|
|
51
|
+
exporting `makeResource = makePlanningCardPostgres` (and `transition.ts`, `link.ts`, `schema.ts`),
|
|
52
|
+
and `services/planning.ts` exporting `makeService = () => makePostgresPlanningService({ plugins })`.
|
|
53
|
+
|
|
54
|
+
## API
|
|
55
|
+
|
|
56
|
+
`makePlanningCardPostgres`, `makePlanningTransitionPostgres`, `makePlanningLinkPostgres`,
|
|
57
|
+
`makePlanningSchemaPostgres` (and `make*Resource(alias, dbAlias?, serviceAlias?)`,
|
|
58
|
+
`makePlanningPostgresResources`); `makePostgresPlanningStore`, `makePostgresPlanningService`,
|
|
59
|
+
`appendPostgresPlanning`; `PostgresPlanningStore` (`fold`, `recover`, `close`);
|
|
60
|
+
`DEFAULT_PLANNING_POSTGRES_LIMITS`, `RES_PLANNING_*`, `PLANNING_POSTGRES_STORE`;
|
|
61
|
+
`PlanningPostgresError`; the table schemas `Planning*TableSchema`.
|
|
62
|
+
|
|
63
|
+
## Common pitfalls
|
|
64
|
+
|
|
65
|
+
- All four resources must be registered — the first call names the missing file.
|
|
66
|
+
- `close()` the store when a process ends on its own: the LISTEN connection keeps it alive.
|
|
67
|
+
- Do not aim `commits.subscribe` at hooks: `after` hooks run where the fold ran, once.
|
|
68
|
+
|
|
69
|
+
## Related packages
|
|
70
|
+
|
|
71
|
+
`@owlmeans/planning`, `@owlmeans/server-planning`, `@owlmeans/client-planning`,
|
|
72
|
+
`@owlmeans/postgres-resource`, `@owlmeans/postgres`.
|
|
73
|
+
|
|
74
|
+
<!-- owlmeans:agent-guidance:start -->
|
|
75
|
+
## Agent guidance
|
|
76
|
+
|
|
77
|
+
This package ships embedded agent skills under `agent-meta/`. After installing your
|
|
78
|
+
`@owlmeans/*` packages, run the OwlMeans agent-skills installer to place them into
|
|
79
|
+
your project's skill store (`.agents/skills/`):
|
|
80
|
+
|
|
81
|
+
```sh
|
|
82
|
+
npx @owlmeans/agent-skills@^0.1.18-rc.41
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The embedded files are version-matched to this package release. Do not edit them
|
|
86
|
+
directly — they are regenerated on each publish. To contribute guidance edits,
|
|
87
|
+
open a PR against the source monorepo.
|
|
88
|
+
<!-- owlmeans:agent-guidance:end -->
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 2,
|
|
3
|
+
"package": "@owlmeans/planning-postgres",
|
|
4
|
+
"version": "0.1.18-rc.0",
|
|
5
|
+
"generatedAt": "2026-09-28T02:06:37.869Z",
|
|
6
|
+
"canonicalRepo": "https://github.com/owlmeans/common",
|
|
7
|
+
"entries": [
|
|
8
|
+
{
|
|
9
|
+
"kind": "skill",
|
|
10
|
+
"name": "planning-postgres",
|
|
11
|
+
"category": "package-specific",
|
|
12
|
+
"file": "skills/planning-postgres/SKILL.md",
|
|
13
|
+
"canonicalPath": ".agents/skills/planning-postgres/SKILL.md"
|
|
14
|
+
}
|
|
15
|
+
]
|
|
16
|
+
}
|
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: planning-postgres
|
|
3
|
+
description: How to use @owlmeans/planning-postgres — the durable Postgres PlanningStore for @owlmeans/server-planning — the four resources (planning-card, planning-transition, planning-link, planning-schema) a target registers from resources/planning/*.ts, the service a target registers from services/planning.ts, the inline fold under a per-card advisory lock and its prelude, healing and recover(), the LISTEN/NOTIFY commit bus, the project purge, data-defined types and flows, the limits and the errors. Auto-invoked when wiring planning into a Postgres backend, touching a planning table, or diagnosing a planning commit that stays pending on Postgres.
|
|
4
|
+
user-invocable: false
|
|
5
|
+
---
|
|
6
|
+
<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
|
|
7
|
+
|
|
8
|
+
# @owlmeans/planning-postgres
|
|
9
|
+
|
|
10
|
+
**Layer:** Infra extension
|
|
11
|
+
**Install:** `"@owlmeans/planning-postgres": "^0.1.18-rc.0"` in `dependencies` (peers `pg`, `ajv`, `ajv-formats`)
|
|
12
|
+
|
|
13
|
+
A `PlanningStore` of `@owlmeans/server-planning` on Postgres. It owns no planning semantics: every
|
|
14
|
+
write still goes through the executor, every fold through `foldPending`, every query through
|
|
15
|
+
`criteriaOf` — this package supplies four tables, the transaction a fold runs in, and the bus that
|
|
16
|
+
carries commits between processes. It implements every port, the data-defined schema port
|
|
17
|
+
included, so a facade over it has `definitions`.
|
|
18
|
+
|
|
19
|
+
## Key Exports
|
|
20
|
+
|
|
21
|
+
| Export | Description |
|
|
22
|
+
|--------|-------------|
|
|
23
|
+
| `makePlanningCardPostgres` / `makePlanningTransitionPostgres` / `makePlanningLinkPostgres` / `makePlanningSchemaPostgres` | `ResourceMaker`s of the four tables, under the default aliases |
|
|
24
|
+
| `makePlanningCardResource(alias?, dbAlias?, serviceAlias?)` (and the three twins), `makePlanningPostgresResources(aliases)` | The same resources under custom aliases |
|
|
25
|
+
| `makePostgresPlanningService(opts?, alias?)` | The planning host service over a Postgres store — a target's `makeService()` |
|
|
26
|
+
| `appendPostgresPlanning(ctx, opts?, alias?)` | The four resources (each unless present), the service and `ctx.planning()` in one call |
|
|
27
|
+
| `makePostgresPlanningStore({ context, ...opts })` | The store alone; `fold(card)`, `recover(opts?)`, `close()` beside the ports |
|
|
28
|
+
| `PlanningPostgresOptions` | `{ aliases?, bus?, limits?, ids?, now? }`; `PostgresPlanningServiceOptions` adds the service's own (`plugins`, `schemas`, `hooks`) |
|
|
29
|
+
| `DEFAULT_PLANNING_POSTGRES_LIMITS` | `{ foldBatch: 500, gapGraceMs: 30_000, healAfterMs: 1_000, recoverAfterMs: 60_000, lockTimeoutMs: 10_000 }` |
|
|
30
|
+
| `RES_PLANNING_CARD` · `RES_PLANNING_TRANSITION` · `RES_PLANNING_LINK` · `RES_PLANNING_SCHEMA`, `PLANNING_POSTGRES_STORE` | `planning-card` … `planning-schema`, `planning-postgres` |
|
|
31
|
+
| `Planning*TableSchema` | The table schemas, derived from the planning record schemas |
|
|
32
|
+
| `PlanningPostgresError` | `planning-postgres:<what>` — a fault (500) |
|
|
33
|
+
| `planningChannel(qualified)`, `LOST_ALLOCATION` | The bus channel of a transition table; the cause of a gap's placeholder |
|
|
34
|
+
|
|
35
|
+
## The four tables
|
|
36
|
+
|
|
37
|
+
| Resource | Holds | Indexes |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `planning-card` | projects, cards AND specifications (routed by `kind` at the query layer) | `(entityId, kind, type)`, `(parent, order)`, GIN `(parents)`, `(parent, status)`, `(entityId, intrinsic, updatedAt)`, GIN `(labels)`, `(entityId, parent, code) WHERE code IS NOT NULL`, `(parent, category) WHERE kind = 'specification'` |
|
|
40
|
+
| `planning-transition` | the append-only log | UNIQUE `(card, seq)`, UNIQUE `(entityId, key) WHERE key IS NOT NULL`, `(project, at)`, `(at) WHERE (commit->>'state') = 'pending'` |
|
|
41
|
+
| `planning-link` | typed edges | UNIQUE `(from, to, type)`, `(to, type)`, `(entityId, type)`, `(project)` |
|
|
42
|
+
| `planning-schema` | data-defined types and flows, and one private `kind: 'head'` row per organization (its schema revision) | UNIQUE `("entityId", COALESCE(project, ''), kind, key)`, `(entityId, rev)` |
|
|
43
|
+
|
|
44
|
+
- The DDL comes from `AnyWorkcardSchema`, `TransitionSchema`, `RelationshipSchema` and
|
|
45
|
+
`ScopedSchemaRecordSchema`. **Every timestamp stays `text`**: the `date-time` format would compile
|
|
46
|
+
to `timestamptz` and marshal through `Date`, which rewrites the ISO string a record carries and
|
|
47
|
+
breaks the lexicographic order `updatedSince`, `at` sorts and gap ages rely on. `seq`, `head`,
|
|
48
|
+
`revision` and `bodyChars` (and a schema record's `version` and `rev`) are `integer`; `body` is
|
|
49
|
+
`text`.
|
|
50
|
+
- The card table carries one private column, `headAt` — when `head` last moved — that no read
|
|
51
|
+
ever returns.
|
|
52
|
+
- A card list includes specifications only when it asks for `kind: 'specification'` or names a
|
|
53
|
+
`category` (`wantsSpecifications`, the memory store's own rule); a summary never counts them.
|
|
54
|
+
- Every maker declares each index once, however often it runs.
|
|
55
|
+
|
|
56
|
+
## Target wiring
|
|
57
|
+
|
|
58
|
+
**Backs:** projects, cards and their documents as planning workcards — the transition log they are folded from, their links, and the card types and flows an application defines as data
|
|
59
|
+
|
|
60
|
+
| Sub-project | Packages |
|
|
61
|
+
|---|---|
|
|
62
|
+
| backend | `@owlmeans/planning-postgres`, `@owlmeans/server-planning`, `@owlmeans/planning` |
|
|
63
|
+
| common | `@owlmeans/planning` |
|
|
64
|
+
| api | `@owlmeans/server-planning`, `@owlmeans/planning` |
|
|
65
|
+
|
|
66
|
+
The service resolves its four resources by alias, so the registration order never matters. All
|
|
67
|
+
four are required: the store keeps data-defined types and flows too, and a write reads their
|
|
68
|
+
layer.
|
|
69
|
+
|
|
70
|
+
```ts file=sources/backend/src/services/planning.ts
|
|
71
|
+
import { makePostgresPlanningService } from '@owlmeans/planning-postgres'
|
|
72
|
+
import type { Service } from '@owlmeans/context'
|
|
73
|
+
import { PLANNING } from '__APP_SLUG__-common/planning'
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The planning service over Postgres — resolves its four resources
|
|
77
|
+
* (`resources/planning/{card,transition,link,schema}.ts`) by alias, so registration order never
|
|
78
|
+
* matters. The maker name is fixed — the generated service registry imports exactly this symbol.
|
|
79
|
+
*/
|
|
80
|
+
export const makeService = (): Service => makePostgresPlanningService({ plugins: [PLANNING] })
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```ts file=sources/backend/src/resources/planning/card.ts
|
|
84
|
+
import { makePlanningCardPostgres } from '@owlmeans/planning-postgres'
|
|
85
|
+
import type { PlanningCardRecord, PlanningCardResource } from '@owlmeans/planning-postgres'
|
|
86
|
+
import type { ResourceMaker } from '@owlmeans/resource'
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Projects, cards and their documents — one table (`planning-card`), routed by `kind` at the
|
|
90
|
+
* query layer. The maker is a thin wrapper: the schema and indexes are the package's own.
|
|
91
|
+
*/
|
|
92
|
+
export const makeResource: ResourceMaker<PlanningCardRecord, PlanningCardResource> =
|
|
93
|
+
(dbAlias, serviceAlias) => makePlanningCardPostgres(dbAlias, serviceAlias)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
```ts file=sources/backend/src/resources/planning/transition.ts
|
|
97
|
+
import { makePlanningTransitionPostgres } from '@owlmeans/planning-postgres'
|
|
98
|
+
import type { PlanningTransitionResource } from '@owlmeans/planning-postgres'
|
|
99
|
+
import type { Transition } from '@owlmeans/planning'
|
|
100
|
+
import type { ResourceMaker } from '@owlmeans/resource'
|
|
101
|
+
|
|
102
|
+
/** The append-only transition log (`planning-transition`) every card is folded from. */
|
|
103
|
+
export const makeResource: ResourceMaker<Transition, PlanningTransitionResource> =
|
|
104
|
+
(dbAlias, serviceAlias) => makePlanningTransitionPostgres(dbAlias, serviceAlias)
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
```ts file=sources/backend/src/resources/planning/link.ts
|
|
108
|
+
import { makePlanningLinkPostgres } from '@owlmeans/planning-postgres'
|
|
109
|
+
import type { PlanningLinkResource } from '@owlmeans/planning-postgres'
|
|
110
|
+
import type { Relationship } from '@owlmeans/planning'
|
|
111
|
+
import type { ResourceMaker } from '@owlmeans/resource'
|
|
112
|
+
|
|
113
|
+
/** Typed links between cards (`planning-link`) — one row per from, to and type. */
|
|
114
|
+
export const makeResource: ResourceMaker<Relationship, PlanningLinkResource> =
|
|
115
|
+
(dbAlias, serviceAlias) => makePlanningLinkPostgres(dbAlias, serviceAlias)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
```ts file=sources/backend/src/resources/planning/schema.ts
|
|
119
|
+
import { makePlanningSchemaPostgres } from '@owlmeans/planning-postgres'
|
|
120
|
+
import type { PlanningSchemaResource, PlanningSchemaRow } from '@owlmeans/planning-postgres'
|
|
121
|
+
import type { ResourceMaker } from '@owlmeans/resource'
|
|
122
|
+
|
|
123
|
+
/** Card types and flows defined as data (`planning-schema`), per organization and per project. */
|
|
124
|
+
export const makeResource: ResourceMaker<PlanningSchemaRow, PlanningSchemaResource> =
|
|
125
|
+
(dbAlias, serviceAlias) => makePlanningSchemaPostgres(dbAlias, serviceAlias)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
```ts file=sources/common/src/planning.ts
|
|
129
|
+
import type { PlanningPlugin } from '@owlmeans/planning'
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* This application's planning types and flows — see the `planning` skill for their shape. The
|
|
133
|
+
* backend's planning service registers it; a client that reads a model loads the same bundle from
|
|
134
|
+
* the server, so nothing else imports it.
|
|
135
|
+
*/
|
|
136
|
+
export const PLANNING: PlanningPlugin = {
|
|
137
|
+
name: 'app-planning',
|
|
138
|
+
schemas: {
|
|
139
|
+
flows: [
|
|
140
|
+
// owlmeans: add planning flows above this line
|
|
141
|
+
],
|
|
142
|
+
types: [
|
|
143
|
+
// owlmeans: add planning types above this line
|
|
144
|
+
],
|
|
145
|
+
},
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## A worked example
|
|
150
|
+
|
|
151
|
+
The api binds the tree the shared package declares (an api's own `entrypoints.ts`, outside the
|
|
152
|
+
wiring above); a hand-written backend may register everything in one call instead —
|
|
153
|
+
`appendPostgres(context); appendPostgresPlanning(context, { plugins: [PLANNING] })`. A lending
|
|
154
|
+
library's branches (projects) and books (cards):
|
|
155
|
+
|
|
156
|
+
```ts
|
|
157
|
+
// common: a lending library's branches (projects) and books (cards)
|
|
158
|
+
export const PLANNING: PlanningPlugin = {
|
|
159
|
+
name: 'app-planning',
|
|
160
|
+
schemas: {
|
|
161
|
+
flows: [{
|
|
162
|
+
id: 'library:circulation', version: 1,
|
|
163
|
+
statuses: [
|
|
164
|
+
{ key: 'shelved', intrinsic: IntrinsicStatus.Planned, initial: true },
|
|
165
|
+
{ key: 'lent', intrinsic: IntrinsicStatus.InProgress },
|
|
166
|
+
{ key: 'retired', intrinsic: IntrinsicStatus.Closed },
|
|
167
|
+
],
|
|
168
|
+
transitions: [
|
|
169
|
+
{ name: 'lend', from: ['shelved'], to: 'lent', explicit: true },
|
|
170
|
+
{ name: 'return', from: ['lent'], to: 'shelved', explicit: true },
|
|
171
|
+
],
|
|
172
|
+
}],
|
|
173
|
+
types: [
|
|
174
|
+
{ type: 'library:branch', kind: WorkcardKind.Project, version: 1, fields: { type: 'object' },
|
|
175
|
+
flows: ['library:circulation'], specifications: [], cardTypes: ['library:book'], scopedCardTypes: true },
|
|
176
|
+
{ type: 'library:book', kind: WorkcardKind.Card, version: 1, fields: { type: 'object' },
|
|
177
|
+
flows: ['library:circulation'], specifications: [] },
|
|
178
|
+
],
|
|
179
|
+
},
|
|
180
|
+
}
|
|
181
|
+
export const planningProtocols = makePlanningProtocols({
|
|
182
|
+
base: { alias: 'library:planning', path: '/planning' }, guards: DEFAULT_GUARD, definitions: true,
|
|
183
|
+
})
|
|
184
|
+
|
|
185
|
+
// api: the protocol bindings
|
|
186
|
+
...servePlanningEntrypoints(planningProtocols)
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
The first store call into a missing resource throws
|
|
190
|
+
`PlanningPostgresError('resource-missing:planning-schema: add src/resources/planning/schema.ts')`,
|
|
191
|
+
naming the file to add.
|
|
192
|
+
|
|
193
|
+
## The fold
|
|
194
|
+
|
|
195
|
+
`project()` folds inline — there is no queued mode. One fold is ONE transaction:
|
|
196
|
+
|
|
197
|
+
1. `SET LOCAL lock_timeout` (`limits.lockTimeoutMs`), then `pg_advisory_xact_lock` on
|
|
198
|
+
`advisoryKey('planning:<qualified card table>:<card id>')`.
|
|
199
|
+
2. **The prelude** walks the rows past `card.seq` (at most `foldBatch`): a FAILED row at the cursor
|
|
200
|
+
moves the cursor past it; the PENDING rows from the cursor on are the run the fold takes; a GAP
|
|
201
|
+
— a row past the expected seq, or `head > seq` with no row at all — is an append still in
|
|
202
|
+
flight while younger than `gapGraceMs` (the fold stops; the appender's own `project()` folds
|
|
203
|
+
it), and a lost allocation after that: a failed `lost-allocation` placeholder per missing seq.
|
|
204
|
+
`nextSeq` and `append` are two round trips, so without it a transient gap or an already-failed
|
|
205
|
+
row would turn the next write into a spurious `fold:out-of-order`.
|
|
206
|
+
3. `foldPending` of `@owlmeans/server-planning` over a view of the ports bound to the transaction,
|
|
207
|
+
limited to that run, each transition's writes in a SAVEPOINT (`FoldOptions.unit`) — so a write
|
|
208
|
+
the database refuses fails that transition alone and the fold goes past it. Prelude and fold
|
|
209
|
+
repeat within the transaction until the log is folded or a young gap stops it.
|
|
210
|
+
4. A `pg_notify` per settled event (no record).
|
|
211
|
+
5. COMMIT — and only then the events reach the commit hub and the `after` hooks, in order, outside
|
|
212
|
+
the transaction and the lock. A hook may therefore write to the card it saw commit.
|
|
213
|
+
|
|
214
|
+
A statement that fails outside a savepoint **poisons** the transaction: it is never committed
|
|
215
|
+
(the "transaction is aborted" answers after it are not the cause — the first failure is). The
|
|
216
|
+
fold is retried once; failing again, `failPending` fails the card's pending transitions in a fresh
|
|
217
|
+
transaction, so no waiter hangs to its timeout. A lock that cannot be taken within
|
|
218
|
+
`lockTimeoutMs` means another process is folding the card: nothing is failed, a heal follows.
|
|
219
|
+
|
|
220
|
+
Allocation: `nextSeq` is a compare-and-set of `head` on the card row, stamping `headAt`; a card
|
|
221
|
+
whose create has not folded counts `max(seq) + 1` from the log and the unique `(card, seq)` index
|
|
222
|
+
refuses a repeat. A card write never lowers `head` (`GREATEST`) and never touches `headAt`; a write
|
|
223
|
+
after the create is an UPDATE of a row that must still exist, so a card purged with its project is
|
|
224
|
+
not resurrected by a fold that was already under way.
|
|
225
|
+
|
|
226
|
+
## Healing and recovery
|
|
227
|
+
|
|
228
|
+
- **Every waiter heals**: a status read of a pending row older than `healAfterMs` starts a
|
|
229
|
+
background fold of its card (try-lock, deduplicated per card).
|
|
230
|
+
- `recover({ olderThanMs = recoverAfterMs, limit = 100 })` folds each card whose oldest pending
|
|
231
|
+
row is older than the threshold (the partial pending index), and runs once in the background on
|
|
232
|
+
the store's first use.
|
|
233
|
+
- A lost allocation with no later row is released by the next fold of the card.
|
|
234
|
+
|
|
235
|
+
## The commit bus
|
|
236
|
+
|
|
237
|
+
`makeCommitHub` is the commit source; LISTEN/NOTIFY feeds it across processes. The channel is
|
|
238
|
+
`planning_<16 hex>` of the qualified transition table, so two schemas in one database never hear
|
|
239
|
+
each other. The frame is `{ p: <process id>, t: 'c', e: <CommitEvent without record> }`; a process
|
|
240
|
+
ignores its own (`p`) — it delivered it itself, after its commit. LISTEN holds one dedicated
|
|
241
|
+
`pg.Client` built from the pool's own configuration (no pool slot), opened on the first subscribe,
|
|
242
|
+
wait or schema watch, reconnecting 1 s → 30 s; meanwhile the hub's poll ladder answers every
|
|
243
|
+
waiter. A schema write sends `{ t: 's', e: <entityId> }` on the same channel. `bus: false` sends
|
|
244
|
+
and hears nothing — waits poll, layers re-read the revision.
|
|
245
|
+
|
|
246
|
+
## Data-defined types and flows
|
|
247
|
+
|
|
248
|
+
`planning-schema` holds one record per `(organization, project layer, kind, key)`; a write is one
|
|
249
|
+
transaction that bumps the organization's `head` row, writes under the compare-and-set on `version`
|
|
250
|
+
(an INSERT for version 1, an UPDATE guarded on `version - 1` after) and NOTIFYs. `revision()` is a
|
|
251
|
+
primary read of the head row, so the service's cached layers are never served stale. The rules —
|
|
252
|
+
what may be defined, sealing, retiring — are `planning`'s and `server-planning`'s.
|
|
253
|
+
|
|
254
|
+
## Purge
|
|
255
|
+
|
|
256
|
+
A project's delete purges in the fold's own transaction (or, called directly, in one under the
|
|
257
|
+
project's lock): a recursive walk over `parents @> ARRAY[id]` finds every doomed card, nested
|
|
258
|
+
projects included; then links, transitions, the doomed projects' schema layers and the cards go,
|
|
259
|
+
in that order. The project's own `delete` rows stay as its tombstone — a waiter still reads the
|
|
260
|
+
delete committed.
|
|
261
|
+
|
|
262
|
+
## Querying
|
|
263
|
+
|
|
264
|
+
Lists, counts and summaries go through `@owlmeans/postgres-resource` (`criteriaToSql`), so a
|
|
265
|
+
`WorkcardQuery` selects here what it selects in memory: `within` is `parents @> $1::varchar[]`,
|
|
266
|
+
`labels` `&&`, `flows.<id>` / `fields.<key>` typed jsonb paths (a list is membership), `q` `ILIKE` +
|
|
267
|
+
code prefix. A summary is one `countBy(parent, intrinsic)`. Paging is Postgres's — 100 rows unless
|
|
268
|
+
`size` says otherwise.
|
|
269
|
+
|
|
270
|
+
## Gotchas
|
|
271
|
+
|
|
272
|
+
- `close()` the store in a process that should end on its own — the LISTEN connection keeps it
|
|
273
|
+
alive.
|
|
274
|
+
- Two fold transactions of one card never run at once in a process (chained) or across processes
|
|
275
|
+
(the advisory lock); a heal that finds the lock taken yields.
|
|
276
|
+
- A row appended by hand (`store.transitions.append`) is folded only by `project()`, a heal or
|
|
277
|
+
`recover()` — nothing watches the table.
|
|
278
|
+
|
|
279
|
+
## Testing
|
|
280
|
+
|
|
281
|
+
`bun test ./tests` — `schema.spec.ts` and `sql.spec.ts` need no database; `conformance.spec.ts`
|
|
282
|
+
(the `@owlmeans/server-planning/conformance` cases), `fold.spec.ts`, `bus.spec.ts` and
|
|
283
|
+
`sync.spec.ts` are gated on `POSTGRES_URL` (`postgresGate()`) and skip cleanly without it. Each spec
|
|
284
|
+
file owns a throwaway schema (`makeSuite`); a fault is injected with a real trigger, never a mock.
|
|
285
|
+
|
|
286
|
+
## Related
|
|
287
|
+
|
|
288
|
+
- `server-planning` — the executor, `foldPending`/`failPending`, the commit hub, the conformance suite
|
|
289
|
+
- `planning` — records, flows, scoped schemas, the query language
|
|
290
|
+
- `postgres-resource` — the table compiler, `criteriaToSql`, `countBy`, `advisoryKey`
|
|
291
|
+
- `postgres` — the connection service these resources resolve through
|
|
292
|
+
- `marketing-consent-postgres` — the same resource-per-file wiring for another feature
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** The four resource aliases — each is also the physical table name (sanitized). */
|
|
2
|
+
export declare const RES_PLANNING_CARD = "planning-card";
|
|
3
|
+
export declare const RES_PLANNING_TRANSITION = "planning-transition";
|
|
4
|
+
export declare const RES_PLANNING_LINK = "planning-link";
|
|
5
|
+
export declare const RES_PLANNING_SCHEMA = "planning-schema";
|
|
6
|
+
/** The alias the store answers `PlanningStore.alias` with, and the service its placeholders name. */
|
|
7
|
+
export declare const PLANNING_POSTGRES_STORE = "planning-postgres";
|
|
8
|
+
export declare const DEFAULT_PLANNING_POSTGRES_LIMITS: Readonly<{
|
|
9
|
+
/** The most log rows one fold transaction reads and applies; the rest follow in the next round. */
|
|
10
|
+
foldBatch: 500;
|
|
11
|
+
/**
|
|
12
|
+
* How long a gap in a card's log — a seq allocated with no row yet — is an append still in
|
|
13
|
+
* flight. Older, it is a lost allocation: the fold writes a failed placeholder and goes on.
|
|
14
|
+
*/
|
|
15
|
+
gapGraceMs: 30000;
|
|
16
|
+
/** A pending row older than this makes every status read of it a heal: a background fold. */
|
|
17
|
+
healAfterMs: 1000;
|
|
18
|
+
/** What `recover()` folds by default: cards whose oldest pending row is older than this. */
|
|
19
|
+
recoverAfterMs: 60000;
|
|
20
|
+
/** `SET LOCAL lock_timeout` of a fold transaction, in milliseconds. */
|
|
21
|
+
lockTimeoutMs: 10000;
|
|
22
|
+
}>;
|
|
23
|
+
/** How many cards one `recover()` folds by default. */
|
|
24
|
+
export declare const DEFAULT_RECOVER_LIMIT = 100;
|
|
25
|
+
/** The cause and error of a failed placeholder written over a lost allocation. */
|
|
26
|
+
export declare const LOST_ALLOCATION = "lost-allocation";
|
|
27
|
+
/** Rounds (transactions) one `fold()` runs while a round reports more rows than `foldBatch`. */
|
|
28
|
+
export declare const MAX_FOLD_ROUNDS = 16;
|
|
29
|
+
/** Passes of prelude and fold one transaction runs — each over one run of consecutive pending rows. */
|
|
30
|
+
export declare const MAX_FOLD_PASSES = 64;
|
|
31
|
+
/** The private schema row that is an organization's monotonic schema revision. */
|
|
32
|
+
export declare const SCHEMA_HEAD_KIND = "head";
|
|
33
|
+
export declare const SCHEMA_HEAD_KEY = "head";
|
|
34
|
+
/** Reconnect backoff of the LISTEN connection, doubling from the first to the last, in milliseconds. */
|
|
35
|
+
export declare const BUS_BACKOFF: readonly [number, number];
|
|
36
|
+
/** Postgres refuses a NOTIFY payload of 8000 bytes or more; a frame stays under this. */
|
|
37
|
+
export declare const NOTIFY_PAYLOAD_MAX = 7900;
|
|
38
|
+
/** The file a target adds for each resource — what a missing-resource error names. */
|
|
39
|
+
export declare const PLANNING_RESOURCE_FILES: Readonly<{
|
|
40
|
+
card: "src/resources/planning/card.ts";
|
|
41
|
+
transition: "src/resources/planning/transition.ts";
|
|
42
|
+
link: "src/resources/planning/link.ts";
|
|
43
|
+
schema: "src/resources/planning/schema.ts";
|
|
44
|
+
}>;
|
|
45
|
+
//# sourceMappingURL=consts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.d.ts","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,eAAO,MAAM,iBAAiB,kBAAkB,CAAA;AAChD,eAAO,MAAM,uBAAuB,wBAAwB,CAAA;AAC5D,eAAO,MAAM,iBAAiB,kBAAkB,CAAA;AAChD,eAAO,MAAM,mBAAmB,oBAAoB,CAAA;AAEpD,qGAAqG;AACrG,eAAO,MAAM,uBAAuB,sBAAsB,CAAA;AAE1D,eAAO,MAAM,gCAAgC;IAC3C,mGAAmG;;IAEnG;;;OAGG;;IAEH,6FAA6F;;IAE7F,4FAA4F;;IAE5F,uEAAuE;;EAEvE,CAAA;AAEF,uDAAuD;AACvD,eAAO,MAAM,qBAAqB,MAAM,CAAA;AAExC,kFAAkF;AAClF,eAAO,MAAM,eAAe,oBAAoB,CAAA;AAEhD,gGAAgG;AAChG,eAAO,MAAM,eAAe,KAAK,CAAA;AAEjC,uGAAuG;AACvG,eAAO,MAAM,eAAe,KAAK,CAAA;AAEjC,kFAAkF;AAClF,eAAO,MAAM,gBAAgB,SAAS,CAAA;AACtC,eAAO,MAAM,eAAe,SAAS,CAAA;AAErC,wGAAwG;AACxG,eAAO,MAAM,WAAW,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,CAA+D,CAAA;AAEjH,yFAAyF;AACzF,eAAO,MAAM,kBAAkB,OAAQ,CAAA;AAEvC,sFAAsF;AACtF,eAAO,MAAM,uBAAuB;;;;;EAKlC,CAAA"}
|
package/build/consts.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** The four resource aliases — each is also the physical table name (sanitized). */
|
|
2
|
+
export const RES_PLANNING_CARD = 'planning-card';
|
|
3
|
+
export const RES_PLANNING_TRANSITION = 'planning-transition';
|
|
4
|
+
export const RES_PLANNING_LINK = 'planning-link';
|
|
5
|
+
export const RES_PLANNING_SCHEMA = 'planning-schema';
|
|
6
|
+
/** The alias the store answers `PlanningStore.alias` with, and the service its placeholders name. */
|
|
7
|
+
export const PLANNING_POSTGRES_STORE = 'planning-postgres';
|
|
8
|
+
export const DEFAULT_PLANNING_POSTGRES_LIMITS = Object.freeze({
|
|
9
|
+
/** The most log rows one fold transaction reads and applies; the rest follow in the next round. */
|
|
10
|
+
foldBatch: 500,
|
|
11
|
+
/**
|
|
12
|
+
* How long a gap in a card's log — a seq allocated with no row yet — is an append still in
|
|
13
|
+
* flight. Older, it is a lost allocation: the fold writes a failed placeholder and goes on.
|
|
14
|
+
*/
|
|
15
|
+
gapGraceMs: 30_000,
|
|
16
|
+
/** A pending row older than this makes every status read of it a heal: a background fold. */
|
|
17
|
+
healAfterMs: 1_000,
|
|
18
|
+
/** What `recover()` folds by default: cards whose oldest pending row is older than this. */
|
|
19
|
+
recoverAfterMs: 60_000,
|
|
20
|
+
/** `SET LOCAL lock_timeout` of a fold transaction, in milliseconds. */
|
|
21
|
+
lockTimeoutMs: 10_000,
|
|
22
|
+
});
|
|
23
|
+
/** How many cards one `recover()` folds by default. */
|
|
24
|
+
export const DEFAULT_RECOVER_LIMIT = 100;
|
|
25
|
+
/** The cause and error of a failed placeholder written over a lost allocation. */
|
|
26
|
+
export const LOST_ALLOCATION = 'lost-allocation';
|
|
27
|
+
/** Rounds (transactions) one `fold()` runs while a round reports more rows than `foldBatch`. */
|
|
28
|
+
export const MAX_FOLD_ROUNDS = 16;
|
|
29
|
+
/** Passes of prelude and fold one transaction runs — each over one run of consecutive pending rows. */
|
|
30
|
+
export const MAX_FOLD_PASSES = 64;
|
|
31
|
+
/** The private schema row that is an organization's monotonic schema revision. */
|
|
32
|
+
export const SCHEMA_HEAD_KIND = 'head';
|
|
33
|
+
export const SCHEMA_HEAD_KEY = 'head';
|
|
34
|
+
/** Reconnect backoff of the LISTEN connection, doubling from the first to the last, in milliseconds. */
|
|
35
|
+
export const BUS_BACKOFF = Object.freeze([1_000, 30_000]);
|
|
36
|
+
/** Postgres refuses a NOTIFY payload of 8000 bytes or more; a frame stays under this. */
|
|
37
|
+
export const NOTIFY_PAYLOAD_MAX = 7_900;
|
|
38
|
+
/** The file a target adds for each resource — what a missing-resource error names. */
|
|
39
|
+
export const PLANNING_RESOURCE_FILES = Object.freeze({
|
|
40
|
+
card: 'src/resources/planning/card.ts',
|
|
41
|
+
transition: 'src/resources/planning/transition.ts',
|
|
42
|
+
link: 'src/resources/planning/link.ts',
|
|
43
|
+
schema: 'src/resources/planning/schema.ts',
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=consts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consts.js","sourceRoot":"","sources":["../src/consts.ts"],"names":[],"mappings":"AAAA,oFAAoF;AACpF,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAA;AAChD,MAAM,CAAC,MAAM,uBAAuB,GAAG,qBAAqB,CAAA;AAC5D,MAAM,CAAC,MAAM,iBAAiB,GAAG,eAAe,CAAA;AAChD,MAAM,CAAC,MAAM,mBAAmB,GAAG,iBAAiB,CAAA;AAEpD,qGAAqG;AACrG,MAAM,CAAC,MAAM,uBAAuB,GAAG,mBAAmB,CAAA;AAE1D,MAAM,CAAC,MAAM,gCAAgC,GAAG,MAAM,CAAC,MAAM,CAAC;IAC5D,mGAAmG;IACnG,SAAS,EAAE,GAAG;IACd;;;OAGG;IACH,UAAU,EAAE,MAAM;IAClB,6FAA6F;IAC7F,WAAW,EAAE,KAAK;IAClB,4FAA4F;IAC5F,cAAc,EAAE,MAAM;IACtB,uEAAuE;IACvE,aAAa,EAAE,MAAM;CACtB,CAAC,CAAA;AAEF,uDAAuD;AACvD,MAAM,CAAC,MAAM,qBAAqB,GAAG,GAAG,CAAA;AAExC,kFAAkF;AAClF,MAAM,CAAC,MAAM,eAAe,GAAG,iBAAiB,CAAA;AAEhD,gGAAgG;AAChG,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAA;AAEjC,uGAAuG;AACvG,MAAM,CAAC,MAAM,eAAe,GAAG,EAAE,CAAA;AAEjC,kFAAkF;AAClF,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAA;AACtC,MAAM,CAAC,MAAM,eAAe,GAAG,MAAM,CAAA;AAErC,wGAAwG;AACxG,MAAM,CAAC,MAAM,WAAW,GAA8B,MAAM,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAA8B,CAAA;AAEjH,yFAAyF;AACzF,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,CAAA;AAEvC,sFAAsF;AACtF,MAAM,CAAC,MAAM,uBAAuB,GAAG,MAAM,CAAC,MAAM,CAAC;IACnD,IAAI,EAAE,gCAAgC;IACtC,UAAU,EAAE,sCAAsC;IAClD,IAAI,EAAE,gCAAgC;IACtC,MAAM,EAAE,kCAAkC;CAC3C,CAAC,CAAA"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error';
|
|
2
|
+
/**
|
|
3
|
+
* A fault of the Postgres planning store — a missing resource, a fold transaction that could not
|
|
4
|
+
* commit. Messages start with `planning-postgres:`; it declares no HTTP status (a fault answers 500).
|
|
5
|
+
*/
|
|
6
|
+
export declare class PlanningPostgresError extends ResilientError {
|
|
7
|
+
static typeName: string;
|
|
8
|
+
constructor(message?: string);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD;;;GAGG;AACH,qBAAa,qBAAsB,SAAQ,cAAc;IACvD,OAAuB,QAAQ,EAAE,MAAM,CAA0B;IAEjE,YAAY,OAAO,GAAE,MAAgB,EAEpC;CACF"}
|
package/build/errors.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { ResilientError } from '@owlmeans/error';
|
|
2
|
+
/**
|
|
3
|
+
* A fault of the Postgres planning store — a missing resource, a fold transaction that could not
|
|
4
|
+
* commit. Messages start with `planning-postgres:`; it declares no HTTP status (a fault answers 500).
|
|
5
|
+
*/
|
|
6
|
+
export class PlanningPostgresError extends ResilientError {
|
|
7
|
+
static typeName = 'PlanningPostgresError';
|
|
8
|
+
constructor(message = 'error') {
|
|
9
|
+
super(PlanningPostgresError.typeName, `planning-postgres:${message}`);
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
ResilientError.registerErrorClass(PlanningPostgresError);
|
|
13
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD;;;GAGG;AACH,MAAM,OAAO,qBAAsB,SAAQ,cAAc;IAChD,MAAM,CAAU,QAAQ,GAAW,uBAAuB,CAAA;IAEjE,YAAY,OAAO,GAAW,OAAO;QACnC,KAAK,CAAC,qBAAqB,CAAC,QAAQ,EAAE,qBAAqB,OAAO,EAAE,CAAC,CAAA;IACvE,CAAC;CACF;AAED,cAAc,CAAC,kBAAkB,CAAC,qBAAqB,CAAC,CAAA"}
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type * from './types.js';
|
|
2
|
+
export * from './consts.js';
|
|
3
|
+
export * from './errors.js';
|
|
4
|
+
export * from './schemas.js';
|
|
5
|
+
export * from './resource.js';
|
|
6
|
+
export * from './sql.js';
|
|
7
|
+
export * from './store/index.js';
|
|
8
|
+
export * from './service.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,mBAAmB,YAAY,CAAA;AAE/B,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA"}
|
package/build/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAEA,cAAc,aAAa,CAAA;AAC3B,cAAc,aAAa,CAAA;AAC3B,cAAc,cAAc,CAAA;AAC5B,cAAc,eAAe,CAAA;AAC7B,cAAc,UAAU,CAAA;AACxB,cAAc,kBAAkB,CAAA;AAChC,cAAc,cAAc,CAAA"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { Relationship, Transition } from '@owlmeans/planning';
|
|
2
|
+
import type { ResourceMaker } from '@owlmeans/resource';
|
|
3
|
+
import type { PlanningCardRecord, PlanningCardResource, PlanningLinkResource, PlanningPostgresAliases, PlanningSchemaResource, PlanningSchemaRow, PlanningTransitionResource } from './types.js';
|
|
4
|
+
/** The index names a table carries, by suffix — what the store matches a unique violation on. */
|
|
5
|
+
export declare const planningIndexName: (alias: string, suffix: string) => string;
|
|
6
|
+
/**
|
|
7
|
+
* `planning-card` — projects, cards and specifications in ONE table, routed by `kind` at the query
|
|
8
|
+
* layer. Indexes follow the reads: a list by kind and type, children in order, membership
|
|
9
|
+
* (`parents`, GIN), a board by status, the intrinsic state by recency, labels (GIN), a code within
|
|
10
|
+
* its parent, and a slot's documents.
|
|
11
|
+
*/
|
|
12
|
+
export declare const makePlanningCardResource: (alias?: string, dbAlias?: string, serviceAlias?: string) => PlanningCardResource;
|
|
13
|
+
/**
|
|
14
|
+
* `planning-transition` — the append-only log. `(card, seq)` is the arbiter of allocation, a key is
|
|
15
|
+
* unique per organization, a project's history reads by time, and the pending rows by age (what
|
|
16
|
+
* `recover()` scans).
|
|
17
|
+
*/
|
|
18
|
+
export declare const makePlanningTransitionResource: (alias?: string, dbAlias?: string, serviceAlias?: string) => PlanningTransitionResource;
|
|
19
|
+
/** `planning-link` — one row per `(from, to, type)`, read from either end, by type, and by project. */
|
|
20
|
+
export declare const makePlanningLinkResource: (alias?: string, dbAlias?: string, serviceAlias?: string) => PlanningLinkResource;
|
|
21
|
+
/**
|
|
22
|
+
* `planning-schema` — data-defined types and flows, one row per `(organization, project layer,
|
|
23
|
+
* kind, key)`, plus each organization's private revision row (`kind: 'head'`).
|
|
24
|
+
*/
|
|
25
|
+
export declare const makePlanningSchemaResource: (alias?: string, dbAlias?: string, serviceAlias?: string) => PlanningSchemaResource;
|
|
26
|
+
export declare const makePlanningCardPostgres: ResourceMaker<PlanningCardRecord, PlanningCardResource>;
|
|
27
|
+
export declare const makePlanningTransitionPostgres: ResourceMaker<Transition, PlanningTransitionResource>;
|
|
28
|
+
export declare const makePlanningLinkPostgres: ResourceMaker<Relationship, PlanningLinkResource>;
|
|
29
|
+
export declare const makePlanningSchemaPostgres: ResourceMaker<PlanningSchemaRow, PlanningSchemaResource>;
|
|
30
|
+
/** The four resources under the aliases a store resolves — what `appendPostgresPlanning` registers. */
|
|
31
|
+
export declare const makePlanningPostgresResources: (aliases: PlanningPostgresAliases, dbAlias?: string, serviceAlias?: string) => [PlanningCardResource, PlanningTransitionResource, PlanningLinkResource, PlanningSchemaResource];
|
|
32
|
+
//# sourceMappingURL=resource.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.d.ts","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAGlE,OAAO,KAAK,EAAE,aAAa,EAAkB,MAAM,oBAAoB,CAAA;AAKvE,OAAO,KAAK,EACV,kBAAkB,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,uBAAuB,EAAE,sBAAsB,EAC/G,iBAAiB,EAAE,0BAA0B,EAC9C,MAAM,YAAY,CAAA;AAoBnB,iGAAiG;AACjG,eAAO,MAAM,iBAAiB,UAAW,MAAM,UAAU,MAAM,KAAG,MAA4C,CAAA;AAE9G;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,WAC5B,MAAM,YAAgC,MAAM,iBAAiB,MAAM,KACzE,oBAeF,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,WAClC,MAAM,YAAsC,MAAM,iBAAiB,MAAM,KAC/E,0BAWF,CAAA;AAED,uGAAuG;AACvG,eAAO,MAAM,wBAAwB,WAC5B,MAAM,YAAgC,MAAM,iBAAiB,MAAM,KACzE,oBAWF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,0BAA0B,WAC9B,MAAM,YAAkC,MAAM,iBAAiB,MAAM,KAC3E,sBASF,CAAA;AAED,eAAO,MAAM,wBAAwB,EAAE,aAAa,CAAC,kBAAkB,EAAE,oBAAoB,CACE,CAAA;AAE/F,eAAO,MAAM,8BAA8B,EAAE,aAAa,CAAC,UAAU,EAAE,0BAA0B,CACU,CAAA;AAE3G,eAAO,MAAM,wBAAwB,EAAE,aAAa,CAAC,YAAY,EAAE,oBAAoB,CACQ,CAAA;AAE/F,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,iBAAiB,EAAE,sBAAsB,CACG,CAAA;AAEnG,uGAAuG;AACvG,eAAO,MAAM,6BAA6B,YAC/B,uBAAuB,YAAY,MAAM,iBAAiB,MAAM,KACxE,CAAC,oBAAoB,EAAE,0BAA0B,EAAE,oBAAoB,EAAE,sBAAsB,CAKjG,CAAA"}
|