@antelopejs/interface-data-api 0.4.0 → 0.6.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 +26 -22
- package/dist/antelope.test.js +1 -5
- package/dist/antelope.test.js.map +1 -1
- package/dist/components.d.ts +67 -39
- package/dist/components.js +106 -685
- package/dist/components.js.map +1 -1
- package/dist/index.d.ts +57 -68
- package/dist/index.js +114 -100
- package/dist/index.js.map +1 -1
- package/dist/internal/computed.d.ts +6 -0
- package/dist/internal/computed.js +51 -0
- package/dist/internal/computed.js.map +1 -0
- package/dist/internal/controller-types.d.ts +34 -0
- package/dist/internal/controller-types.js +4 -0
- package/dist/internal/controller-types.js.map +1 -0
- package/dist/internal/default-filter.d.ts +4 -0
- package/dist/internal/default-filter.js +29 -0
- package/dist/internal/default-filter.js.map +1 -0
- package/dist/internal/deferred-joined-stream.d.ts +11 -0
- package/dist/internal/deferred-joined-stream.js +125 -0
- package/dist/internal/deferred-joined-stream.js.map +1 -0
- package/dist/internal/extractors.d.ts +25 -0
- package/dist/internal/extractors.js +31 -0
- package/dist/internal/extractors.js.map +1 -0
- package/dist/internal/field-types.d.ts +199 -0
- package/dist/internal/field-types.js +4 -0
- package/dist/internal/field-types.js.map +1 -0
- package/dist/internal/filters.d.ts +6 -0
- package/dist/internal/filters.js +22 -0
- package/dist/internal/filters.js.map +1 -0
- package/dist/internal/foreign.d.ts +6 -0
- package/dist/internal/foreign.js +116 -0
- package/dist/internal/foreign.js.map +1 -0
- package/dist/internal/guards.d.ts +39 -0
- package/dist/internal/guards.js +116 -0
- package/dist/internal/guards.js.map +1 -0
- package/dist/internal/joined.d.ts +28 -0
- package/dist/internal/joined.js +127 -0
- package/dist/internal/joined.js.map +1 -0
- package/dist/internal/list.d.ts +16 -0
- package/dist/internal/list.js +182 -0
- package/dist/internal/list.js.map +1 -0
- package/dist/internal/meta-records.d.ts +19 -0
- package/dist/internal/meta-records.js +78 -0
- package/dist/internal/meta-records.js.map +1 -0
- package/dist/internal/properties.d.ts +4 -0
- package/dist/internal/properties.js +45 -0
- package/dist/internal/properties.js.map +1 -0
- package/dist/internal/prototypes.d.ts +14 -0
- package/dist/internal/prototypes.js +38 -0
- package/dist/internal/prototypes.js.map +1 -0
- package/dist/internal/read-properties.d.ts +3 -0
- package/dist/internal/read-properties.js +29 -0
- package/dist/internal/read-properties.js.map +1 -0
- package/dist/internal/table-reference.d.ts +11 -0
- package/dist/internal/table-reference.js +46 -0
- package/dist/internal/table-reference.js.map +1 -0
- package/dist/internal/tables.d.ts +28 -0
- package/dist/internal/tables.js +48 -0
- package/dist/internal/tables.js.map +1 -0
- package/dist/internal/validation.d.ts +12 -0
- package/dist/internal/validation.js +100 -0
- package/dist/internal/validation.js.map +1 -0
- package/dist/metadata.d.ts +27 -228
- package/dist/metadata.js +84 -213
- package/dist/metadata.js.map +1 -1
- package/dist/tests/components/access_control.test.js +28 -20
- package/dist/tests/components/access_control.test.js.map +1 -1
- package/dist/tests/components/computed.test.js +36 -27
- package/dist/tests/components/computed.test.js.map +1 -1
- package/dist/tests/components/cross_instance.test.js +8 -6
- package/dist/tests/components/cross_instance.test.js.map +1 -1
- package/dist/tests/components/deferred_joined.test.js +57 -54
- package/dist/tests/components/deferred_joined.test.js.map +1 -1
- package/dist/tests/components/extract_generic.test.d.ts +1 -0
- package/dist/tests/components/extract_generic.test.js +64 -0
- package/dist/tests/components/extract_generic.test.js.map +1 -0
- package/dist/tests/components/filter_comparisons.test.d.ts +1 -0
- package/dist/tests/components/filter_comparisons.test.js +34 -0
- package/dist/tests/components/filter_comparisons.test.js.map +1 -0
- package/dist/tests/components/filter_list_route.test.d.ts +1 -0
- package/dist/tests/components/filter_list_route.test.js +182 -0
- package/dist/tests/components/filter_list_route.test.js.map +1 -0
- package/dist/tests/components/filter_options.test.js +11 -10
- package/dist/tests/components/filter_options.test.js.map +1 -1
- package/dist/tests/components/foreign_computed.test.js +23 -14
- package/dist/tests/components/foreign_computed.test.js.map +1 -1
- package/dist/tests/components/foreign_joined.test.js +31 -27
- package/dist/tests/components/foreign_joined.test.js.map +1 -1
- package/dist/tests/components/joined.test.js +59 -45
- package/dist/tests/components/joined.test.js.map +1 -1
- package/dist/tests/components/listable.test.js +78 -58
- package/dist/tests/components/listable.test.js.map +1 -1
- package/dist/tests/components/localized_sort.test.js +14 -11
- package/dist/tests/components/localized_sort.test.js.map +1 -1
- package/dist/tests/components/mandatory.test.js +25 -25
- package/dist/tests/components/mandatory.test.js.map +1 -1
- package/dist/tests/components/modifier_key.test.js +27 -18
- package/dist/tests/components/modifier_key.test.js.map +1 -1
- package/dist/tests/components/parse_body.test.d.ts +1 -0
- package/dist/tests/components/parse_body.test.js +76 -0
- package/dist/tests/components/parse_body.test.js.map +1 -0
- package/dist/tests/components/parsed_value.test.js +23 -21
- package/dist/tests/components/parsed_value.test.js.map +1 -1
- package/dist/tests/components/partial_edit.test.js +38 -32
- package/dist/tests/components/partial_edit.test.js.map +1 -1
- package/dist/tests/components/per_action_access.test.js +20 -12
- package/dist/tests/components/per_action_access.test.js.map +1 -1
- package/dist/tests/components/prototype_named_filter.test.d.ts +1 -0
- package/dist/tests/components/prototype_named_filter.test.js +87 -0
- package/dist/tests/components/prototype_named_filter.test.js.map +1 -0
- package/dist/tests/components/validator.test.js +45 -37
- package/dist/tests/components/validator.test.js.map +1 -1
- package/dist/tests/components/written_row.test.d.ts +1 -0
- package/dist/tests/components/written_row.test.js +99 -0
- package/dist/tests/components/written_row.test.js.map +1 -0
- package/dist/tests/helpers/other-instance.d.ts +11 -1
- package/dist/tests/helpers/other-instance.js +35 -9
- package/dist/tests/helpers/other-instance.js.map +1 -1
- package/dist/tests/index/field_decorator_target.test.d.ts +1 -0
- package/dist/tests/index/field_decorator_target.test.js +20 -0
- package/dist/tests/index/field_decorator_target.test.js.map +1 -0
- package/dist/tests/index/get_guard.test.js +6 -6
- package/dist/tests/index/get_guard.test.js.map +1 -1
- package/dist/tests/index/inheritance.test.js +14 -9
- package/dist/tests/index/inheritance.test.js.map +1 -1
- package/dist/tests/index/metadata_names.test.d.ts +1 -0
- package/dist/tests/index/metadata_names.test.js +91 -0
- package/dist/tests/index/metadata_names.test.js.map +1 -0
- package/dist/tests/index/routes.test.js +49 -44
- package/dist/tests/index/routes.test.js.map +1 -1
- package/dist/tests/integration.test.d.ts +1 -1
- package/dist/tests/integration.test.js +66 -729
- package/dist/tests/integration.test.js.map +1 -1
- package/dist/tests/internal/controller-fields.test.d.ts +1 -0
- package/dist/tests/internal/controller-fields.test.js +25 -0
- package/dist/tests/internal/controller-fields.test.js.map +1 -0
- package/dist/tests/internal/http.d.ts +35 -0
- package/dist/tests/internal/http.js +56 -0
- package/dist/tests/internal/http.js.map +1 -0
- package/dist/tests/internal/shop-api.d.ts +1 -0
- package/dist/tests/internal/shop-api.js +331 -0
- package/dist/tests/internal/shop-api.js.map +1 -0
- package/dist/tests/internal/shop-schema.d.ts +162 -0
- package/dist/tests/internal/shop-schema.js +387 -0
- package/dist/tests/internal/shop-schema.js.map +1 -0
- package/dist/tests/utils.d.ts +8 -10
- package/dist/tests/utils.js +1 -3
- package/dist/tests/utils.js.map +1 -1
- package/dist/tests/utils.test.js +10 -11
- package/dist/tests/utils.test.js.map +1 -1
- package/docs/1.introduction.md +38 -35
- package/docs/10.joined.md +51 -40
- package/docs/11.computed.md +91 -47
- package/docs/2.data-controllers.md +50 -42
- package/docs/3.routes.md +156 -134
- package/docs/4.access-rights.md +48 -44
- package/docs/5.validators.md +65 -47
- package/docs/6.listable.md +53 -49
- package/docs/7.foreign-keys.md +55 -48
- package/docs/8.filters.md +65 -54
- package/docs/9.modifiers.md +29 -25
- package/package.json +34 -13
- package/skills/data-api-interface/SKILL.md +13 -3
- package/skills/data-api-interface/cases.json +99 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** @internal @packageDocumentation */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.Computed = Computed;
|
|
5
|
+
const interface_database_1 = require("@antelopejs/interface-database");
|
|
6
|
+
function collectComputedEntries(meta, only) {
|
|
7
|
+
return Object.entries(meta.fields).flatMap(([name, field]) => field.computed && (!only || only.has(name))
|
|
8
|
+
? [[name, field.computed]]
|
|
9
|
+
: []);
|
|
10
|
+
}
|
|
11
|
+
function buildComputedMappers(db, entries) {
|
|
12
|
+
const mergeExpressions = (row) => {
|
|
13
|
+
const merged = {};
|
|
14
|
+
for (const [name, computed] of entries) {
|
|
15
|
+
merged[name] = computed.expr(row, db);
|
|
16
|
+
}
|
|
17
|
+
return row.merge(merged).cast();
|
|
18
|
+
};
|
|
19
|
+
const defaulted = entries.filter(([, computed]) => computed.default !== undefined);
|
|
20
|
+
if (defaulted.length === 0) {
|
|
21
|
+
return [mergeExpressions];
|
|
22
|
+
}
|
|
23
|
+
const applyDefaults = (row) => {
|
|
24
|
+
const merged = {};
|
|
25
|
+
for (const [name, computed] of defaulted) {
|
|
26
|
+
merged[name] = row.key(name).default(computed.default);
|
|
27
|
+
}
|
|
28
|
+
return row.merge(merged).cast();
|
|
29
|
+
};
|
|
30
|
+
return [mergeExpressions, applyDefaults];
|
|
31
|
+
}
|
|
32
|
+
function Computed(db, meta, query, only) {
|
|
33
|
+
const entries = collectComputedEntries(meta, only);
|
|
34
|
+
if (entries.length === 0) {
|
|
35
|
+
return query;
|
|
36
|
+
}
|
|
37
|
+
const mappers = buildComputedMappers(db, entries);
|
|
38
|
+
if ((0, interface_database_1.IsStream)(query)) {
|
|
39
|
+
let stream = query;
|
|
40
|
+
for (const mapper of mappers) {
|
|
41
|
+
stream = stream.map(mapper);
|
|
42
|
+
}
|
|
43
|
+
return stream;
|
|
44
|
+
}
|
|
45
|
+
let datum = query;
|
|
46
|
+
for (const mapper of mappers) {
|
|
47
|
+
datum = datum.do(mapper);
|
|
48
|
+
}
|
|
49
|
+
return datum;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=computed.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"computed.js","sourceRoot":"","sources":["../../src/internal/computed.ts"],"names":[],"mappings":";AAAA,sCAAsC;;AA4EtC,4BAuBC;AAjGD,uEAMwC;AASxC,SAAS,sBAAsB,CAC7B,IAAiB,EACjB,IAAkB;IAElB,OAAO,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,OAAO,CACxC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAmB,EAAE,CACjC,KAAK,CAAC,QAAQ,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,QAAQ,CAAC,CAAC;QAC1B,CAAC,CAAC,EAAE,CACT,CAAC;AACJ,CAAC;AAED,SAAS,oBAAoB,CAC3B,EAAuB,EACvB,OAAwB;IAExB,MAAM,gBAAgB,GAAc,CAAC,GAAG,EAAE,EAAE;QAC1C,MAAM,MAAM,GAA0D,EAAE,CAAC;QACzE,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,OAAO,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAe,CAAC;IAC/C,CAAC,CAAC;IACF,MAAM,SAAS,GAAG,OAAO,CAAC,MAAM,CAC9B,CAAC,CAAC,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,KAAK,SAAS,CACjD,CAAC;IACF,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC3B,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC5B,CAAC;IACD,MAAM,aAAa,GAAc,CAAC,GAAG,EAAE,EAAE;QACvC,MAAM,MAAM,GAAoC,EAAE,CAAC;QACnD,KAAK,MAAM,CAAC,IAAI,EAAE,QAAQ,CAAC,IAAI,SAAS,EAAE,CAAC;YACzC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;QACzD,CAAC;QACD,OAAO,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAe,CAAC;IAC/C,CAAC,CAAC;IACF,OAAO,CAAC,gBAAgB,EAAE,aAAa,CAAC,CAAC;AAC3C,CAAC;AAsBD,SAAgB,QAAQ,CACtB,EAAuB,EACvB,IAAiB,EACjB,KAA+B,EAC/B,IAAkB;IAElB,MAAM,OAAO,GAAG,sBAAsB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC;IACf,CAAC;IACD,MAAM,OAAO,GAAG,oBAAoB,CAAC,EAAE,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,IAAA,6BAAQ,EAAC,KAAK,CAAC,EAAE,CAAC;QACpB,IAAI,MAAM,GAAG,KAAK,CAAC;QACnB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC7B,KAAK,GAAG,KAAK,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;IAC3B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** @internal @packageDocumentation */
|
|
2
|
+
import type { ControllerClass } from "@antelopejs/interface-api";
|
|
3
|
+
import type { ParameterDecorator } from "@antelopejs/interface-core/decorators";
|
|
4
|
+
/** The controller a data route runs on: an instance of a class built by `DataController`. */
|
|
5
|
+
export type DataControllerInstance = InstanceType<ControllerClass>;
|
|
6
|
+
/**
|
|
7
|
+
* One route of a data controller. Entry `i` of `args` injects parameter `i`
|
|
8
|
+
* of `func`, so `A` lists what `func` receives, one element per entry; the
|
|
9
|
+
* stock routes take the request context first and their parameters second.
|
|
10
|
+
* Without `A`, the route is one whose parameters are not known, as a
|
|
11
|
+
* {@link DataControllerDef} holds it: its `func` takes `O` second and anything
|
|
12
|
+
* else, unchecked.
|
|
13
|
+
*
|
|
14
|
+
* @typeParam O The options the route receives in its second parameter
|
|
15
|
+
* @typeParam R What `func` answers, resolved
|
|
16
|
+
* @typeParam A The parameters of `func`, one per entry of `args`
|
|
17
|
+
*/
|
|
18
|
+
export type DataControllerCallback<O = any, R = unknown, A extends unknown[] = [ctx: any, opts: O, ...args: any[]]> = {
|
|
19
|
+
args: (ParameterDecorator | ParameterDecorator[])[];
|
|
20
|
+
method: string;
|
|
21
|
+
func: (...args: A) => R | Promise<R>;
|
|
22
|
+
};
|
|
23
|
+
export type DataControllerCallbackWithOptions<O = any, R = unknown, A extends unknown[] = [ctx: any, opts: O, ...args: any[]]> = {
|
|
24
|
+
endpoint?: string;
|
|
25
|
+
options?: Partial<O>;
|
|
26
|
+
callback: DataControllerCallback<O, R, A>;
|
|
27
|
+
};
|
|
28
|
+
export type ExtractCallback<T> = T extends DataControllerCallbackWithOptions ? T["callback"]["func"] : T extends DataControllerCallback ? T["func"] : never;
|
|
29
|
+
export type DataControllerDef = {
|
|
30
|
+
[name: string]: DataControllerCallback | DataControllerCallbackWithOptions;
|
|
31
|
+
};
|
|
32
|
+
export type ExtractDefCallbacks<T extends {}> = {
|
|
33
|
+
[K in keyof T]: ExtractCallback<T[K]>;
|
|
34
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"controller-types.js","sourceRoot":"","sources":["../../src/internal/controller-types.ts"],"names":[],"mappings":";AAAA,sCAAsC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** @internal @packageDocumentation */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.createDefaultFilter = createDefaultFilter;
|
|
5
|
+
const interface_api_util_1 = require("@antelopejs/interface-api-util");
|
|
6
|
+
const meta_records_1 = require("./meta-records");
|
|
7
|
+
const DEFAULT_FILTER_OPERATORS = {
|
|
8
|
+
eq: (proxy, value) => proxy.eq(value),
|
|
9
|
+
ne: (proxy, value) => proxy.ne(value),
|
|
10
|
+
gt: (proxy, value) => proxy.gt(value),
|
|
11
|
+
ge: (proxy, value) => proxy.ge(value),
|
|
12
|
+
lt: (proxy, value) => proxy.lt(value),
|
|
13
|
+
le: (proxy, value) => proxy.le(value),
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Applies a comparison to the column.
|
|
17
|
+
*
|
|
18
|
+
* @throws HTTPResult 400 when `mode` is not one of the default filter's
|
|
19
|
+
* comparisons; a custom filter receives its mode unchecked
|
|
20
|
+
*/
|
|
21
|
+
function applyDefaultFilterMode(proxy, value, mode) {
|
|
22
|
+
const operator = (0, meta_records_1.ownEntry)(DEFAULT_FILTER_OPERATORS, mode);
|
|
23
|
+
(0, interface_api_util_1.assert)(operator, 400, `Unknown filter comparison "${mode}": use one of ${Object.keys(DEFAULT_FILTER_OPERATORS).join(", ")}, or start a value that contains a colon with "eq:".`);
|
|
24
|
+
return operator(proxy, value);
|
|
25
|
+
}
|
|
26
|
+
function createDefaultFilter() {
|
|
27
|
+
return (_context, proxy, _key, value, mode) => applyDefaultFilterMode(proxy, value, mode);
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=default-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"default-filter.js","sourceRoot":"","sources":["../../src/internal/default-filter.ts"],"names":[],"mappings":";AAAA,sCAAsC;;AAkDtC,kDAGC;AAnDD,uEAAwD;AAQxD,iDAA0C;AAW1C,MAAM,wBAAwB,GAA2B;IACvD,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;IACrC,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;IACrC,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;IACrC,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;IACrC,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;IACrC,EAAE,EAAE,CAAC,KAAK,EAAE,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,KAAK,CAAC;CACtC,CAAC;AAEF;;;;;GAKG;AACH,SAAS,sBAAsB,CAC7B,KAA0B,EAC1B,KAAa,EACb,IAAoB;IAEpB,MAAM,QAAQ,GAAG,IAAA,uBAAQ,EAAC,wBAAwB,EAAE,IAAI,CAAC,CAAC;IAC1D,IAAA,2BAAM,EACJ,QAAQ,EACR,GAAG,EACH,8BAA8B,IAAI,iBAAiB,MAAM,CAAC,IAAI,CAAC,wBAAwB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,sDAAsD,CAC1J,CAAC;IACF,OAAO,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAChC,CAAC;AAED,SAAgB,mBAAmB;IACjC,OAAO,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE,CAC5C,sBAAsB,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC;AAC/C,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** @internal @packageDocumentation */
|
|
2
|
+
import { type SchemaInstance, Stream } from "@antelopejs/interface-database";
|
|
3
|
+
import type { DataAPIMeta } from "../metadata";
|
|
4
|
+
/**
|
|
5
|
+
* Wraps `raw` into a {@link DeferredJoinedStream}. The wrapper keeps the
|
|
6
|
+
* raw stages as its own (an unobservable fallback, since every operation
|
|
7
|
+
* is overridden) while `build()` serializes the materialized pipeline, so
|
|
8
|
+
* a wrapper embedded as an argument of another query never leaks rows
|
|
9
|
+
* without the joined fields.
|
|
10
|
+
*/
|
|
11
|
+
export declare function wrapDeferredJoined(db: SchemaInstance<any>, meta: DataAPIMeta, names: Set<string>, raw: Stream<any>): Stream<any>;
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** @internal @packageDocumentation */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.wrapDeferredJoined = wrapDeferredJoined;
|
|
5
|
+
const interface_database_1 = require("@antelopejs/interface-database");
|
|
6
|
+
const joined_1 = require("./joined");
|
|
7
|
+
/**
|
|
8
|
+
* Stream returned by {@link List} when the deferred joined fields are not
|
|
9
|
+
* exposed to the caller: it resolves them itself, so consumers that
|
|
10
|
+
* destructure `[query, total]` alone still observe every `@Joined` field.
|
|
11
|
+
* The lookups are appended after `slice`/`nth`, keeping them on the
|
|
12
|
+
* requested page only; bare `count()` calls and field aggregates on
|
|
13
|
+
* non-joined fields skip them (left-join lookups never change the row
|
|
14
|
+
* count nor other fields); `changes()` attaches to the raw stream, since
|
|
15
|
+
* change feeds cannot carry the lookups. Any other operation may observe
|
|
16
|
+
* the joined fields, so it materializes them first.
|
|
17
|
+
*/
|
|
18
|
+
class DeferredJoinedStream extends interface_database_1.Stream {
|
|
19
|
+
}
|
|
20
|
+
function materializeDeferredJoin(stream) {
|
|
21
|
+
const { db, meta, names, raw } = stream.deferredJoin;
|
|
22
|
+
return (0, joined_1.Joined)(db, meta, raw, names);
|
|
23
|
+
}
|
|
24
|
+
function pagedDeferredJoinOperation() {
|
|
25
|
+
return function (...args) {
|
|
26
|
+
const { db, meta, names, raw } = this.deferredJoin;
|
|
27
|
+
return (0, joined_1.Joined)(db, meta, raw.slice(...args), names);
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Resolves the joined fields through the stream form of {@link Joined}
|
|
32
|
+
* rather than its datum form: only the datum form has no way to drop the
|
|
33
|
+
* temporary key it stages (neither `Datum` nor `ValueProxy` exposes
|
|
34
|
+
* `without`), so it would leave `__joined_orig_*` on the row.
|
|
35
|
+
*/
|
|
36
|
+
function nthDeferredJoinOperation() {
|
|
37
|
+
return function (n) {
|
|
38
|
+
const { db, meta, names, raw } = this.deferredJoin;
|
|
39
|
+
return (0, joined_1.Joined)(db, meta, raw.slice(n, 1), names).nth(0);
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function rawDeferredJoinOperation() {
|
|
43
|
+
return function () {
|
|
44
|
+
return this.deferredJoin.raw.changes();
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function aggregate(stream, name, field) {
|
|
48
|
+
switch (name) {
|
|
49
|
+
case "count":
|
|
50
|
+
return stream.count(field);
|
|
51
|
+
case "sum":
|
|
52
|
+
return stream.sum(field);
|
|
53
|
+
case "avg":
|
|
54
|
+
return stream.avg(field);
|
|
55
|
+
case "min":
|
|
56
|
+
return stream.min(field);
|
|
57
|
+
case "max":
|
|
58
|
+
return stream.max(field);
|
|
59
|
+
case "distinct":
|
|
60
|
+
return field === undefined ? stream.distinct() : stream.distinct(field);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
function fieldAggregateDeferredJoinOperation(name, bareCallOnRaw) {
|
|
64
|
+
return function (field) {
|
|
65
|
+
const { db, meta, names, raw } = this.deferredJoin;
|
|
66
|
+
if (!field) {
|
|
67
|
+
const bareTarget = bareCallOnRaw ? raw : materializeDeferredJoin(this);
|
|
68
|
+
return aggregate(bareTarget, name);
|
|
69
|
+
}
|
|
70
|
+
const target = names.has(field)
|
|
71
|
+
? (0, joined_1.Joined)(db, meta, raw, new Set([field]))
|
|
72
|
+
: raw;
|
|
73
|
+
return aggregate(target, name, field);
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function forwardingDeferredJoinOperation(name) {
|
|
77
|
+
return function (...args) {
|
|
78
|
+
// SAFETY: name comes from wireDeferredJoinedStream, which lists the own method names of Stream.prototype plus run, cursor and build, all present on any Stream, and Joined returns a Stream for the raw Stream it is given.
|
|
79
|
+
// oxlint-disable-next-line typescript/no-unsafe-return -- the method is looked up by a runtime name on a Stream, which TypeScript cannot relate to a signature; Reflect.apply and a typed lookup were tried and moved the finding to no-reflect-get, no-reflect-apply and no-unknown-returns
|
|
80
|
+
return materializeDeferredJoin(this)[name](...args);
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** The operation a {@link DeferredJoinedStream} installs for a stream method. */
|
|
84
|
+
const DEFERRED_JOIN_OPERATION_FACTORIES = new Map([
|
|
85
|
+
["slice", pagedDeferredJoinOperation],
|
|
86
|
+
["nth", nthDeferredJoinOperation],
|
|
87
|
+
["changes", rawDeferredJoinOperation],
|
|
88
|
+
["count", () => fieldAggregateDeferredJoinOperation("count", true)],
|
|
89
|
+
["sum", () => fieldAggregateDeferredJoinOperation("sum", false)],
|
|
90
|
+
["avg", () => fieldAggregateDeferredJoinOperation("avg", false)],
|
|
91
|
+
["min", () => fieldAggregateDeferredJoinOperation("min", false)],
|
|
92
|
+
["max", () => fieldAggregateDeferredJoinOperation("max", false)],
|
|
93
|
+
["distinct", () => fieldAggregateDeferredJoinOperation("distinct", false)],
|
|
94
|
+
]);
|
|
95
|
+
const DEFERRED_JOIN_INHERITED_OPERATIONS = new Set(["constructor", "cast"]);
|
|
96
|
+
function wireDeferredJoinedStream() {
|
|
97
|
+
const operationNames = [
|
|
98
|
+
...Object.getOwnPropertyNames(interface_database_1.Stream.prototype),
|
|
99
|
+
"run",
|
|
100
|
+
"cursor",
|
|
101
|
+
"build",
|
|
102
|
+
].filter((name) => !DEFERRED_JOIN_INHERITED_OPERATIONS.has(name));
|
|
103
|
+
for (const name of operationNames) {
|
|
104
|
+
const factory = DEFERRED_JOIN_OPERATION_FACTORIES.get(name) ??
|
|
105
|
+
forwardingDeferredJoinOperation;
|
|
106
|
+
// SAFETY: name is a method name of Stream.prototype (or run, cursor, build), so the key is a writable data property on DeferredJoinedStream's prototype chain and assigning a function to it cannot throw; each installed operation takes the arguments of the Stream method it replaces.
|
|
107
|
+
DeferredJoinedStream.prototype[name] = factory(name);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
wireDeferredJoinedStream();
|
|
111
|
+
/**
|
|
112
|
+
* Wraps `raw` into a {@link DeferredJoinedStream}. The wrapper keeps the
|
|
113
|
+
* raw stages as its own (an unobservable fallback, since every operation
|
|
114
|
+
* is overridden) while `build()` serializes the materialized pipeline, so
|
|
115
|
+
* a wrapper embedded as an argument of another query never leaks rows
|
|
116
|
+
* without the joined fields.
|
|
117
|
+
*/
|
|
118
|
+
function wrapDeferredJoined(db, meta, names, raw) {
|
|
119
|
+
const stages = raw.build();
|
|
120
|
+
const wrapped = new DeferredJoinedStream(stages[0]);
|
|
121
|
+
Object.assign(wrapped, { stages });
|
|
122
|
+
wrapped.deferredJoin = { db, meta, names, raw };
|
|
123
|
+
return wrapped;
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=deferred-joined-stream.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"deferred-joined-stream.js","sourceRoot":"","sources":["../../src/internal/deferred-joined-stream.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** @internal @packageDocumentation */
|
|
2
|
+
import type { RequestContext } from "@antelopejs/interface-api";
|
|
3
|
+
import type { DataAPIMeta } from "../metadata";
|
|
4
|
+
/**
|
|
5
|
+
* Reads one field of a route's parameters from the request. `undefined` leaves
|
|
6
|
+
* the field out. `key` is the field's name.
|
|
7
|
+
*/
|
|
8
|
+
export type ParameterExtractor<Value> = (reqCtx: RequestContext, meta: DataAPIMeta, key: string) => Value | undefined;
|
|
9
|
+
/** The extractors of a route's parameters, by field. */
|
|
10
|
+
export type ParameterExtractors<T> = {
|
|
11
|
+
[K in keyof T]?: ParameterExtractor<T[K]>;
|
|
12
|
+
};
|
|
13
|
+
export declare function fromSearch<Value>(convert: (text: string) => Value): ParameterExtractor<Value>;
|
|
14
|
+
/** Reads a field from the query string, under the field's name. */
|
|
15
|
+
export declare const FromSearch: {
|
|
16
|
+
number: ParameterExtractor<number>;
|
|
17
|
+
int: ParameterExtractor<number>;
|
|
18
|
+
bool: ParameterExtractor<boolean>;
|
|
19
|
+
string: ParameterExtractor<string>;
|
|
20
|
+
/** Every value of the field, none when the request sends none. */
|
|
21
|
+
numbers: ParameterExtractor<number[]>;
|
|
22
|
+
ints: ParameterExtractor<number[]>;
|
|
23
|
+
bools: ParameterExtractor<boolean[]>;
|
|
24
|
+
strings: ParameterExtractor<string[]>;
|
|
25
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** @internal @packageDocumentation */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.FromSearch = void 0;
|
|
5
|
+
exports.fromSearch = fromSearch;
|
|
6
|
+
function fromSearch(convert) {
|
|
7
|
+
return (reqCtx, _meta, key) => {
|
|
8
|
+
const text = reqCtx.url.searchParams.get(key);
|
|
9
|
+
return text === null ? undefined : convert(text);
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
function allFromSearch(convert) {
|
|
13
|
+
return (reqCtx, _meta, key) => reqCtx.url.searchParams.getAll(key).map((text) => convert(text));
|
|
14
|
+
}
|
|
15
|
+
const toNumber = (text) => parseFloat(text);
|
|
16
|
+
const toInt = (text) => parseInt(text, 10);
|
|
17
|
+
const toBool = (text) => text !== "0";
|
|
18
|
+
const toText = (text) => text;
|
|
19
|
+
/** Reads a field from the query string, under the field's name. */
|
|
20
|
+
exports.FromSearch = {
|
|
21
|
+
number: fromSearch(toNumber),
|
|
22
|
+
int: fromSearch(toInt),
|
|
23
|
+
bool: fromSearch(toBool),
|
|
24
|
+
string: fromSearch(toText),
|
|
25
|
+
/** Every value of the field, none when the request sends none. */
|
|
26
|
+
numbers: allFromSearch(toNumber),
|
|
27
|
+
ints: allFromSearch(toInt),
|
|
28
|
+
bools: allFromSearch(toBool),
|
|
29
|
+
strings: allFromSearch(toText),
|
|
30
|
+
};
|
|
31
|
+
//# sourceMappingURL=extractors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractors.js","sourceRoot":"","sources":["../../src/internal/extractors.ts"],"names":[],"mappings":";AAAA,sCAAsC;;;AAqBtC,gCAOC;AAPD,SAAgB,UAAU,CACxB,OAAgC;IAEhC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9C,OAAO,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,CAAC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CACpB,OAAgC;IAEhC,OAAO,CAAC,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE,CAC5B,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AACrE,CAAC;AAED,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;AACpD,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;AACnD,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,KAAK,GAAG,CAAC;AAC9C,MAAM,MAAM,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,CAAC;AAEtC,mEAAmE;AACtD,QAAA,UAAU,GAAG;IACxB,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC;IAC5B,GAAG,EAAE,UAAU,CAAC,KAAK,CAAC;IACtB,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC;IACxB,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC;IAC1B,kEAAkE;IAClE,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC;IAChC,IAAI,EAAE,aAAa,CAAC,KAAK,CAAC;IAC1B,KAAK,EAAE,aAAa,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE,aAAa,CAAC,MAAM,CAAC;CAC/B,CAAC"}
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
/** @internal @packageDocumentation */
|
|
2
|
+
import type { RequestContext } from "@antelopejs/interface-api";
|
|
3
|
+
import type { Class } from "@antelopejs/interface-core/decorators";
|
|
4
|
+
import type { Query, SchemaInstance, ValueProxy, ValueProxyOrValue } from "@antelopejs/interface-database";
|
|
5
|
+
import type { Table } from "@antelopejs/interface-database-decorators";
|
|
6
|
+
import type { AccessMode, DataAPIMeta } from "../metadata";
|
|
7
|
+
/**
|
|
8
|
+
* A `@Joined` field declared on the target of a relation, captured so the
|
|
9
|
+
* foreign lookup can re-materialize it (joined fields are not physical columns,
|
|
10
|
+
* so a plain pluck of the target table would return them empty).
|
|
11
|
+
*/
|
|
12
|
+
export interface ForeignJoinedRef {
|
|
13
|
+
name: string;
|
|
14
|
+
table: string;
|
|
15
|
+
tableClass?: Class<Table>;
|
|
16
|
+
localKey: string;
|
|
17
|
+
remoteField: string;
|
|
18
|
+
remoteIndex: string;
|
|
19
|
+
schemaName?: string;
|
|
20
|
+
}
|
|
21
|
+
/** A value a request body or a table row holds. */
|
|
22
|
+
export type FieldValue = string | number | boolean | null | Date | FieldValue[] | FieldValues;
|
|
23
|
+
/** Fields by name, as a request body or a table row holds them. */
|
|
24
|
+
export interface FieldValues {
|
|
25
|
+
[field: string]: FieldValue | undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* The fields of a table row a route holds, by name. The table class decides
|
|
29
|
+
* what they hold: a relation holds a row instance, and a getter's result is
|
|
30
|
+
* what the getter returned. Read a field through a guard.
|
|
31
|
+
*/
|
|
32
|
+
export interface RowFields {
|
|
33
|
+
[field: string]: unknown;
|
|
34
|
+
}
|
|
35
|
+
/** Parse outcome accepting the field value, carrying the value to write. */
|
|
36
|
+
export interface FieldParseSuccess {
|
|
37
|
+
success: true;
|
|
38
|
+
data: FieldValue | undefined;
|
|
39
|
+
}
|
|
40
|
+
/** Parse outcome rejecting the field value. */
|
|
41
|
+
export interface FieldParseFailure {
|
|
42
|
+
success: false;
|
|
43
|
+
}
|
|
44
|
+
/** Parse outcome of a field value, shaped like the result of zod's `safeParse`. */
|
|
45
|
+
export type FieldParseResult = FieldParseSuccess | FieldParseFailure;
|
|
46
|
+
/**
|
|
47
|
+
* Outcome of a field validator: `true`/`false` accepts or rejects the raw
|
|
48
|
+
* value, which is then written as received; a {@link FieldParseResult}
|
|
49
|
+
* accepts or rejects it, and on success its `data` is written instead of the
|
|
50
|
+
* raw value.
|
|
51
|
+
*/
|
|
52
|
+
export type FieldValidationResult = boolean | FieldParseResult;
|
|
53
|
+
/** Field value validator, see {@link FieldValidationResult}. */
|
|
54
|
+
export type FieldValidator = (value: FieldValue | undefined) => FieldValidationResult | Promise<FieldValidationResult>;
|
|
55
|
+
/** What a foreign key declaration takes after the field and the other table. */
|
|
56
|
+
export type ForeignOptions = [
|
|
57
|
+
index?: string,
|
|
58
|
+
multi?: boolean,
|
|
59
|
+
pluck?: string[],
|
|
60
|
+
schema?: string,
|
|
61
|
+
targetMeta?: DataAPIMeta
|
|
62
|
+
];
|
|
63
|
+
/** DataAPI Metadata field information. */
|
|
64
|
+
export interface FieldData {
|
|
65
|
+
/**
|
|
66
|
+
* Field name in-database.
|
|
67
|
+
*/
|
|
68
|
+
dbName?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Field access mode.
|
|
71
|
+
*
|
|
72
|
+
* @see {@link AccessMode}
|
|
73
|
+
*/
|
|
74
|
+
mode?: AccessMode;
|
|
75
|
+
/**
|
|
76
|
+
* Field access mode overrides per DataAPI action.
|
|
77
|
+
*/
|
|
78
|
+
modeOverrides?: Record<string, AccessMode>;
|
|
79
|
+
/**
|
|
80
|
+
* DB Fields that should be selected for this field.
|
|
81
|
+
*/
|
|
82
|
+
listable?: Record<string, string[]>;
|
|
83
|
+
/**
|
|
84
|
+
* Set of api methods for which the field must be set.
|
|
85
|
+
*/
|
|
86
|
+
mandatory?: Set<string>;
|
|
87
|
+
/**
|
|
88
|
+
* Whether the DataAPI can be sorted using this field.
|
|
89
|
+
*/
|
|
90
|
+
sortable?: {
|
|
91
|
+
indexed?: boolean;
|
|
92
|
+
};
|
|
93
|
+
/**
|
|
94
|
+
* Foreign key reference.
|
|
95
|
+
*
|
|
96
|
+
* `schemaName` is set when the target lives in another schema than the
|
|
97
|
+
* controller's table; runtime resolves the foreign lookup against that
|
|
98
|
+
* schema's default instance instead of the source instance.
|
|
99
|
+
*/
|
|
100
|
+
foreign?: [
|
|
101
|
+
table: string,
|
|
102
|
+
tableClass?: Class<Table>,
|
|
103
|
+
index?: string,
|
|
104
|
+
multi?: true,
|
|
105
|
+
pluck?: string[],
|
|
106
|
+
schemaName?: string,
|
|
107
|
+
targetJoined?: ForeignJoinedRef[]
|
|
108
|
+
];
|
|
109
|
+
/**
|
|
110
|
+
* Joined-from-other-table reference.
|
|
111
|
+
*
|
|
112
|
+
* Imports a single scalar field from another table at query time and
|
|
113
|
+
* flattens it into the row, enabling sort/search/filter natively in DB.
|
|
114
|
+
*
|
|
115
|
+
* `schemaName` is set when the target lives in another schema than the
|
|
116
|
+
* controller's table; runtime resolves the join against that schema's
|
|
117
|
+
* default instance instead of the source instance.
|
|
118
|
+
*/
|
|
119
|
+
joined?: {
|
|
120
|
+
table: string;
|
|
121
|
+
tableClass?: Class<Table>;
|
|
122
|
+
localKey: string;
|
|
123
|
+
remoteField: string;
|
|
124
|
+
remoteIndex: string;
|
|
125
|
+
schemaName?: string;
|
|
126
|
+
};
|
|
127
|
+
/**
|
|
128
|
+
* In-database computed value.
|
|
129
|
+
*
|
|
130
|
+
* The expression is merged into each row at query time, enabling sort,
|
|
131
|
+
* filter, and pagination natively in DB.
|
|
132
|
+
*/
|
|
133
|
+
computed?: ComputedFieldData;
|
|
134
|
+
/**
|
|
135
|
+
* Value validator callback.
|
|
136
|
+
*/
|
|
137
|
+
validator?: FieldValidator;
|
|
138
|
+
/**
|
|
139
|
+
* Field property descriptor.
|
|
140
|
+
*/
|
|
141
|
+
desc?: PropertyDescriptor;
|
|
142
|
+
/**
|
|
143
|
+
* Whether or not an `eq` filter with this field name can use an indexed lookup.
|
|
144
|
+
*/
|
|
145
|
+
indexable?: boolean;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* In-database computed field expression.
|
|
149
|
+
*
|
|
150
|
+
* Receives the row proxy and the schema instance of the controller's table.
|
|
151
|
+
* May return a row-local expression (built from `row`), a per-row subquery
|
|
152
|
+
* (ex: an aggregate over another table), or a constant. A value proxy is
|
|
153
|
+
* invariant in its value, so the proxy and the query take any value.
|
|
154
|
+
*/
|
|
155
|
+
export type ComputedExpression = (row: ValueProxy<FieldValues>, db: SchemaInstance<any>) => ValueProxy<any> | Query<any> | FieldValue;
|
|
156
|
+
/**
|
|
157
|
+
* Computed field options.
|
|
158
|
+
*/
|
|
159
|
+
export interface ComputedOptions {
|
|
160
|
+
/**
|
|
161
|
+
* Fallback merged over the computed value when the expression yields null
|
|
162
|
+
* (ex: `0` for an aggregate count over an empty set).
|
|
163
|
+
*/
|
|
164
|
+
default?: FieldValue;
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Computed field information.
|
|
168
|
+
*/
|
|
169
|
+
export interface ComputedFieldData extends ComputedOptions {
|
|
170
|
+
expr: ComputedExpression;
|
|
171
|
+
}
|
|
172
|
+
/** The comparisons the default filter applies. */
|
|
173
|
+
export type Comparison = "eq" | "ne" | "gt" | "ge" | "lt" | "le";
|
|
174
|
+
/**
|
|
175
|
+
* One filter of a list request: the value, and the mode to compare it with.
|
|
176
|
+
* The mode is text: what the request sent, or what the route wrote. The
|
|
177
|
+
* default filter refuses a mode that is not a {@link Comparison}. A custom
|
|
178
|
+
* filter defines its own modes.
|
|
179
|
+
*/
|
|
180
|
+
export type FilterValue = [value: string, mode: string];
|
|
181
|
+
/**
|
|
182
|
+
* The filters of one field in a list request: one {@link FilterValue}, or
|
|
183
|
+
* several a row must all pass (`?filter_age=ge:18&filter_age=lt:65`).
|
|
184
|
+
*/
|
|
185
|
+
export type FieldFilter = FilterValue | FilterValue[];
|
|
186
|
+
/**
|
|
187
|
+
* Filter callback.
|
|
188
|
+
*/
|
|
189
|
+
export type FilterFunction<T extends Record<string, any>, U extends Record<string, any> = FieldValues> = (context: RequestContext & {
|
|
190
|
+
this: T;
|
|
191
|
+
}, proxy: ValueProxy<any>, key: string, ...rest: [value: FilterValue[0], mode: FilterValue[1], row: ValueProxy<U>]) => ValueProxyOrValue<boolean>;
|
|
192
|
+
export interface ReadableAccessFields {
|
|
193
|
+
getters: [string, FieldData][];
|
|
194
|
+
props: [string, FieldData][];
|
|
195
|
+
}
|
|
196
|
+
export interface WritableAccessFields {
|
|
197
|
+
setters: [string, FieldData][];
|
|
198
|
+
props: [string, FieldData][];
|
|
199
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"field-types.js","sourceRoot":"","sources":["../../src/internal/field-types.ts"],"names":[],"mappings":";AAAA,sCAAsC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** @internal @packageDocumentation */
|
|
2
|
+
import type { FieldFilter, FilterValue } from "./field-types";
|
|
3
|
+
/** The filters of one field as a list, whether it holds one or several. */
|
|
4
|
+
export declare function FilterValuesOf(filter: FieldFilter): FilterValue[];
|
|
5
|
+
export declare function parseFilter(searchVal: string): FilterValue;
|
|
6
|
+
export declare function copyFilter(filter: FieldFilter): FieldFilter;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** @internal @packageDocumentation */
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.FilterValuesOf = FilterValuesOf;
|
|
5
|
+
exports.parseFilter = parseFilter;
|
|
6
|
+
exports.copyFilter = copyFilter;
|
|
7
|
+
function isFilterList(filter) {
|
|
8
|
+
return Array.isArray(filter[0]);
|
|
9
|
+
}
|
|
10
|
+
/** The filters of one field as a list, whether it holds one or several. */
|
|
11
|
+
function FilterValuesOf(filter) {
|
|
12
|
+
return isFilterList(filter) ? filter : [filter];
|
|
13
|
+
}
|
|
14
|
+
function parseFilter(searchVal) {
|
|
15
|
+
const match = searchVal.match(/([^:]+):(.*)/);
|
|
16
|
+
return match ? [match[2], match[1]] : [searchVal, "eq"];
|
|
17
|
+
}
|
|
18
|
+
function copyFilter(filter) {
|
|
19
|
+
const copies = FilterValuesOf(filter).map((value) => [...value]);
|
|
20
|
+
return copies.length === 1 ? copies[0] : copies;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=filters.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filters.js","sourceRoot":"","sources":["../../src/internal/filters.ts"],"names":[],"mappings":";AAAA,sCAAsC;;AAStC,wCAEC;AAED,kCAGC;AAED,gCAGC;AAjBD,SAAS,YAAY,CAAC,MAAmB;IACvC,OAAO,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAClC,CAAC;AAED,2EAA2E;AAC3E,SAAgB,cAAc,CAAC,MAAmB;IAChD,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;AAClD,CAAC;AAED,SAAgB,WAAW,CAAC,SAAiB;IAC3C,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;AAC1D,CAAC;AAED,SAAgB,UAAU,CAAC,MAAmB;IAC5C,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAe,EAAE,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC;IAC9E,OAAO,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;AAClD,CAAC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** @internal @packageDocumentation */
|
|
2
|
+
import { type Datum, type SchemaInstance, type Stream } from "@antelopejs/interface-database";
|
|
3
|
+
import type { DataAPIMeta } from "../metadata";
|
|
4
|
+
export declare function Foreign(db: SchemaInstance<any>, meta: DataAPIMeta, query: Stream<any>, pluck?: Set<string>): Stream<any>;
|
|
5
|
+
export declare function Foreign(db: SchemaInstance<any>, meta: DataAPIMeta, query: Datum<any>, pluck?: Set<string>): Datum<any>;
|
|
6
|
+
export declare function Foreign(db: SchemaInstance<any>, meta: DataAPIMeta, query: Stream<any> | Datum<any>, pluck?: Set<string>): Stream<any> | Datum<any>;
|