@lovrozagar/oat 0.1.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/LICENSE +21 -0
- package/README.md +401 -0
- package/bin/oat.js +2 -0
- package/dist/cli.js +386 -0
- package/dist/config/define-config.d.ts +165 -0
- package/dist/config/define-config.d.ts.map +1 -0
- package/dist/config/define-config.js +24 -0
- package/dist/config/define-config.js.map +1 -0
- package/dist/config/load.d.ts +10 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +61 -0
- package/dist/config/load.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +9 -0
- package/dist/reference/defects.d.ts +254 -0
- package/dist/reference/defects.d.ts.map +1 -0
- package/dist/reference/defects.js +265 -0
- package/dist/reference/defects.js.map +1 -0
- package/dist/reference/dialect.d.ts +104 -0
- package/dist/reference/dialect.d.ts.map +1 -0
- package/dist/reference/dialect.js +174 -0
- package/dist/reference/dialect.js.map +1 -0
- package/dist/reference/http.d.ts +79 -0
- package/dist/reference/http.d.ts.map +1 -0
- package/dist/reference/http.js +889 -0
- package/dist/reference/http.js.map +1 -0
- package/dist/reference/model.d.ts +56 -0
- package/dist/reference/model.d.ts.map +1 -0
- package/dist/reference/model.js +156 -0
- package/dist/reference/model.js.map +1 -0
- package/dist/reference/query.d.ts +45 -0
- package/dist/reference/query.d.ts.map +1 -0
- package/dist/reference/query.js +353 -0
- package/dist/reference/query.js.map +1 -0
- package/dist/reference/spec.d.ts +13 -0
- package/dist/reference/spec.d.ts.map +1 -0
- package/dist/reference/spec.js +547 -0
- package/dist/reference/spec.js.map +1 -0
- package/dist/reference/store-api.d.ts +59 -0
- package/dist/reference/store-api.d.ts.map +1 -0
- package/dist/reference/store-api.js +76 -0
- package/dist/reference/store-api.js.map +1 -0
- package/dist/reference/stores/memory.d.ts +27 -0
- package/dist/reference/stores/memory.d.ts.map +1 -0
- package/dist/reference/stores/memory.js +97 -0
- package/dist/reference/stores/memory.js.map +1 -0
- package/dist/reference/stores/postgres.d.ts +61 -0
- package/dist/reference/stores/postgres.d.ts.map +1 -0
- package/dist/reference/stores/postgres.js +406 -0
- package/dist/reference/stores/postgres.js.map +1 -0
- package/dist/reference/stores/sqlite-driver.d.ts +54 -0
- package/dist/reference/stores/sqlite-driver.d.ts.map +1 -0
- package/dist/reference/stores/sqlite-driver.js +99 -0
- package/dist/reference/stores/sqlite-driver.js.map +1 -0
- package/dist/reference/stores/sqlite.d.ts +68 -0
- package/dist/reference/stores/sqlite.d.ts.map +1 -0
- package/dist/reference/stores/sqlite.js +464 -0
- package/dist/reference/stores/sqlite.js.map +1 -0
- package/dist/report/console.d.ts +33 -0
- package/dist/report/console.d.ts.map +1 -0
- package/dist/report/console.js +237 -0
- package/dist/report/console.js.map +1 -0
- package/dist/report/matrix.d.ts +131 -0
- package/dist/report/matrix.d.ts.map +1 -0
- package/dist/report/matrix.js +865 -0
- package/dist/report/matrix.js.map +1 -0
- package/dist/report/render.d.ts +66 -0
- package/dist/report/render.d.ts.map +1 -0
- package/dist/report/render.js +423 -0
- package/dist/report/render.js.map +1 -0
- package/dist/runtime/async.d.ts +34 -0
- package/dist/runtime/async.d.ts.map +1 -0
- package/dist/runtime/async.js +130 -0
- package/dist/runtime/async.js.map +1 -0
- package/dist/runtime/auth.d.ts +46 -0
- package/dist/runtime/auth.d.ts.map +1 -0
- package/dist/runtime/auth.js +236 -0
- package/dist/runtime/auth.js.map +1 -0
- package/dist/runtime/checks.d.ts +89 -0
- package/dist/runtime/checks.d.ts.map +1 -0
- package/dist/runtime/checks.js +3400 -0
- package/dist/runtime/checks.js.map +1 -0
- package/dist/runtime/client.d.ts +65 -0
- package/dist/runtime/client.d.ts.map +1 -0
- package/dist/runtime/client.js +122 -0
- package/dist/runtime/client.js.map +1 -0
- package/dist/runtime/finding.d.ts +50 -0
- package/dist/runtime/finding.d.ts.map +1 -0
- package/dist/runtime/finding.js +50 -0
- package/dist/runtime/finding.js.map +1 -0
- package/dist/runtime/fixture.d.ts +20 -0
- package/dist/runtime/fixture.d.ts.map +1 -0
- package/dist/runtime/fixture.js +175 -0
- package/dist/runtime/fixture.js.map +1 -0
- package/dist/runtime/progress.d.ts +41 -0
- package/dist/runtime/progress.d.ts.map +1 -0
- package/dist/runtime/progress.js +186 -0
- package/dist/runtime/progress.js.map +1 -0
- package/dist/runtime/run.d.ts +69 -0
- package/dist/runtime/run.d.ts.map +1 -0
- package/dist/runtime/run.js +575 -0
- package/dist/runtime/run.js.map +1 -0
- package/dist/runtime/teardown.d.ts +38 -0
- package/dist/runtime/teardown.d.ts.map +1 -0
- package/dist/runtime/teardown.js +129 -0
- package/dist/runtime/teardown.js.map +1 -0
- package/dist/runtime/validate.d.ts +31 -0
- package/dist/runtime/validate.d.ts.map +1 -0
- package/dist/runtime/validate.js +111 -0
- package/dist/runtime/validate.js.map +1 -0
- package/dist/runtime/world.d.ts +49 -0
- package/dist/runtime/world.d.ts.map +1 -0
- package/dist/runtime/world.js +119 -0
- package/dist/runtime/world.js.map +1 -0
- package/dist/spec/collection.d.ts +31 -0
- package/dist/spec/collection.d.ts.map +1 -0
- package/dist/spec/collection.js +121 -0
- package/dist/spec/collection.js.map +1 -0
- package/dist/spec/conventions.d.ts +103 -0
- package/dist/spec/conventions.d.ts.map +1 -0
- package/dist/spec/conventions.js +275 -0
- package/dist/spec/conventions.js.map +1 -0
- package/dist/spec/extensions.d.ts +74 -0
- package/dist/spec/extensions.d.ts.map +1 -0
- package/dist/spec/extensions.js +335 -0
- package/dist/spec/extensions.js.map +1 -0
- package/dist/spec/graph.d.ts +89 -0
- package/dist/spec/graph.d.ts.map +1 -0
- package/dist/spec/graph.js +318 -0
- package/dist/spec/graph.js.map +1 -0
- package/dist/spec/load.d.ts +36 -0
- package/dist/spec/load.d.ts.map +1 -0
- package/dist/spec/load.js +166 -0
- package/dist/spec/load.js.map +1 -0
- package/dist/spec/types.d.ts +57 -0
- package/dist/spec/types.d.ts.map +1 -0
- package/dist/spec/types.js +24 -0
- package/dist/spec/types.js.map +1 -0
- package/labs/README.md +130 -0
- package/labs/annotated-openapi.yaml +311 -0
- package/labs/anyrow.config.ts +107 -0
- package/labs/demo.mjs +124 -0
- package/labs/local.config.ts +41 -0
- package/labs/minimal.config.ts +32 -0
- package/package.json +80 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage contract shared by the reference backends.
|
|
3
|
+
*
|
|
4
|
+
* One HTTP server implementation runs against every store, so a defect is defined once and
|
|
5
|
+
* exercised on each engine. Where two engines disagree about what a defect *does*, that is
|
|
6
|
+
* itself the finding — SQLite and Postgres differ on NULL ordering, type discipline, collation
|
|
7
|
+
* and case-insensitive matching, and those are exactly the semantics oat's checks reason about.
|
|
8
|
+
*
|
|
9
|
+
* Async throughout, because a real database is: SQLite implementations resolve immediately.
|
|
10
|
+
*/
|
|
11
|
+
/** Raised for input the storage layer rejects — surfaces as 400, or 500 under the defect. */
|
|
12
|
+
export class SqlError extends Error {
|
|
13
|
+
code;
|
|
14
|
+
constructor(code, message) {
|
|
15
|
+
super(message);
|
|
16
|
+
this.code = code;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function encodeCursor(id) {
|
|
20
|
+
return Buffer.from(JSON.stringify({ id }), "utf8").toString("base64url");
|
|
21
|
+
}
|
|
22
|
+
export function decodeCursor(cursor) {
|
|
23
|
+
try {
|
|
24
|
+
const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
|
|
25
|
+
if (typeof parsed.id !== "string")
|
|
26
|
+
throw new Error("missing id");
|
|
27
|
+
return parsed.id;
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
throw new SqlError("invalid_cursor", "cursor is not a value produced by this API");
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
/** Splits on commas at paren depth zero — shared by the filter and order grammars. */
|
|
34
|
+
export function splitTopLevel(input) {
|
|
35
|
+
const parts = [];
|
|
36
|
+
let depth = 0;
|
|
37
|
+
let start = 0;
|
|
38
|
+
for (let i = 0; i < input.length; i++) {
|
|
39
|
+
const ch = input[i];
|
|
40
|
+
if (ch === "(")
|
|
41
|
+
depth++;
|
|
42
|
+
else if (ch === ")")
|
|
43
|
+
depth--;
|
|
44
|
+
else if (ch === "," && depth === 0) {
|
|
45
|
+
parts.push(input.slice(start, i));
|
|
46
|
+
start = i + 1;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
parts.push(input.slice(start));
|
|
50
|
+
return parts.map((p) => p.trim()).filter((p) => p.length > 0);
|
|
51
|
+
}
|
|
52
|
+
export function stripParens(value) {
|
|
53
|
+
return value.startsWith("(") && value.endsWith(")") ? value.slice(1, -1) : value;
|
|
54
|
+
}
|
|
55
|
+
export function project(row, select, ignore,
|
|
56
|
+
/* Fields the document still declares selectable that the backend has stopped honouring — see
|
|
57
|
+
* SPEC_OVERCLAIMS_SELECTABLE. Everything else stays as permissive as before: an unrecognised
|
|
58
|
+
* field is silently dropped, never rejected. */
|
|
59
|
+
excluded = []) {
|
|
60
|
+
if (select === undefined || select === "" || select === "*")
|
|
61
|
+
return { ...row };
|
|
62
|
+
if (ignore)
|
|
63
|
+
return { ...row };
|
|
64
|
+
const fields = select.split(",").map((s) => s.trim()).filter(Boolean);
|
|
65
|
+
const rejected = fields.find((f) => excluded.includes(f));
|
|
66
|
+
if (rejected !== undefined) {
|
|
67
|
+
throw new SqlError("invalid_select", `field "${rejected}" is not selectable`);
|
|
68
|
+
}
|
|
69
|
+
const out = {};
|
|
70
|
+
for (const field of fields) {
|
|
71
|
+
if (Object.hasOwn(row, field))
|
|
72
|
+
out[field] = row[field];
|
|
73
|
+
}
|
|
74
|
+
return out;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=store-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-api.js","sourceRoot":"","sources":["../../src/reference/store-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory storage for the reference backend.
|
|
3
|
+
*
|
|
4
|
+
* The zero-dependency fallback: no database, no flags, no server to have running. It executes the
|
|
5
|
+
* query grammar in JavaScript, which is exactly why the SQL stores exist alongside it — a JS
|
|
6
|
+
* store cannot exhibit NULL ordering, collation, or type-discipline behaviour, so a check that
|
|
7
|
+
* only ever runs here has not been proven against anything a real backend does.
|
|
8
|
+
*/
|
|
9
|
+
import type { DefectSet } from "../defects.ts";
|
|
10
|
+
import type { EntityDef } from "../model.ts";
|
|
11
|
+
import type { QueryOptions, QueryParams, QueryResult, Row, Store } from "../store-api.ts";
|
|
12
|
+
export declare class MemoryStore implements Store {
|
|
13
|
+
private readonly defects;
|
|
14
|
+
private readonly tables;
|
|
15
|
+
private sequence;
|
|
16
|
+
constructor(defects: DefectSet);
|
|
17
|
+
close(): Promise<void>;
|
|
18
|
+
private collection;
|
|
19
|
+
nextId(prefix: string): string;
|
|
20
|
+
now(): number;
|
|
21
|
+
insert(entity: EntityDef, record: Row): Promise<Row>;
|
|
22
|
+
byId(entity: EntityDef, id: string): Promise<Row | null>;
|
|
23
|
+
update(entity: EntityDef, id: string, patch: Row): Promise<Row | null>;
|
|
24
|
+
remove(entity: EntityDef, id: string): Promise<void>;
|
|
25
|
+
query(entity: EntityDef, scope: Record<string, string>, params: QueryParams, options: QueryOptions): Promise<QueryResult>;
|
|
26
|
+
}
|
|
27
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/reference/stores/memory.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAG5C,OAAO,KAAK,EACX,YAAY,EACZ,WAAW,EACX,WAAW,EACX,GAAG,EACH,KAAK,EACL,MAAM,iBAAiB,CAAA;AAExB,qBAAa,WAAY,YAAW,KAAK;IAI5B,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsC;IAC7D,OAAO,CAAC,QAAQ,CAAI;gBAES,OAAO,EAAE,SAAS;IAEzC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAI5B,OAAO,CAAC,UAAU;IAQlB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAK9B,GAAG,IAAI,MAAM;IAMP,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAMpD,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAKxD,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAQtE,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIpD,KAAK,CACV,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,YAAY,GACnB,OAAO,CAAC,WAAW,CAAC;CA4CvB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* In-memory storage for the reference backend.
|
|
3
|
+
*
|
|
4
|
+
* The zero-dependency fallback: no database, no flags, no server to have running. It executes the
|
|
5
|
+
* query grammar in JavaScript, which is exactly why the SQL stores exist alongside it — a JS
|
|
6
|
+
* store cannot exhibit NULL ordering, collation, or type-discipline behaviour, so a check that
|
|
7
|
+
* only ever runs here has not been proven against anything a real backend does.
|
|
8
|
+
*/
|
|
9
|
+
import { OVERCLAIMED_FIELD, fieldsWhere } from "../model.js";
|
|
10
|
+
import { runQuery } from "../query.js";
|
|
11
|
+
export class MemoryStore {
|
|
12
|
+
defects;
|
|
13
|
+
tables = new Map();
|
|
14
|
+
sequence = 0;
|
|
15
|
+
constructor(defects) {
|
|
16
|
+
this.defects = defects;
|
|
17
|
+
}
|
|
18
|
+
async close() {
|
|
19
|
+
this.tables.clear();
|
|
20
|
+
}
|
|
21
|
+
collection(entity) {
|
|
22
|
+
const existing = this.tables.get(entity.name);
|
|
23
|
+
if (existing !== undefined)
|
|
24
|
+
return existing;
|
|
25
|
+
const created = new Map();
|
|
26
|
+
this.tables.set(entity.name, created);
|
|
27
|
+
return created;
|
|
28
|
+
}
|
|
29
|
+
nextId(prefix) {
|
|
30
|
+
this.sequence += 1;
|
|
31
|
+
return `${prefix}_${String(this.sequence).padStart(6, "0")}`;
|
|
32
|
+
}
|
|
33
|
+
now() {
|
|
34
|
+
/* Derived from the sequence rather than the clock: wall-clock timestamps make ordering
|
|
35
|
+
* assertions flaky for reasons that have nothing to do with the backend. */
|
|
36
|
+
return 1_700_000_000_000 + this.sequence * 1000;
|
|
37
|
+
}
|
|
38
|
+
async insert(entity, record) {
|
|
39
|
+
const stored = { ...record };
|
|
40
|
+
this.collection(entity).set(String(record[entity.identity]), stored);
|
|
41
|
+
return { ...stored };
|
|
42
|
+
}
|
|
43
|
+
async byId(entity, id) {
|
|
44
|
+
const found = this.collection(entity).get(id);
|
|
45
|
+
return found === undefined ? null : { ...found };
|
|
46
|
+
}
|
|
47
|
+
async update(entity, id, patch) {
|
|
48
|
+
const existing = this.collection(entity).get(id);
|
|
49
|
+
if (existing === undefined)
|
|
50
|
+
return null;
|
|
51
|
+
const next = { ...existing, ...patch };
|
|
52
|
+
this.collection(entity).set(id, next);
|
|
53
|
+
return { ...next };
|
|
54
|
+
}
|
|
55
|
+
async remove(entity, id) {
|
|
56
|
+
this.collection(entity).delete(id);
|
|
57
|
+
}
|
|
58
|
+
async query(entity, scope, params, options) {
|
|
59
|
+
/* Ancestor scoping — `.../tables/{table_id}/rows` lists that table only. The defect keeps
|
|
60
|
+
* scoping on the plain listing but drops it once a filter is present, which is how a
|
|
61
|
+
* filter turns into an authorization bypass. */
|
|
62
|
+
const scoped = !(this.defects.has("TENANT_LEAK_VIA_FILTER") && params.filter !== undefined);
|
|
63
|
+
let source = [...this.collection(entity).values()];
|
|
64
|
+
for (const parent of scoped ? entity.parents : []) {
|
|
65
|
+
const value = scope[parent];
|
|
66
|
+
if (value === undefined)
|
|
67
|
+
continue;
|
|
68
|
+
if (!entity.fields.some((f) => f.name === parent))
|
|
69
|
+
continue;
|
|
70
|
+
source = source.filter((row) => String(row[parent]) === value);
|
|
71
|
+
}
|
|
72
|
+
const result = runQuery(source, params, {
|
|
73
|
+
defaultLimit: entity.defaultLimit,
|
|
74
|
+
/* Under the overclaim defect the backend refuses a field the document still declares
|
|
75
|
+
* filterable. The backend is not wrong to refuse — the document is wrong to promise. */
|
|
76
|
+
filterable: this.defects.has("SPEC_OVERCLAIMS_FILTERABLE")
|
|
77
|
+
? fieldsWhere(entity, "filterable").filter((f) => f !== OVERCLAIMED_FIELD)
|
|
78
|
+
: fieldsWhere(entity, "filterable"),
|
|
79
|
+
/* Same overclaim shape, applied to the select role: the document still lists the field,
|
|
80
|
+
* the backend now refuses to project it. */
|
|
81
|
+
excludedSelect: this.defects.has("SPEC_OVERCLAIMS_SELECTABLE") ? [OVERCLAIMED_FIELD] : [],
|
|
82
|
+
identity: entity.identity,
|
|
83
|
+
maxLimit: entity.maxLimit,
|
|
84
|
+
searchable: fieldsWhere(entity, "searchable"),
|
|
85
|
+
/* Same overclaim shape as filterable, one role over: the document still promises the
|
|
86
|
+
* field can be sorted by; the backend refuses it. */
|
|
87
|
+
sortable: this.defects.has("SPEC_OVERCLAIMS_SORTABLE")
|
|
88
|
+
? fieldsWhere(entity, "sortable").filter((f) => f !== OVERCLAIMED_FIELD)
|
|
89
|
+
: fieldsWhere(entity, "sortable"),
|
|
90
|
+
...(options.softDeleteField === undefined
|
|
91
|
+
? {}
|
|
92
|
+
: { softDeleteField: options.softDeleteField }),
|
|
93
|
+
}, this.defects, options.transform);
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/reference/stores/memory.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Postgres-backed storage for the reference backend.
|
|
3
|
+
*
|
|
4
|
+
* Present because Postgres disagrees with SQLite in exactly the places API contracts are
|
|
5
|
+
* ambiguous, which makes the pair a differential oracle rather than a redundant one:
|
|
6
|
+
*
|
|
7
|
+
* - `ORDER BY x ASC` puts NULLs **last**; SQLite puts them first.
|
|
8
|
+
* - Types are enforced, so bad input is rejected rather than silently coerced.
|
|
9
|
+
* - Collation is locale-aware, so ordering of unicode and mixed case is a real decision.
|
|
10
|
+
* - `LIKE` is case-sensitive and `ILIKE` genuinely differs — on SQLite the two are
|
|
11
|
+
* indistinguishable for ASCII, which makes an ilike check structurally untestable there.
|
|
12
|
+
*
|
|
13
|
+
* Each server instance gets its own scratch database, dropped on close, so runs never collide.
|
|
14
|
+
*/
|
|
15
|
+
import type { DefectSet } from "../defects.ts";
|
|
16
|
+
import { type EntityDef } from "../model.ts";
|
|
17
|
+
import { type QueryOptions, type QueryParams, type QueryResult, type Row, type Store } from "../store-api.ts";
|
|
18
|
+
export declare class PgStore implements Store {
|
|
19
|
+
private readonly sql;
|
|
20
|
+
private readonly admin;
|
|
21
|
+
private readonly dbName;
|
|
22
|
+
private readonly defects;
|
|
23
|
+
private sequence;
|
|
24
|
+
private constructor();
|
|
25
|
+
static create(defects: DefectSet): Promise<PgStore>;
|
|
26
|
+
close(): Promise<void>;
|
|
27
|
+
private migrate;
|
|
28
|
+
/** The stored column for a field — diverges from the declared name only under the defect. */
|
|
29
|
+
private physical;
|
|
30
|
+
/**
|
|
31
|
+
* Explicit projection, aliased back to declared names.
|
|
32
|
+
*
|
|
33
|
+
* Under COLUMN_NAME_MISMATCH the read asks for a column that does not exist. Postgres errors
|
|
34
|
+
* outright where SQLite's double-quoted-string fallback silently yields the identifier as a
|
|
35
|
+
* literal — the same defect, two failure modes, which is the point of running both.
|
|
36
|
+
*/
|
|
37
|
+
private selectList;
|
|
38
|
+
nextId(prefix: string): string;
|
|
39
|
+
now(): number;
|
|
40
|
+
insert(entity: EntityDef, record: Row): Promise<Row>;
|
|
41
|
+
byId(entity: EntityDef, id: string): Promise<Row | null>;
|
|
42
|
+
update(entity: EntityDef, id: string, patch: Row): Promise<Row | null>;
|
|
43
|
+
/**
|
|
44
|
+
* Reads the row, yields, then writes every column back — the `save(entity)` pattern.
|
|
45
|
+
*
|
|
46
|
+
* Under concurrency the second writer reinstates whatever it read before the first writer
|
|
47
|
+
* committed, so a patch to a different field is silently reverted. The yield is what makes
|
|
48
|
+
* the window observable rather than dependent on scheduler luck.
|
|
49
|
+
*/
|
|
50
|
+
private readModifyWrite;
|
|
51
|
+
remove(entity: EntityDef, id: string): Promise<void>;
|
|
52
|
+
/**
|
|
53
|
+
* Restores declared shapes. `bigint` arrives as a string from the wire protocol and would
|
|
54
|
+
* fail its own response schema, and a NULL column must read back as null rather than absent.
|
|
55
|
+
*/
|
|
56
|
+
private decode;
|
|
57
|
+
query(entity: EntityDef, scope: Record<string, string>, params: QueryParams, options: QueryOptions): Promise<QueryResult>;
|
|
58
|
+
private compileFilter;
|
|
59
|
+
private compileOrder;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=postgres.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../../src/reference/stores/postgres.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,eAAe,CAAA;AAC9C,OAAO,EAAY,KAAK,SAAS,EAAiD,MAAM,aAAa,CAAA;AACrG,OAAO,EAIN,KAAK,YAAY,EACjB,KAAK,WAAW,EAChB,KAAK,WAAW,EAChB,KAAK,GAAG,EAIR,KAAK,KAAK,EACV,MAAM,iBAAiB,CAAA;AAkBxB,qBAAa,OAAQ,YAAW,KAAK;IAInC,OAAO,CAAC,QAAQ,CAAC,GAAG;IACpB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,OAAO;IANzB,OAAO,CAAC,QAAQ,CAAI;IAEpB,OAAO;WAOM,MAAM,CAAC,OAAO,EAAE,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC;IAUnD,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;YAWd,OAAO;IAoBrB,6FAA6F;IAC7F,OAAO,CAAC,QAAQ;IAIhB;;;;;;OAMG;IACH,OAAO,CAAC,UAAU;IAWlB,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM;IAK9B,GAAG,IAAI,MAAM;IAIP,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC;IAWpD,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IASxD,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC;IAa5E;;;;;;OAMG;YACW,eAAe;IAcvB,MAAM,CAAC,MAAM,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAO1D;;;OAGG;IACH,OAAO,CAAC,MAAM;IAoBR,KAAK,CACV,MAAM,EAAE,SAAS,EACjB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAC7B,MAAM,EAAE,WAAW,EACnB,OAAO,EAAE,YAAY,GACnB,OAAO,CAAC,WAAW,CAAC;IAwHvB,OAAO,CAAC,aAAa;IAyFrB,OAAO,CAAC,YAAY;CA8CpB"}
|