@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,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The conventions a reference backend speaks.
|
|
3
|
+
*
|
|
4
|
+
* Everything oat had quietly hardcoded — what the page-size parameter is called, where the total
|
|
5
|
+
* lives in the envelope, how a filter expression is spelled — varies wildly between real APIs
|
|
6
|
+
* and not at all between two instances of the same fixture. A single-dialect fixture therefore
|
|
7
|
+
* proves that the checks work on *one* shape, which is a much weaker claim than it appears.
|
|
8
|
+
*
|
|
9
|
+
* Two dialects, differing in every name and in the filter grammar, turn that into a real test:
|
|
10
|
+
* the defects and the checks are identical, so anything that passes on one and fails on the
|
|
11
|
+
* other was reading a convention rather than the contract.
|
|
12
|
+
*/
|
|
13
|
+
export interface Dialect {
|
|
14
|
+
name: string;
|
|
15
|
+
/** Query parameter names, by role. */
|
|
16
|
+
params: {
|
|
17
|
+
filter: string;
|
|
18
|
+
order: string;
|
|
19
|
+
select: string;
|
|
20
|
+
search: string;
|
|
21
|
+
limit: string;
|
|
22
|
+
/** Page number, where the API counts pages. Absent when it counts rows skipped. */
|
|
23
|
+
page?: string;
|
|
24
|
+
/** Rows to skip, where the API pages by offset rather than page number. */
|
|
25
|
+
offset?: string;
|
|
26
|
+
cursor?: string;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Envelope property names, by role.
|
|
30
|
+
*
|
|
31
|
+
* `null` for the whole object means the response *is* the array and pagination facts travel in
|
|
32
|
+
* a `Link` header instead — the GitHub shape. That is a different pagination model, not a
|
|
33
|
+
* different spelling of one, which is precisely why it belongs in the fixture: a tool that
|
|
34
|
+
* only ever saw facts in a body may be reading conventions rather than the contract.
|
|
35
|
+
*/
|
|
36
|
+
envelope: {
|
|
37
|
+
/** Property holding the array. `null` means "named after the entity" — `tables`, `rows`. */
|
|
38
|
+
collection: string | null;
|
|
39
|
+
total: string;
|
|
40
|
+
hasMore: string;
|
|
41
|
+
nextCursor?: string;
|
|
42
|
+
page: string;
|
|
43
|
+
limit: string;
|
|
44
|
+
} | null;
|
|
45
|
+
/**
|
|
46
|
+
* How filter expressions are written.
|
|
47
|
+
*
|
|
48
|
+
* `equality` has no filter parameter at all: each filterable field is its own query parameter,
|
|
49
|
+
* and only equality is expressible. It is the most common shape in the wild and the one where
|
|
50
|
+
* oat can say the least — which makes it the real test of whether "I cannot express this" is
|
|
51
|
+
* reported honestly rather than silently skipped.
|
|
52
|
+
*/
|
|
53
|
+
grammar: "postgrest" | "colon" | "equality";
|
|
54
|
+
/** How sort directions are written. Defaults to the dotted form when unset. */
|
|
55
|
+
sortGrammar?: "dotted" | "prefixed" | "colon" | "spaced";
|
|
56
|
+
/** How a sparse fieldset is written. Defaults to comma-separated names. */
|
|
57
|
+
selectGrammar?: "csv" | "bracketed";
|
|
58
|
+
}
|
|
59
|
+
/** `field.op.value` with cursor pagination and entity-named collections. */
|
|
60
|
+
export declare const POSTGREST: Dialect;
|
|
61
|
+
/**
|
|
62
|
+
* A deliberately unrelated spelling: `per_page`, `sort`, `fields`, `search`, a `data` envelope
|
|
63
|
+
* with `total_count`/`has_more`, no cursor at all, and `field=op:value` filters.
|
|
64
|
+
*
|
|
65
|
+
* Nothing here shares a name with the other dialect, so a check that still works is working from
|
|
66
|
+
* the document rather than from familiarity.
|
|
67
|
+
*/
|
|
68
|
+
export declare const CLASSIC: Dialect;
|
|
69
|
+
/**
|
|
70
|
+
* Root array, `Link` header pagination, `offset`/`limit`.
|
|
71
|
+
*
|
|
72
|
+
* No envelope at all: the body is the array, there is no total, no `hasMore`, and no page number.
|
|
73
|
+
* Whether more pages exist is answered by `rel="next"` in a header. Nothing about a check should
|
|
74
|
+
* have to change for this, and where something does, the check was reading the fixture's habits.
|
|
75
|
+
*/
|
|
76
|
+
export declare const LINKED: Dialect;
|
|
77
|
+
/**
|
|
78
|
+
* JSON:API-flavoured: `sort=-name` for descending, `fields[table]=id,name` for projection.
|
|
79
|
+
*
|
|
80
|
+
* The parameter *names* here are unremarkable — `sort`, `fields`, `page`, `filter` — which is the
|
|
81
|
+
* point. Everything oat had generalised was the name; the value grammars stayed hardcoded to one
|
|
82
|
+
* spelling. A shape that renames nothing but writes its values differently is the sharpest test
|
|
83
|
+
* of whether that generalisation was real.
|
|
84
|
+
*/
|
|
85
|
+
export declare const JSONAPI: Dialect;
|
|
86
|
+
/**
|
|
87
|
+
* The plainest shape there is: `?status=active&page=2`, no filter expression language at all.
|
|
88
|
+
*
|
|
89
|
+
* Half of oat's query checks cannot be expressed here — there is no way to write a negation, a
|
|
90
|
+
* range, or a pattern — and that is precisely the point. Every one of them must report what it
|
|
91
|
+
* could not test and why, because a run that goes quiet against the most common API shape in the
|
|
92
|
+
* world is the single most damaging way this tool could fail.
|
|
93
|
+
*/
|
|
94
|
+
export declare const PLAIN: Dialect;
|
|
95
|
+
export declare const DIALECTS: Record<string, Dialect>;
|
|
96
|
+
/**
|
|
97
|
+
* Rewrites a filter expression from a dialect's grammar into the canonical `field.op.value` the
|
|
98
|
+
* stores parse, so the storage layer never has to know which dialect it is serving.
|
|
99
|
+
*
|
|
100
|
+
* Returns `null` when the expression is malformed, which the caller surfaces as a 400 — the same
|
|
101
|
+
* treatment the canonical grammar gets.
|
|
102
|
+
*/
|
|
103
|
+
export declare function toCanonicalFilter(expression: string, dialect: Dialect): string | null;
|
|
104
|
+
//# sourceMappingURL=dialect.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialect.d.ts","sourceRoot":"","sources":["../../src/reference/dialect.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,OAAO;IACvB,IAAI,EAAE,MAAM,CAAA;IACZ,sCAAsC;IACtC,MAAM,EAAE;QACP,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,EAAE,MAAM,CAAA;QACb,MAAM,EAAE,MAAM,CAAA;QACd,MAAM,EAAE,MAAM,CAAA;QACd,KAAK,EAAE,MAAM,CAAA;QACb,mFAAmF;QACnF,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,2EAA2E;QAC3E,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,MAAM,CAAC,EAAE,MAAM,CAAA;KACf,CAAA;IACD;;;;;;;OAOG;IACH,QAAQ,EAAE;QACT,4FAA4F;QAC5F,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;QACzB,KAAK,EAAE,MAAM,CAAA;QACb,OAAO,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,IAAI,EAAE,MAAM,CAAA;QACZ,KAAK,EAAE,MAAM,CAAA;KACb,GAAG,IAAI,CAAA;IACR;;;;;;;OAOG;IACH,OAAO,EAAE,WAAW,GAAG,OAAO,GAAG,UAAU,CAAA;IAC3C,+EAA+E;IAC/E,WAAW,CAAC,EAAE,QAAQ,GAAG,UAAU,GAAG,OAAO,GAAG,QAAQ,CAAA;IACxD,2EAA2E;IAC3E,aAAa,CAAC,EAAE,KAAK,GAAG,WAAW,CAAA;CACnC;AAED,4EAA4E;AAC5E,eAAO,MAAM,SAAS,EAAE,OAoBvB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,OAAO,EAAE,OAkBrB,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,EAAE,OAYpB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,OAAO,EAAE,OAoBrB,CAAA;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,EAAE,OAqBnB,CAAA;AAED,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAM5C,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAkBrF"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The conventions a reference backend speaks.
|
|
3
|
+
*
|
|
4
|
+
* Everything oat had quietly hardcoded — what the page-size parameter is called, where the total
|
|
5
|
+
* lives in the envelope, how a filter expression is spelled — varies wildly between real APIs
|
|
6
|
+
* and not at all between two instances of the same fixture. A single-dialect fixture therefore
|
|
7
|
+
* proves that the checks work on *one* shape, which is a much weaker claim than it appears.
|
|
8
|
+
*
|
|
9
|
+
* Two dialects, differing in every name and in the filter grammar, turn that into a real test:
|
|
10
|
+
* the defects and the checks are identical, so anything that passes on one and fails on the
|
|
11
|
+
* other was reading a convention rather than the contract.
|
|
12
|
+
*/
|
|
13
|
+
/** `field.op.value` with cursor pagination and entity-named collections. */
|
|
14
|
+
export const POSTGREST = {
|
|
15
|
+
envelope: {
|
|
16
|
+
collection: null,
|
|
17
|
+
hasMore: "hasMore",
|
|
18
|
+
limit: "limit",
|
|
19
|
+
nextCursor: "nextCursor",
|
|
20
|
+
page: "page",
|
|
21
|
+
total: "count",
|
|
22
|
+
},
|
|
23
|
+
grammar: "postgrest",
|
|
24
|
+
name: "postgrest",
|
|
25
|
+
params: {
|
|
26
|
+
cursor: "cursor",
|
|
27
|
+
filter: "filter",
|
|
28
|
+
limit: "limit",
|
|
29
|
+
order: "order",
|
|
30
|
+
page: "page",
|
|
31
|
+
search: "q",
|
|
32
|
+
select: "select",
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* A deliberately unrelated spelling: `per_page`, `sort`, `fields`, `search`, a `data` envelope
|
|
37
|
+
* with `total_count`/`has_more`, no cursor at all, and `field=op:value` filters.
|
|
38
|
+
*
|
|
39
|
+
* Nothing here shares a name with the other dialect, so a check that still works is working from
|
|
40
|
+
* the document rather than from familiarity.
|
|
41
|
+
*/
|
|
42
|
+
export const CLASSIC = {
|
|
43
|
+
envelope: {
|
|
44
|
+
collection: "data",
|
|
45
|
+
hasMore: "has_more",
|
|
46
|
+
limit: "per_page",
|
|
47
|
+
page: "page",
|
|
48
|
+
total: "total_count",
|
|
49
|
+
},
|
|
50
|
+
grammar: "colon",
|
|
51
|
+
name: "classic",
|
|
52
|
+
params: {
|
|
53
|
+
filter: "filter",
|
|
54
|
+
limit: "per_page",
|
|
55
|
+
order: "sort",
|
|
56
|
+
page: "page",
|
|
57
|
+
search: "search",
|
|
58
|
+
select: "fields",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Root array, `Link` header pagination, `offset`/`limit`.
|
|
63
|
+
*
|
|
64
|
+
* No envelope at all: the body is the array, there is no total, no `hasMore`, and no page number.
|
|
65
|
+
* Whether more pages exist is answered by `rel="next"` in a header. Nothing about a check should
|
|
66
|
+
* have to change for this, and where something does, the check was reading the fixture's habits.
|
|
67
|
+
*/
|
|
68
|
+
export const LINKED = {
|
|
69
|
+
envelope: null,
|
|
70
|
+
grammar: "postgrest",
|
|
71
|
+
name: "linked",
|
|
72
|
+
params: {
|
|
73
|
+
filter: "filter",
|
|
74
|
+
limit: "limit",
|
|
75
|
+
offset: "offset",
|
|
76
|
+
order: "order",
|
|
77
|
+
search: "q",
|
|
78
|
+
select: "fields",
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* JSON:API-flavoured: `sort=-name` for descending, `fields[table]=id,name` for projection.
|
|
83
|
+
*
|
|
84
|
+
* The parameter *names* here are unremarkable — `sort`, `fields`, `page`, `filter` — which is the
|
|
85
|
+
* point. Everything oat had generalised was the name; the value grammars stayed hardcoded to one
|
|
86
|
+
* spelling. A shape that renames nothing but writes its values differently is the sharpest test
|
|
87
|
+
* of whether that generalisation was real.
|
|
88
|
+
*/
|
|
89
|
+
export const JSONAPI = {
|
|
90
|
+
envelope: {
|
|
91
|
+
collection: "data",
|
|
92
|
+
hasMore: "has_more",
|
|
93
|
+
limit: "size",
|
|
94
|
+
page: "number",
|
|
95
|
+
total: "total",
|
|
96
|
+
},
|
|
97
|
+
grammar: "postgrest",
|
|
98
|
+
name: "jsonapi",
|
|
99
|
+
params: {
|
|
100
|
+
filter: "filter",
|
|
101
|
+
limit: "size",
|
|
102
|
+
order: "sort",
|
|
103
|
+
page: "page",
|
|
104
|
+
search: "q",
|
|
105
|
+
select: "fields",
|
|
106
|
+
},
|
|
107
|
+
selectGrammar: "bracketed",
|
|
108
|
+
sortGrammar: "prefixed",
|
|
109
|
+
};
|
|
110
|
+
/**
|
|
111
|
+
* The plainest shape there is: `?status=active&page=2`, no filter expression language at all.
|
|
112
|
+
*
|
|
113
|
+
* Half of oat's query checks cannot be expressed here — there is no way to write a negation, a
|
|
114
|
+
* range, or a pattern — and that is precisely the point. Every one of them must report what it
|
|
115
|
+
* could not test and why, because a run that goes quiet against the most common API shape in the
|
|
116
|
+
* world is the single most damaging way this tool could fail.
|
|
117
|
+
*/
|
|
118
|
+
export const PLAIN = {
|
|
119
|
+
envelope: {
|
|
120
|
+
collection: "items",
|
|
121
|
+
hasMore: "has_more",
|
|
122
|
+
limit: "limit",
|
|
123
|
+
page: "page",
|
|
124
|
+
total: "total",
|
|
125
|
+
},
|
|
126
|
+
grammar: "equality",
|
|
127
|
+
name: "plain",
|
|
128
|
+
params: {
|
|
129
|
+
/* Declared for completeness; the equality grammar never sends it, and the fixture never
|
|
130
|
+
* reads it. A parameter a backend accepts and ignores is itself a defect oat reports. */
|
|
131
|
+
filter: "filter",
|
|
132
|
+
limit: "limit",
|
|
133
|
+
order: "sort",
|
|
134
|
+
page: "page",
|
|
135
|
+
search: "q",
|
|
136
|
+
select: "fields",
|
|
137
|
+
},
|
|
138
|
+
sortGrammar: "colon",
|
|
139
|
+
};
|
|
140
|
+
export const DIALECTS = {
|
|
141
|
+
jsonapi: JSONAPI,
|
|
142
|
+
plain: PLAIN,
|
|
143
|
+
classic: CLASSIC,
|
|
144
|
+
linked: LINKED,
|
|
145
|
+
postgrest: POSTGREST,
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Rewrites a filter expression from a dialect's grammar into the canonical `field.op.value` the
|
|
149
|
+
* stores parse, so the storage layer never has to know which dialect it is serving.
|
|
150
|
+
*
|
|
151
|
+
* Returns `null` when the expression is malformed, which the caller surfaces as a 400 — the same
|
|
152
|
+
* treatment the canonical grammar gets.
|
|
153
|
+
*/
|
|
154
|
+
export function toCanonicalFilter(expression, dialect) {
|
|
155
|
+
/* Already canonical: the postgrest grammar *is* the canonical form, and the equality grammar
|
|
156
|
+
* has no expression of its own — the per-field parameters were assembled into canonical terms
|
|
157
|
+
* before reaching here. Re-parsing that would reject a well-formed expression. */
|
|
158
|
+
if (dialect.grammar === "postgrest" || dialect.grammar === "equality")
|
|
159
|
+
return expression;
|
|
160
|
+
/* `status=eq:active`, joined by commas. No grouping: the point of a second dialect is
|
|
161
|
+
* different spelling, not a second expression language to maintain. */
|
|
162
|
+
const terms = expression.split(",").map((term) => term.trim()).filter(Boolean);
|
|
163
|
+
if (terms.length === 0)
|
|
164
|
+
return null;
|
|
165
|
+
const canonical = [];
|
|
166
|
+
for (const term of terms) {
|
|
167
|
+
const match = /^([^=]+)=([a-z]+):(.*)$/s.exec(term);
|
|
168
|
+
if (match?.[1] === undefined || match[2] === undefined || match[3] === undefined)
|
|
169
|
+
return null;
|
|
170
|
+
canonical.push(`${match[1]}.${match[2]}.${match[3]}`);
|
|
171
|
+
}
|
|
172
|
+
return canonical.length === 1 ? canonical[0] : `and(${canonical.join(",")})`;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=dialect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dialect.js","sourceRoot":"","sources":["../../src/reference/dialect.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The reference backend's HTTP layer — one implementation, any store.
|
|
3
|
+
*
|
|
4
|
+
* Routing, auth, tenancy, validation and the defect behaviours that live above storage are
|
|
5
|
+
* written once here. Each engine supplies only a `Store`, so a defect is defined in one place
|
|
6
|
+
* and exercised identically on every backend; when two engines then disagree about what that
|
|
7
|
+
* defect *does*, the disagreement is real rather than an artefact of two implementations
|
|
8
|
+
* drifting apart.
|
|
9
|
+
*
|
|
10
|
+
* Backends: `createMemoryServer` (no dependencies), `createSqliteServer` (`node:sqlite`),
|
|
11
|
+
* `createD1Server` (Cloudflare D1 over HTTP),
|
|
12
|
+
* `createPostgresServer` (a reachable server). Every store is imported lazily — a static import
|
|
13
|
+
* would load one engine's driver for all of them, and an optional runtime such as `node:sqlite`
|
|
14
|
+
* would take down paths that never touch it.
|
|
15
|
+
*
|
|
16
|
+
* This is a test fixture. It is excluded from the published package; oat itself never talks to
|
|
17
|
+
* a database.
|
|
18
|
+
*/
|
|
19
|
+
import { type Server } from "node:http";
|
|
20
|
+
import { type DefectSet } from "./defects.ts";
|
|
21
|
+
import { type Store } from "./store-api.ts";
|
|
22
|
+
interface Principal {
|
|
23
|
+
key: string;
|
|
24
|
+
token: string;
|
|
25
|
+
projectId: string;
|
|
26
|
+
/** Higher can do everything a lower rank can. Viewer 0, member 1, owner 2. */
|
|
27
|
+
rank: number;
|
|
28
|
+
}
|
|
29
|
+
export interface ReferenceServer {
|
|
30
|
+
server: Server;
|
|
31
|
+
url: string;
|
|
32
|
+
defects: DefectSet;
|
|
33
|
+
close: () => Promise<void>;
|
|
34
|
+
principals: Principal[];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* One server, any store. Defining a defect once and exercising it on every engine is what makes
|
|
38
|
+
* a disagreement between engines meaningful rather than an artefact of two implementations.
|
|
39
|
+
*/
|
|
40
|
+
export declare function createReferenceServer(options: {
|
|
41
|
+
defects?: string[];
|
|
42
|
+
untagged?: boolean;
|
|
43
|
+
dialect?: string;
|
|
44
|
+
}, createStore: (defects: DefectSet) => Promise<Store>): Promise<ReferenceServer>;
|
|
45
|
+
/** In-memory reference server — no dependencies, no flags, nothing to have running. */
|
|
46
|
+
export declare function createMemoryServer(options?: {
|
|
47
|
+
defects?: string[];
|
|
48
|
+
untagged?: boolean;
|
|
49
|
+
dialect?: string;
|
|
50
|
+
}): Promise<ReferenceServer>;
|
|
51
|
+
/** SQLite-backed reference server, in-process via `node:sqlite`. */
|
|
52
|
+
export declare function createSqliteServer(options?: {
|
|
53
|
+
defects?: string[];
|
|
54
|
+
untagged?: boolean;
|
|
55
|
+
dialect?: string;
|
|
56
|
+
}): Promise<ReferenceServer>;
|
|
57
|
+
/**
|
|
58
|
+
* Cloudflare D1 reference server — the same SQL over the network, against someone else's build.
|
|
59
|
+
*
|
|
60
|
+
* Credentials come from the environment rather than config so a token never reaches a file that
|
|
61
|
+
* could be committed. Tables are prefixed per run and dropped on close, because D1 persists: two
|
|
62
|
+
* runs sharing one database would otherwise contaminate each other's results.
|
|
63
|
+
*/
|
|
64
|
+
export declare function createD1Server(options?: {
|
|
65
|
+
defects?: string[];
|
|
66
|
+
untagged?: boolean;
|
|
67
|
+
dialect?: string;
|
|
68
|
+
accountId?: string;
|
|
69
|
+
databaseId?: string;
|
|
70
|
+
apiToken?: string;
|
|
71
|
+
}): Promise<ReferenceServer>;
|
|
72
|
+
/** Postgres-backed reference server. Imported lazily so the driver is only loaded when used. */
|
|
73
|
+
export declare function createPostgresServer(options?: {
|
|
74
|
+
defects?: string[];
|
|
75
|
+
untagged?: boolean;
|
|
76
|
+
dialect?: string;
|
|
77
|
+
}): Promise<ReferenceServer>;
|
|
78
|
+
export {};
|
|
79
|
+
//# sourceMappingURL=http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../../src/reference/http.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAsC,KAAK,MAAM,EAAuB,MAAM,WAAW,CAAA;AAEhG,OAAO,EAAwB,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAInE,OAAO,EAAsB,KAAK,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAE/D,UAAU,SAAS;IAClB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAA;IACb,SAAS,EAAE,MAAM,CAAA;IACjB,8EAA8E;IAC9E,IAAI,EAAE,MAAM,CAAA;CACZ;AAoJD,MAAM,WAAW,eAAe;IAC/B,MAAM,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,MAAM,CAAA;IACX,OAAO,EAAE,SAAS,CAAA;IAClB,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;IAC1B,UAAU,EAAE,SAAS,EAAE,CAAA;CACvB;AAED;;;GAGG;AACH,wBAAsB,qBAAqB,CAC1C,OAAO,EAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,EAIrE,WAAW,EAAE,CAAC,OAAO,EAAE,SAAS,KAAK,OAAO,CAAC,KAAK,CAAC,GACjD,OAAO,CAAC,eAAe,CAAC,CAwlB1B;AAmKD,uFAAuF;AACvF,wBAAsB,kBAAkB,CACvC,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACxE,OAAO,CAAC,eAAe,CAAC,CAG1B;AAED,oEAAoE;AACpE,wBAAsB,kBAAkB,CACvC,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACxE,OAAO,CAAC,eAAe,CAAC,CAM1B;AAED;;;;;;GAMG;AACH,wBAAsB,cAAc,CACnC,OAAO,GAAE;IACR,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;CACZ,GACJ,OAAO,CAAC,eAAe,CAAC,CA2B1B;AAED,gGAAgG;AAChG,wBAAsB,oBAAoB,CACzC,OAAO,GAAE;IAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAO,GACxE,OAAO,CAAC,eAAe,CAAC,CAG1B"}
|