@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
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { getDeclaration, makePostgresResource, PgIndexMethod, pgIdentifier } from '@owlmeans/postgres-resource';
|
|
2
|
+
import { RES_PLANNING_CARD, RES_PLANNING_LINK, RES_PLANNING_SCHEMA, RES_PLANNING_TRANSITION } from './consts.js';
|
|
3
|
+
import { PlanningCardTableSchema, PlanningLinkTableSchema, PlanningSchemaTableSchema, PlanningTransitionTableSchema, } from './schemas.js';
|
|
4
|
+
/**
|
|
5
|
+
* Declare the indexes a maker owns — each only once. The declaration is keyed by alias at module
|
|
6
|
+
* scope, so a maker that runs twice (a target's maker wrapping this one, a spec) re-declaring an
|
|
7
|
+
* index would make the compiler warn about a duplicate on every boot.
|
|
8
|
+
*/
|
|
9
|
+
const declareIndexes = (resource, alias, indexes) => {
|
|
10
|
+
const table = pgIdentifier(alias);
|
|
11
|
+
const declared = getDeclaration(alias).indexes;
|
|
12
|
+
for (const [suffix, spec] of indexes) {
|
|
13
|
+
const name = `${table}_${suffix}`;
|
|
14
|
+
if (!declared.some(index => index.name === name)) {
|
|
15
|
+
resource.index(name, spec);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
/** The index names a table carries, by suffix — what the store matches a unique violation on. */
|
|
20
|
+
export const planningIndexName = (alias, suffix) => `${pgIdentifier(alias)}_${suffix}`;
|
|
21
|
+
/**
|
|
22
|
+
* `planning-card` — projects, cards and specifications in ONE table, routed by `kind` at the query
|
|
23
|
+
* layer. Indexes follow the reads: a list by kind and type, children in order, membership
|
|
24
|
+
* (`parents`, GIN), a board by status, the intrinsic state by recency, labels (GIN), a code within
|
|
25
|
+
* its parent, and a slot's documents.
|
|
26
|
+
*/
|
|
27
|
+
export const makePlanningCardResource = (alias = RES_PLANNING_CARD, dbAlias, serviceAlias) => {
|
|
28
|
+
const resource = makePostgresResource(alias, dbAlias, serviceAlias);
|
|
29
|
+
resource.schema = PlanningCardTableSchema;
|
|
30
|
+
declareIndexes(resource, alias, [
|
|
31
|
+
['entity_kind_type', { columns: ['entityId', 'kind', 'type'] }],
|
|
32
|
+
['parent_order', { columns: ['parent', 'order'] }],
|
|
33
|
+
['parents', { columns: ['parents'], method: PgIndexMethod.Gin }],
|
|
34
|
+
['parent_status', { columns: ['parent', 'status'] }],
|
|
35
|
+
['entity_intrinsic', { columns: ['entityId', 'intrinsic', 'updatedAt'] }],
|
|
36
|
+
['labels', { columns: ['labels'], method: PgIndexMethod.Gin }],
|
|
37
|
+
['code', { columns: ['entityId', 'parent', 'code'], where: '"code" IS NOT NULL' }],
|
|
38
|
+
['slot', { columns: ['parent', 'category'], where: `"kind" = 'specification'` }],
|
|
39
|
+
]);
|
|
40
|
+
return resource;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* `planning-transition` — the append-only log. `(card, seq)` is the arbiter of allocation, a key is
|
|
44
|
+
* unique per organization, a project's history reads by time, and the pending rows by age (what
|
|
45
|
+
* `recover()` scans).
|
|
46
|
+
*/
|
|
47
|
+
export const makePlanningTransitionResource = (alias = RES_PLANNING_TRANSITION, dbAlias, serviceAlias) => {
|
|
48
|
+
const resource = makePostgresResource(alias, dbAlias, serviceAlias);
|
|
49
|
+
resource.schema = PlanningTransitionTableSchema;
|
|
50
|
+
declareIndexes(resource, alias, [
|
|
51
|
+
['card_seq', { columns: ['card', 'seq'], unique: true }],
|
|
52
|
+
['entity_key', { columns: ['entityId', 'key'], unique: true, where: '"key" IS NOT NULL' }],
|
|
53
|
+
['project_at', { columns: ['project', 'at'] }],
|
|
54
|
+
['pending', { columns: ['at'], where: `("commit"->>'state') = 'pending'` }],
|
|
55
|
+
]);
|
|
56
|
+
return resource;
|
|
57
|
+
};
|
|
58
|
+
/** `planning-link` — one row per `(from, to, type)`, read from either end, by type, and by project. */
|
|
59
|
+
export const makePlanningLinkResource = (alias = RES_PLANNING_LINK, dbAlias, serviceAlias) => {
|
|
60
|
+
const resource = makePostgresResource(alias, dbAlias, serviceAlias);
|
|
61
|
+
resource.schema = PlanningLinkTableSchema;
|
|
62
|
+
declareIndexes(resource, alias, [
|
|
63
|
+
['edge', { columns: ['from', 'to', 'type'], unique: true }],
|
|
64
|
+
['inbound', { columns: ['to', 'type'] }],
|
|
65
|
+
['entity_type', { columns: ['entityId', 'type'] }],
|
|
66
|
+
['project', { columns: ['project'] }],
|
|
67
|
+
]);
|
|
68
|
+
return resource;
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* `planning-schema` — data-defined types and flows, one row per `(organization, project layer,
|
|
72
|
+
* kind, key)`, plus each organization's private revision row (`kind: 'head'`).
|
|
73
|
+
*/
|
|
74
|
+
export const makePlanningSchemaResource = (alias = RES_PLANNING_SCHEMA, dbAlias, serviceAlias) => {
|
|
75
|
+
const resource = makePostgresResource(alias, dbAlias, serviceAlias);
|
|
76
|
+
resource.schema = PlanningSchemaTableSchema;
|
|
77
|
+
declareIndexes(resource, alias, [
|
|
78
|
+
['scope', { unique: true, expression: `"entityId", COALESCE("project", ''), "kind", "key"` }],
|
|
79
|
+
['rev', { columns: ['entityId', 'rev'] }],
|
|
80
|
+
]);
|
|
81
|
+
return resource;
|
|
82
|
+
};
|
|
83
|
+
export const makePlanningCardPostgres = (dbAlias, serviceAlias) => makePlanningCardResource(RES_PLANNING_CARD, dbAlias, serviceAlias);
|
|
84
|
+
export const makePlanningTransitionPostgres = (dbAlias, serviceAlias) => makePlanningTransitionResource(RES_PLANNING_TRANSITION, dbAlias, serviceAlias);
|
|
85
|
+
export const makePlanningLinkPostgres = (dbAlias, serviceAlias) => makePlanningLinkResource(RES_PLANNING_LINK, dbAlias, serviceAlias);
|
|
86
|
+
export const makePlanningSchemaPostgres = (dbAlias, serviceAlias) => makePlanningSchemaResource(RES_PLANNING_SCHEMA, dbAlias, serviceAlias);
|
|
87
|
+
/** The four resources under the aliases a store resolves — what `appendPostgresPlanning` registers. */
|
|
88
|
+
export const makePlanningPostgresResources = (aliases, dbAlias, serviceAlias) => [
|
|
89
|
+
makePlanningCardResource(aliases.card, dbAlias, serviceAlias),
|
|
90
|
+
makePlanningTransitionResource(aliases.transition, dbAlias, serviceAlias),
|
|
91
|
+
makePlanningLinkResource(aliases.link, dbAlias, serviceAlias),
|
|
92
|
+
makePlanningSchemaResource(aliases.schema, dbAlias, serviceAlias),
|
|
93
|
+
];
|
|
94
|
+
//# sourceMappingURL=resource.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resource.js","sourceRoot":"","sources":["../src/resource.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAA;AAG/G,OAAO,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAA;AAChH,OAAO,EACL,uBAAuB,EAAE,uBAAuB,EAAE,yBAAyB,EAAE,6BAA6B,GAC3G,MAAM,cAAc,CAAA;AAQrB;;;;GAIG;AACH,MAAM,cAAc,GAAG,CAA2B,QAA6B,EAAE,KAAa,EAAE,OAAkB,EAAQ,EAAE;IAC1H,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,CAAA;IACjC,MAAM,QAAQ,GAAG,cAAc,CAAC,KAAK,CAAC,CAAC,OAAO,CAAA;IAC9C,KAAK,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,GAAG,GAAG,KAAK,IAAI,MAAM,EAAE,CAAA;QACjC,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,CAAC;YACjD,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC5B,CAAC;IACH,CAAC;AACH,CAAC,CAAA;AAED,iGAAiG;AACjG,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,KAAa,EAAE,MAAc,EAAU,EAAE,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,IAAI,MAAM,EAAE,CAAA;AAE9G;;;;;GAKG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,KAAK,GAAW,iBAAiB,EAAE,OAAgB,EAAE,YAAqB,EACpD,EAAE;IACxB,MAAM,QAAQ,GAAG,oBAAoB,CAA2C,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;IAC7G,QAAQ,CAAC,MAAM,GAAG,uBAAuB,CAAA;IACzC,cAAc,CAAC,QAAQ,EAAE,KAAK,EAAE;QAC9B,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,CAAC;QAC/D,CAAC,cAAc,EAAE,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAAC;QAClD,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,CAAC;QAChE,CAAC,eAAe,EAAE,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;QACpD,CAAC,kBAAkB,EAAE,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,WAAW,EAAE,WAAW,CAAC,EAAE,CAAC;QACzE,CAAC,QAAQ,EAAE,EAAE,OAAO,EAAE,CAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,GAAG,EAAE,CAAC;QAC9D,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,MAAM,CAAC,EAAE,KAAK,EAAE,oBAAoB,EAAE,CAAC;QAClF,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE,0BAA0B,EAAE,CAAC;KACjF,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED;;;;GAIG;AACH,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAC5C,KAAK,GAAW,uBAAuB,EAAE,OAAgB,EAAE,YAAqB,EACpD,EAAE;IAC9B,MAAM,QAAQ,GAAG,oBAAoB,CAAyC,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;IAC3G,QAAQ,CAAC,MAAM,GAAG,6BAA6B,CAAA;IAC/C,cAAc,CAAC,QAAQ,EAAE,KAAK,EAAE;QAC9B,CAAC,UAAU,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACxD,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC;QAC1F,CAAC,YAAY,EAAE,EAAE,OAAO,EAAE,CAAC,SAAS,EAAE,IAAI,CAAC,EAAE,CAAC;QAC9C,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,CAAC;KAC5E,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,uGAAuG;AACvG,MAAM,CAAC,MAAM,wBAAwB,GAAG,CACtC,KAAK,GAAW,iBAAiB,EAAE,OAAgB,EAAE,YAAqB,EACpD,EAAE;IACxB,MAAM,QAAQ,GAAG,oBAAoB,CAAqC,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;IACvG,QAAQ,CAAC,MAAM,GAAG,uBAAuB,CAAA;IACzC,cAAc,CAAC,QAAQ,EAAE,KAAK,EAAE;QAC9B,CAAC,MAAM,EAAE,EAAE,OAAO,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QAC3D,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,CAAC;QACxC,CAAC,aAAa,EAAE,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,MAAM,CAAC,EAAE,CAAC;QAClD,CAAC,SAAS,EAAE,EAAE,OAAO,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC;KACtC,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,0BAA0B,GAAG,CACxC,KAAK,GAAW,mBAAmB,EAAE,OAAgB,EAAE,YAAqB,EACpD,EAAE;IAC1B,MAAM,QAAQ,GAAG,oBAAoB,CAA4C,KAAK,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;IAC9G,QAAQ,CAAC,MAAM,GAAG,yBAAyB,CAAA;IAC3C,cAAc,CAAC,QAAQ,EAAE,KAAK,EAAE;QAC9B,CAAC,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,EAAE,oDAAoD,EAAE,CAAC;QAC7F,CAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,UAAU,EAAE,KAAK,CAAC,EAAE,CAAC;KAC1C,CAAC,CAAA;IAEF,OAAO,QAAQ,CAAA;AACjB,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,wBAAwB,GACnC,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,wBAAwB,CAAC,iBAAiB,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;AAE/F,MAAM,CAAC,MAAM,8BAA8B,GACzC,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,8BAA8B,CAAC,uBAAuB,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;AAE3G,MAAM,CAAC,MAAM,wBAAwB,GACnC,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,wBAAwB,CAAC,iBAAiB,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;AAE/F,MAAM,CAAC,MAAM,0BAA0B,GACrC,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,CAAC,0BAA0B,CAAC,mBAAmB,EAAE,OAAO,EAAE,YAAY,CAAC,CAAA;AAEnG,uGAAuG;AACvG,MAAM,CAAC,MAAM,6BAA6B,GAAG,CAC3C,OAAgC,EAAE,OAAgB,EAAE,YAAqB,EACyB,EAAE,CAAC;IACrG,wBAAwB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC;IAC7D,8BAA8B,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,EAAE,YAAY,CAAC;IACzE,wBAAwB,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,CAAC;IAC7D,0BAA0B,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,EAAE,YAAY,CAAC;CAClE,CAAA"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Relationship, Transition } from '@owlmeans/planning';
|
|
2
|
+
import type { JSONSchemaType } from 'ajv';
|
|
3
|
+
import type { PlanningCardRecord, PlanningSchemaRow } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* `planning-card`: projects, cards and specifications in one table — derived from
|
|
6
|
+
* `AnyWorkcardSchema`, plus the private `headAt` column. `seq`/`head`/`revision`/`bodyChars` are
|
|
7
|
+
* `integer`, `body` is `text`, every timestamp is `text`.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PlanningCardTableSchema: JSONSchemaType<PlanningCardRecord>;
|
|
10
|
+
/** `planning-transition`: the append-only log, derived from `TransitionSchema`. */
|
|
11
|
+
export declare const PlanningTransitionTableSchema: JSONSchemaType<Transition>;
|
|
12
|
+
/** `planning-link`: typed edges, derived from `RelationshipSchema`. */
|
|
13
|
+
export declare const PlanningLinkTableSchema: JSONSchemaType<Relationship>;
|
|
14
|
+
/**
|
|
15
|
+
* `planning-schema`: data-defined types and flows, derived from `ScopedSchemaRecordSchema`. `kind`
|
|
16
|
+
* also admits the private `head` row an organization's revision is counted in; `project` is `text`
|
|
17
|
+
* so the uniqueness expression `COALESCE("project", '')` stays one type.
|
|
18
|
+
*/
|
|
19
|
+
export declare const PlanningSchemaTableSchema: JSONSchemaType<PlanningSchemaRow>;
|
|
20
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,KAAK,CAAA;AAEzC,OAAO,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AA2BvE;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,oCAWH,CAAA;AAIjC,mFAAmF;AACnF,eAAO,MAAM,6BAA6B,4BAIV,CAAA;AAIhC,uEAAuE;AACvE,eAAO,MAAM,uBAAuB,8BAGF,CAAA;AAIlC;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,mCAQE,CAAA"}
|
package/build/schemas.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { AnyWorkcardSchema, PlanningSchemaKind, RelationshipSchema, ScopedSchemaRecordSchema, TransitionSchema, } from '@owlmeans/planning';
|
|
2
|
+
import { SCHEMA_HEAD_KIND } from './consts.js';
|
|
3
|
+
const propertiesOf = (schema) => structuredClone(schema.properties);
|
|
4
|
+
const requiredOf = (schema) => [...(schema.required ?? [])];
|
|
5
|
+
/**
|
|
6
|
+
* An ISO-8601 timestamp stays TEXT. The record's `format: 'date-time'` would compile to
|
|
7
|
+
* `timestamptz` and marshal through `Date` — which changes the string a record carries and breaks
|
|
8
|
+
* the lexicographic order every `updatedSince`, `at` sort and gap age relies on.
|
|
9
|
+
*/
|
|
10
|
+
const timestamp = (property) => {
|
|
11
|
+
const { format: _format, ...rest } = property;
|
|
12
|
+
return { ...rest, pg: { type: 'text' } };
|
|
13
|
+
};
|
|
14
|
+
const integer = (property) => ({ ...property, pg: { type: 'integer' } });
|
|
15
|
+
const tableSchema = (properties, required) => ({
|
|
16
|
+
type: 'object', properties, required, additionalProperties: false,
|
|
17
|
+
});
|
|
18
|
+
const cardProperties = propertiesOf(AnyWorkcardSchema);
|
|
19
|
+
/**
|
|
20
|
+
* `planning-card`: projects, cards and specifications in one table — derived from
|
|
21
|
+
* `AnyWorkcardSchema`, plus the private `headAt` column. `seq`/`head`/`revision`/`bodyChars` are
|
|
22
|
+
* `integer`, `body` is `text`, every timestamp is `text`.
|
|
23
|
+
*/
|
|
24
|
+
export const PlanningCardTableSchema = tableSchema({
|
|
25
|
+
...cardProperties,
|
|
26
|
+
createdAt: timestamp(cardProperties.createdAt),
|
|
27
|
+
updatedAt: timestamp(cardProperties.updatedAt),
|
|
28
|
+
closedAt: timestamp(cardProperties.closedAt),
|
|
29
|
+
seq: integer(cardProperties.seq),
|
|
30
|
+
head: integer(cardProperties.head),
|
|
31
|
+
revision: integer(cardProperties.revision),
|
|
32
|
+
bodyChars: integer(cardProperties.bodyChars),
|
|
33
|
+
body: { ...cardProperties.body, pg: { type: 'text' } },
|
|
34
|
+
headAt: { type: 'string', nullable: true, pg: { type: 'text' } },
|
|
35
|
+
}, requiredOf(AnyWorkcardSchema));
|
|
36
|
+
const transitionProperties = propertiesOf(TransitionSchema);
|
|
37
|
+
/** `planning-transition`: the append-only log, derived from `TransitionSchema`. */
|
|
38
|
+
export const PlanningTransitionTableSchema = tableSchema({
|
|
39
|
+
...transitionProperties,
|
|
40
|
+
seq: integer(transitionProperties.seq),
|
|
41
|
+
at: timestamp(transitionProperties.at),
|
|
42
|
+
}, requiredOf(TransitionSchema));
|
|
43
|
+
const linkProperties = propertiesOf(RelationshipSchema);
|
|
44
|
+
/** `planning-link`: typed edges, derived from `RelationshipSchema`. */
|
|
45
|
+
export const PlanningLinkTableSchema = tableSchema({
|
|
46
|
+
...linkProperties,
|
|
47
|
+
createdAt: timestamp(linkProperties.createdAt),
|
|
48
|
+
}, requiredOf(RelationshipSchema));
|
|
49
|
+
const schemaProperties = propertiesOf(ScopedSchemaRecordSchema);
|
|
50
|
+
/**
|
|
51
|
+
* `planning-schema`: data-defined types and flows, derived from `ScopedSchemaRecordSchema`. `kind`
|
|
52
|
+
* also admits the private `head` row an organization's revision is counted in; `project` is `text`
|
|
53
|
+
* so the uniqueness expression `COALESCE("project", '')` stays one type.
|
|
54
|
+
*/
|
|
55
|
+
export const PlanningSchemaTableSchema = tableSchema({
|
|
56
|
+
...schemaProperties,
|
|
57
|
+
kind: { type: 'string', enum: [...Object.values(PlanningSchemaKind), SCHEMA_HEAD_KIND] },
|
|
58
|
+
project: { ...schemaProperties.project, pg: { type: 'text' } },
|
|
59
|
+
version: integer(schemaProperties.version),
|
|
60
|
+
rev: integer(schemaProperties.rev),
|
|
61
|
+
createdAt: timestamp(schemaProperties.createdAt),
|
|
62
|
+
updatedAt: timestamp(schemaProperties.updatedAt),
|
|
63
|
+
}, requiredOf(ScopedSchemaRecordSchema));
|
|
64
|
+
//# sourceMappingURL=schemas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.js","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,iBAAiB,EAAE,kBAAkB,EAAE,kBAAkB,EAAE,wBAAwB,EAAE,gBAAgB,GACtG,MAAM,oBAAoB,CAAA;AAG3B,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAK9C,MAAM,YAAY,GAAG,CAAC,MAAc,EAAc,EAAE,CAClD,eAAe,CAAE,MAAqC,CAAC,UAAU,CAAC,CAAA;AAEpE,MAAM,UAAU,GAAG,CAAC,MAAc,EAAY,EAAE,CAAC,CAAC,GAAG,CAAE,MAAkC,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,CAAA;AAE1G;;;;GAIG;AACH,MAAM,SAAS,GAAG,CAAC,QAAiC,EAA2B,EAAE;IAC/E,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,IAAI,EAAE,GAAG,QAAQ,CAAA;IAC7C,OAAO,EAAE,GAAG,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,CAAA;AAC1C,CAAC,CAAA;AAED,MAAM,OAAO,GAAG,CAAC,QAAiC,EAA2B,EAAE,CAAC,CAAC,EAAE,GAAG,QAAQ,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,CAAC,CAAA;AAE1H,MAAM,WAAW,GAAG,CAAI,UAAsB,EAAE,QAAkB,EAAqB,EAAE,CAAC,CAAC;IACzF,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,QAAQ,EAAE,oBAAoB,EAAE,KAAK;CAClE,CAAiC,CAAA;AAElC,MAAM,cAAc,GAAG,YAAY,CAAC,iBAAiB,CAAC,CAAA;AAEtD;;;;GAIG;AACH,MAAM,CAAC,MAAM,uBAAuB,GAAG,WAAW,CAAqB;IACrE,GAAG,cAAc;IACjB,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC;IAC9C,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC;IAC9C,QAAQ,EAAE,SAAS,CAAC,cAAc,CAAC,QAAQ,CAAC;IAC5C,GAAG,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,CAAC;IAChC,IAAI,EAAE,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC;IAClC,QAAQ,EAAE,OAAO,CAAC,cAAc,CAAC,QAAQ,CAAC;IAC1C,SAAS,EAAE,OAAO,CAAC,cAAc,CAAC,SAAS,CAAC;IAC5C,IAAI,EAAE,EAAE,GAAG,cAAc,CAAC,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IACtD,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;CACjE,EAAE,UAAU,CAAC,iBAAiB,CAAC,CAAC,CAAA;AAEjC,MAAM,oBAAoB,GAAG,YAAY,CAAC,gBAAgB,CAAC,CAAA;AAE3D,mFAAmF;AACnF,MAAM,CAAC,MAAM,6BAA6B,GAAG,WAAW,CAAa;IACnE,GAAG,oBAAoB;IACvB,GAAG,EAAE,OAAO,CAAC,oBAAoB,CAAC,GAAG,CAAC;IACtC,EAAE,EAAE,SAAS,CAAC,oBAAoB,CAAC,EAAE,CAAC;CACvC,EAAE,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAA;AAEhC,MAAM,cAAc,GAAG,YAAY,CAAC,kBAAkB,CAAC,CAAA;AAEvD,uEAAuE;AACvE,MAAM,CAAC,MAAM,uBAAuB,GAAG,WAAW,CAAe;IAC/D,GAAG,cAAc;IACjB,SAAS,EAAE,SAAS,CAAC,cAAc,CAAC,SAAS,CAAC;CAC/C,EAAE,UAAU,CAAC,kBAAkB,CAAC,CAAC,CAAA;AAElC,MAAM,gBAAgB,GAAG,YAAY,CAAC,wBAAwB,CAAC,CAAA;AAE/D;;;;GAIG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAAG,WAAW,CAAoB;IACtE,GAAG,gBAAgB;IACnB,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,CAAC,EAAE;IACxF,OAAO,EAAE,EAAE,GAAG,gBAAgB,CAAC,OAAO,EAAE,EAAE,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE;IAC9D,OAAO,EAAE,OAAO,CAAC,gBAAgB,CAAC,OAAO,CAAC;IAC1C,GAAG,EAAE,OAAO,CAAC,gBAAgB,CAAC,GAAG,CAAC;IAClC,SAAS,EAAE,SAAS,CAAC,gBAAgB,CAAC,SAAS,CAAC;IAChD,SAAS,EAAE,SAAS,CAAC,gBAAgB,CAAC,SAAS,CAAC;CACjD,EAAE,UAAU,CAAC,wBAAwB,CAAC,CAAC,CAAA"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { BasicConfig, BasicContext } from '@owlmeans/context';
|
|
2
|
+
import type { WithPlanningService } from '@owlmeans/planning';
|
|
3
|
+
import type { PlanningHostService } from '@owlmeans/server-planning';
|
|
4
|
+
import type { PostgresPlanningServiceOptions } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* The planning service over a Postgres store — what a target's own `services/planning.ts`
|
|
7
|
+
* exports as its `makeService()`. The store resolves its four resources on the service's context
|
|
8
|
+
* at the first call, so the resources are registered separately (`resources/planning/*.ts`).
|
|
9
|
+
*/
|
|
10
|
+
export declare const makePostgresPlanningService: (opts?: PostgresPlanningServiceOptions, alias?: string) => PlanningHostService;
|
|
11
|
+
/**
|
|
12
|
+
* Register the four Postgres resources (each unless the context already has it), the planning
|
|
13
|
+
* service over them, and `context.planning()` — the one-call wiring of a hand-written backend.
|
|
14
|
+
*/
|
|
15
|
+
export declare const appendPostgresPlanning: <C extends BasicConfig, T extends BasicContext<C>>(ctx: T, opts?: PostgresPlanningServiceOptions, alias?: string) => T & WithPlanningService;
|
|
16
|
+
//# sourceMappingURL=service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.d.ts","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAA;AAClE,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AAE7D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AAGpE,OAAO,KAAK,EAAE,8BAA8B,EAAE,MAAM,YAAY,CAAA;AAEhE;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,UAChC,8BAA8B,UAAc,MAAM,KACvD,mBAUF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,sBAAsB,GAAI,CAAC,SAAS,WAAW,EAAE,CAAC,SAAS,YAAY,CAAC,CAAC,CAAC,OAChF,CAAC,SAAQ,8BAA8B,UAAc,MAAM,KAC/D,CAAC,GAAG,mBAWN,CAAA"}
|
package/build/service.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { DEFAULT_ALIAS, makePlanningService } from '@owlmeans/server-planning';
|
|
2
|
+
import { makePlanningPostgresResources } from './resource.js';
|
|
3
|
+
import { makePostgresPlanningStore, planningPostgresAliases } from './store/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* The planning service over a Postgres store — what a target's own `services/planning.ts`
|
|
6
|
+
* exports as its `makeService()`. The store resolves its four resources on the service's context
|
|
7
|
+
* at the first call, so the resources are registered separately (`resources/planning/*.ts`).
|
|
8
|
+
*/
|
|
9
|
+
export const makePostgresPlanningService = (opts = {}, alias = DEFAULT_ALIAS) => {
|
|
10
|
+
const { aliases, bus, limits, ...service } = opts;
|
|
11
|
+
let host;
|
|
12
|
+
const store = makePostgresPlanningStore({
|
|
13
|
+
aliases, bus, limits, ids: opts.ids, now: opts.now,
|
|
14
|
+
context: () => host?.ctx,
|
|
15
|
+
});
|
|
16
|
+
host = makePlanningService({ ...service, store }, alias);
|
|
17
|
+
return host;
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Register the four Postgres resources (each unless the context already has it), the planning
|
|
21
|
+
* service over them, and `context.planning()` — the one-call wiring of a hand-written backend.
|
|
22
|
+
*/
|
|
23
|
+
export const appendPostgresPlanning = (ctx, opts = {}, alias = DEFAULT_ALIAS) => {
|
|
24
|
+
const context = ctx;
|
|
25
|
+
for (const resource of makePlanningPostgresResources(planningPostgresAliases(opts.aliases))) {
|
|
26
|
+
if (!context.hasResource(resource.alias)) {
|
|
27
|
+
context.registerResource(resource);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
context.registerService(makePostgresPlanningService(opts, alias));
|
|
31
|
+
context.planning = () => context.service(alias);
|
|
32
|
+
return context;
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"service.js","sourceRoot":"","sources":["../src/service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAA;AAE9E,OAAO,EAAE,6BAA6B,EAAE,MAAM,eAAe,CAAA;AAC7D,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAA;AAGrF;;;;GAIG;AACH,MAAM,CAAC,MAAM,2BAA2B,GAAG,CACzC,IAAI,GAAmC,EAAE,EAAE,KAAK,GAAW,aAAa,EACnD,EAAE;IACvB,MAAM,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,GAAG,IAAI,CAAA;IACjD,IAAI,IAAqC,CAAA;IACzC,MAAM,KAAK,GAAG,yBAAyB,CAAC;QACtC,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,IAAI,CAAC,GAAG;QAClD,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI,EAAE,GAAoC;KAC1D,CAAC,CAAA;IACF,IAAI,GAAG,mBAAmB,CAAC,EAAE,GAAG,OAAO,EAAE,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;IAExD,OAAO,IAAI,CAAA;AACb,CAAC,CAAA;AAED;;;GAGG;AACH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CACpC,GAAM,EAAE,IAAI,GAAmC,EAAE,EAAE,KAAK,GAAW,aAAa,EACvD,EAAE;IAC3B,MAAM,OAAO,GAAG,GAA8B,CAAA;IAC9C,KAAK,MAAM,QAAQ,IAAI,6BAA6B,CAAC,uBAAuB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;QAC5F,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACzC,OAAO,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAA;QACpC,CAAC;IACH,CAAC;IACD,OAAO,CAAC,eAAe,CAAC,2BAA2B,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAA;IACjE,OAAO,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,OAAO,CAAsB,KAAK,CAAC,CAAA;IAEpE,OAAO,OAAO,CAAA;AAChB,CAAC,CAAA"}
|
package/build/sql.d.ts
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { ColumnSpec, TableSpec } from '@owlmeans/postgres-resource';
|
|
2
|
+
import type { ResourceRecord } from '@owlmeans/resource';
|
|
3
|
+
import type { Pool, PoolClient, QueryResultRow } from 'pg';
|
|
4
|
+
/** Where a statement runs — the pool, or one open transaction. */
|
|
5
|
+
export interface SqlRunner {
|
|
6
|
+
query: <R extends QueryResultRow = QueryResultRow>(text: string, params?: unknown[]) => Promise<R[]>;
|
|
7
|
+
}
|
|
8
|
+
/** The compiled specs of the four tables, read once the resources have initialized. */
|
|
9
|
+
export interface PlanningTables {
|
|
10
|
+
card: TableSpec;
|
|
11
|
+
transition: TableSpec;
|
|
12
|
+
link: TableSpec;
|
|
13
|
+
schema: TableSpec;
|
|
14
|
+
}
|
|
15
|
+
/** A runner and the tables it addresses — what every statement builder takes. */
|
|
16
|
+
export interface SqlContext {
|
|
17
|
+
runner: SqlRunner;
|
|
18
|
+
tables: PlanningTables;
|
|
19
|
+
}
|
|
20
|
+
export declare const poolRunner: (pool: Pool) => SqlRunner;
|
|
21
|
+
export declare const clientRunner: (client: PoolClient, onError?: (error: unknown) => void) => SqlRunner;
|
|
22
|
+
/** A property's physical column, quoted. */
|
|
23
|
+
export declare const col: (spec: TableSpec, property: string) => string;
|
|
24
|
+
/** A value as the driver takes it for a column: JSON text for jsonb, `null` for absent. */
|
|
25
|
+
export declare const driverValue: (column: ColumnSpec, value: unknown) => unknown;
|
|
26
|
+
/**
|
|
27
|
+
* A row back as a record: physical columns to properties with the package's coercions, `null`
|
|
28
|
+
* dropped (the planning records leave an absent value out), private columns removed.
|
|
29
|
+
*/
|
|
30
|
+
export declare const recordOf: <T extends ResourceRecord>(row: Record<string, unknown>, spec: TableSpec, hidden?: readonly string[]) => T;
|
|
31
|
+
/** A resource read answered with drizzle's property-keyed rows: `null` and private keys dropped. */
|
|
32
|
+
export declare const cleanRecord: <T extends object>(record: T, hidden?: readonly string[]) => T;
|
|
33
|
+
export interface InsertStatement {
|
|
34
|
+
text: string;
|
|
35
|
+
params: unknown[];
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* `INSERT INTO <table> (<every column but the skipped>) VALUES (…)` for a record, with an optional
|
|
39
|
+
* tail (`ON CONFLICT …`, `RETURNING …`).
|
|
40
|
+
*/
|
|
41
|
+
export declare const insertOf: (spec: TableSpec, record: Record<string, unknown>, opts?: {
|
|
42
|
+
skip?: readonly string[];
|
|
43
|
+
tail?: string;
|
|
44
|
+
as?: string;
|
|
45
|
+
}) => InsertStatement;
|
|
46
|
+
/**
|
|
47
|
+
* A WHERE over fixed fields: a string is equality, a list is `= ANY`, `undefined` is skipped.
|
|
48
|
+
* Parameters start after `offset`.
|
|
49
|
+
*/
|
|
50
|
+
export declare const whereOf: (spec: TableSpec, fields: Record<string, string | readonly string[] | undefined>, offset?: number) => {
|
|
51
|
+
text: string;
|
|
52
|
+
params: unknown[];
|
|
53
|
+
};
|
|
54
|
+
/** The driver error's Postgres code and the constraint (index) it names, through a `cause` chain. */
|
|
55
|
+
export declare const pgFault: (error: unknown) => {
|
|
56
|
+
code?: string;
|
|
57
|
+
constraint?: string;
|
|
58
|
+
};
|
|
59
|
+
export declare const UNIQUE_VIOLATION = "23505";
|
|
60
|
+
export declare const LOCK_NOT_AVAILABLE = "55P03";
|
|
61
|
+
//# sourceMappingURL=sql.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,6BAA6B,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,IAAI,CAAA;AAE1D,kEAAkE;AAClE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,CAAC,EAAE,CAAC,CAAA;CACrG;AAED,uFAAuF;AACvF,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,SAAS,CAAA;IACf,UAAU,EAAE,SAAS,CAAA;IACrB,IAAI,EAAE,SAAS,CAAA;IACf,MAAM,EAAE,SAAS,CAAA;CAClB;AAED,iFAAiF;AACjF,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,SAAS,CAAA;IACjB,MAAM,EAAE,cAAc,CAAA;CACvB;AAED,eAAO,MAAM,UAAU,SAAU,IAAI,KAAG,SAGtC,CAAA;AAEF,eAAO,MAAM,YAAY,WAAY,UAAU,YAAY,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,KAAG,SASpF,CAAA;AAEF,4CAA4C;AAC5C,eAAO,MAAM,GAAG,SAAU,SAAS,YAAY,MAAM,KAAG,MAAsD,CAAA;AAE9G,2FAA2F;AAC3F,eAAO,MAAM,WAAW,WAAY,UAAU,SAAS,OAAO,KAAG,OAMhE,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,GAAI,CAAC,SAAS,cAAc,OAC1C,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,QAAQ,SAAS,WAAU,SAAS,MAAM,EAAE,KACvE,CAQF,CAAA;AAED,oGAAoG;AACpG,eAAO,MAAM,WAAW,GAAI,CAAC,SAAS,MAAM,UAAU,CAAC,WAAU,SAAS,MAAM,EAAE,KAAQ,CACgD,CAAA;AAE1I,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,OAAO,EAAE,CAAA;CAClB;AAED;;;GAGG;AACH,eAAO,MAAM,QAAQ,SACb,SAAS,UAAU,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,SAAQ;IAAE,IAAI,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,EAAE,CAAC,EAAE,MAAM,CAAA;CAAE,KAC/G,eAUF,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO,SACZ,SAAS,UAAU,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC,WAAU,MAAM,KAC9F;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,OAAO,EAAE,CAAA;CAcnC,CAAA;AAED,qGAAqG;AACrG,eAAO,MAAM,OAAO,UAAW,OAAO,KAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAa5E,CAAA;AAED,eAAO,MAAM,gBAAgB,UAAU,CAAA;AACvC,eAAO,MAAM,kBAAkB,UAAU,CAAA"}
|
package/build/sql.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { quoteIdent, rowToRecord } from '@owlmeans/postgres-resource';
|
|
2
|
+
export const poolRunner = (pool) => ({
|
|
3
|
+
query: async (text, params) => (await pool.query(text, params)).rows,
|
|
4
|
+
});
|
|
5
|
+
export const clientRunner = (client, onError) => ({
|
|
6
|
+
query: async (text, params) => {
|
|
7
|
+
try {
|
|
8
|
+
return (await client.query(text, params)).rows;
|
|
9
|
+
}
|
|
10
|
+
catch (error) {
|
|
11
|
+
onError?.(error);
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
});
|
|
16
|
+
/** A property's physical column, quoted. */
|
|
17
|
+
export const col = (spec, property) => quoteIdent(spec.byProperty[property].column);
|
|
18
|
+
/** A value as the driver takes it for a column: JSON text for jsonb, `null` for absent. */
|
|
19
|
+
export const driverValue = (column, value) => {
|
|
20
|
+
if (value === undefined || value === null) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
// node-postgres would write a JS array as a Postgres array literal, which a jsonb column refuses.
|
|
24
|
+
return column.jsonb ? JSON.stringify(value) : value;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* A row back as a record: physical columns to properties with the package's coercions, `null`
|
|
28
|
+
* dropped (the planning records leave an absent value out), private columns removed.
|
|
29
|
+
*/
|
|
30
|
+
export const recordOf = (row, spec, hidden = []) => {
|
|
31
|
+
const record = rowToRecord(row, spec);
|
|
32
|
+
for (const [key, value] of Object.entries(record)) {
|
|
33
|
+
if (value === null || hidden.includes(key)) {
|
|
34
|
+
delete record[key];
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return record;
|
|
38
|
+
};
|
|
39
|
+
/** A resource read answered with drizzle's property-keyed rows: `null` and private keys dropped. */
|
|
40
|
+
export const cleanRecord = (record, hidden = []) => Object.fromEntries(Object.entries(record).filter(([key, value]) => value !== null && value !== undefined && !hidden.includes(key)));
|
|
41
|
+
/**
|
|
42
|
+
* `INSERT INTO <table> (<every column but the skipped>) VALUES (…)` for a record, with an optional
|
|
43
|
+
* tail (`ON CONFLICT …`, `RETURNING …`).
|
|
44
|
+
*/
|
|
45
|
+
export const insertOf = (spec, record, opts = {}) => {
|
|
46
|
+
const columns = spec.columns.filter(column => !(opts.skip ?? []).includes(column.property));
|
|
47
|
+
const params = columns.map(column => driverValue(column, record[column.property]));
|
|
48
|
+
return {
|
|
49
|
+
text: `INSERT INTO ${spec.qualified}${opts.as != null ? ` AS ${opts.as}` : ''}`
|
|
50
|
+
+ ` (${columns.map(column => quoteIdent(column.column)).join(', ')})`
|
|
51
|
+
+ ` VALUES (${columns.map((_, index) => `$${index + 1}`).join(', ')})${opts.tail != null ? ` ${opts.tail}` : ''}`,
|
|
52
|
+
params,
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
/**
|
|
56
|
+
* A WHERE over fixed fields: a string is equality, a list is `= ANY`, `undefined` is skipped.
|
|
57
|
+
* Parameters start after `offset`.
|
|
58
|
+
*/
|
|
59
|
+
export const whereOf = (spec, fields, offset = 0) => {
|
|
60
|
+
const clauses = [];
|
|
61
|
+
const params = [];
|
|
62
|
+
for (const [property, value] of Object.entries(fields)) {
|
|
63
|
+
if (value === undefined) {
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
params.push(Array.isArray(value) ? [...value] : value);
|
|
67
|
+
clauses.push(Array.isArray(value)
|
|
68
|
+
? `${col(spec, property)} = ANY($${offset + params.length})`
|
|
69
|
+
: `${col(spec, property)} = $${offset + params.length}`);
|
|
70
|
+
}
|
|
71
|
+
return { text: clauses.length > 0 ? clauses.join(' AND ') : 'TRUE', params };
|
|
72
|
+
};
|
|
73
|
+
/** The driver error's Postgres code and the constraint (index) it names, through a `cause` chain. */
|
|
74
|
+
export const pgFault = (error) => {
|
|
75
|
+
let current = error;
|
|
76
|
+
for (let depth = 0; depth < 5 && current != null && typeof current === 'object'; depth++) {
|
|
77
|
+
const candidate = current;
|
|
78
|
+
if (typeof candidate.code === 'string' && /^[0-9A-Z]{5}$/.test(candidate.code)) {
|
|
79
|
+
return {
|
|
80
|
+
code: candidate.code,
|
|
81
|
+
...(typeof candidate.constraint === 'string' ? { constraint: candidate.constraint } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
current = candidate.cause;
|
|
85
|
+
}
|
|
86
|
+
return {};
|
|
87
|
+
};
|
|
88
|
+
export const UNIQUE_VIOLATION = '23505';
|
|
89
|
+
export const LOCK_NOT_AVAILABLE = '55P03';
|
|
90
|
+
//# sourceMappingURL=sql.js.map
|
package/build/sql.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sql.js","sourceRoot":"","sources":["../src/sql.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { CommitEvent, Unsubscribe } from '@owlmeans/planning';
|
|
2
|
+
import type { Pool } from 'pg';
|
|
3
|
+
import type { SqlRunner } from '../sql.js';
|
|
4
|
+
/** A settled commit, without its record — the card is re-read where one is needed. */
|
|
5
|
+
export interface CommitFrame {
|
|
6
|
+
p: string;
|
|
7
|
+
t: 'c';
|
|
8
|
+
e: Omit<CommitEvent, 'record'>;
|
|
9
|
+
}
|
|
10
|
+
/** A write of an organization's data-defined schemas. */
|
|
11
|
+
export interface SchemaFrame {
|
|
12
|
+
p: string;
|
|
13
|
+
t: 's';
|
|
14
|
+
e: string;
|
|
15
|
+
}
|
|
16
|
+
export type BusFrame = CommitFrame | SchemaFrame;
|
|
17
|
+
export interface PlanningBus {
|
|
18
|
+
enabled: boolean;
|
|
19
|
+
/** NOTIFY a frame inside a transaction — delivered only if it commits. A no-op when disabled. */
|
|
20
|
+
notify: (runner: SqlRunner, frame: Omit<CommitFrame, 'p'> | Omit<SchemaFrame, 'p'>) => Promise<void>;
|
|
21
|
+
onCommit: (listener: (event: CommitEvent) => void | Promise<void>) => Unsubscribe;
|
|
22
|
+
onSchema: (listener: (entityId: string) => void) => Unsubscribe;
|
|
23
|
+
/** Open the LISTEN connection if it is not open (lazily, on the first subscription or wait). */
|
|
24
|
+
ensure: () => void;
|
|
25
|
+
connected: () => boolean;
|
|
26
|
+
close: () => Promise<void>;
|
|
27
|
+
}
|
|
28
|
+
export interface PlanningBusOptions {
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
processId: string;
|
|
31
|
+
/** The qualified transition table — what the channel name is derived from. */
|
|
32
|
+
table: () => Promise<string>;
|
|
33
|
+
/** The pool whose configuration the dedicated connection copies. */
|
|
34
|
+
pool: () => Promise<Pool>;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The channel of one transition table: `planning_` and a short hash of its qualified name, so two
|
|
38
|
+
* schemas in one database never hear each other.
|
|
39
|
+
*/
|
|
40
|
+
export declare const planningChannel: (qualified: string) => string;
|
|
41
|
+
/**
|
|
42
|
+
* The cross-process commit bus over LISTEN/NOTIFY.
|
|
43
|
+
*
|
|
44
|
+
* NOTIFY runs inside the transaction that settled the commit, so a frame is heard only if that
|
|
45
|
+
* commit landed. LISTEN holds ONE dedicated connection — a `pg.Client` built from the pool's own
|
|
46
|
+
* configuration, never a pooled one, so it takes no pool slot — opened on the first subscription
|
|
47
|
+
* or wait. A dropped connection reconnects with backoff (1 s doubling to 30 s); meanwhile the commit
|
|
48
|
+
* hub's poll ladder still answers every waiter. A process ignores its own frames (`p`): it delivered
|
|
49
|
+
* them itself, after its commit.
|
|
50
|
+
*/
|
|
51
|
+
export declare const makePlanningBus: (opts: PlanningBusOptions) => PlanningBus;
|
|
52
|
+
//# sourceMappingURL=bus.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bus.d.ts","sourceRoot":"","sources":["../../src/store/bus.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAGlE,OAAO,KAAK,EAA8B,IAAI,EAAE,MAAM,IAAI,CAAA;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAA;AAE1C,sFAAsF;AACtF,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,GAAG,CAAA;IACN,CAAC,EAAE,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;CAC/B;AAED,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,CAAC,EAAE,MAAM,CAAA;IACT,CAAC,EAAE,GAAG,CAAA;IACN,CAAC,EAAE,MAAM,CAAA;CACV;AAED,MAAM,MAAM,QAAQ,GAAG,WAAW,GAAG,WAAW,CAAA;AAEhD,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,OAAO,CAAA;IAChB,iGAAiG;IACjG,MAAM,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,WAAW,EAAE,GAAG,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;IACpG,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,WAAW,CAAA;IACjF,QAAQ,EAAE,CAAC,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,KAAK,WAAW,CAAA;IAC/D,gGAAgG;IAChG,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,SAAS,EAAE,MAAM,OAAO,CAAA;IACxB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,OAAO,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,8EAA8E;IAC9E,KAAK,EAAE,MAAM,OAAO,CAAC,MAAM,CAAC,CAAA;IAC5B,oEAAoE;IACpE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED;;;GAGG;AACH,eAAO,MAAM,eAAe,cAAe,MAAM,KAAG,MAGnD,CAAA;AAeD;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,SAAU,kBAAkB,KAAG,WA4I1D,CAAA"}
|