@owlmeans/resource 0.1.18-rc.2 → 0.1.18-rc.21
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 +355 -76
- package/agent-meta/manifest.json +9 -2
- package/agent-meta/skills/resource/SKILL.md +143 -11
- package/agent-meta/skills/resource-choice/SKILL.md +264 -0
- package/build/consts.d.ts.map +1 -1
- package/build/consts.js +4 -27
- package/build/consts.js.map +1 -1
- package/build/helper.d.ts +0 -2
- package/build/helper.d.ts.map +1 -1
- package/build/helper.js +0 -24
- package/build/helper.js.map +1 -1
- package/build/index.d.ts +1 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +1 -0
- package/build/index.js.map +1 -1
- package/build/types.d.ts +125 -44
- package/build/types.d.ts.map +1 -1
- package/build/utils/index.d.ts +1 -0
- package/build/utils/index.d.ts.map +1 -1
- package/build/utils/index.js +1 -0
- package/build/utils/index.js.map +1 -1
- package/build/utils/name.d.ts +1 -1
- package/build/utils/name.d.ts.map +1 -1
- package/build/utils/name.js +1 -19
- package/build/utils/name.js.map +1 -1
- package/build/utils/query.d.ts +29 -0
- package/build/utils/query.d.ts.map +1 -0
- package/build/utils/query.js +270 -0
- package/build/utils/query.js.map +1 -0
- package/package.json +3 -5
- package/src/consts.ts +4 -27
- package/src/helper.ts +0 -28
- package/src/index.ts +1 -0
- package/src/types.ts +125 -46
- package/src/utils/index.ts +1 -0
- package/src/utils/name.ts +3 -25
- package/src/utils/query.ts +277 -0
- package/build/.gitkeep +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","sourceRoot":"","sources":["../../src/utils/query.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@owlmeans/resource",
|
|
3
|
-
"version": "0.1.18-rc.
|
|
3
|
+
"version": "0.1.18-rc.21",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -21,10 +21,8 @@
|
|
|
21
21
|
}
|
|
22
22
|
},
|
|
23
23
|
"dependencies": {
|
|
24
|
-
"@
|
|
25
|
-
"@owlmeans/
|
|
26
|
-
"@owlmeans/error": "^0.1.18-rc.2",
|
|
27
|
-
"@scure/base": "^2.3.0"
|
|
24
|
+
"@owlmeans/context": "^0.1.18-rc.20",
|
|
25
|
+
"@owlmeans/error": "^0.1.18-rc.20"
|
|
28
26
|
},
|
|
29
27
|
"peerDependencies": {
|
|
30
28
|
"ajv": "*"
|
package/src/consts.ts
CHANGED
|
@@ -34,33 +34,10 @@ export const createListSchema = <T extends ResourceRecord>(schema: JSONSchemaTyp
|
|
|
34
34
|
additionalProperties: false,
|
|
35
35
|
}
|
|
36
36
|
},
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
sort: {
|
|
41
|
-
type: 'array',
|
|
42
|
-
items: {
|
|
43
|
-
oneOf: [
|
|
44
|
-
{ type: 'string' },
|
|
45
|
-
{
|
|
46
|
-
type: 'array', items: [
|
|
47
|
-
{ type: 'string' },
|
|
48
|
-
{ type: 'boolean', nullable: true }
|
|
49
|
-
], minItems: 1, maxItems: 2
|
|
50
|
-
}
|
|
51
|
-
]
|
|
52
|
-
},
|
|
53
|
-
nullable: true
|
|
54
|
-
},
|
|
55
|
-
page: { type: 'number', nullable: true },
|
|
56
|
-
size: { type: 'number', nullable: true },
|
|
57
|
-
total: { type: 'number', nullable: true }
|
|
58
|
-
},
|
|
59
|
-
required: [],
|
|
60
|
-
nullable: true,
|
|
61
|
-
additionalProperties: false,
|
|
62
|
-
}
|
|
37
|
+
total: { type: 'number' },
|
|
38
|
+
page: { type: 'number', nullable: true },
|
|
39
|
+
size: { type: 'number', nullable: true }
|
|
63
40
|
},
|
|
64
|
-
required: ['items'],
|
|
41
|
+
required: ['items', 'total'],
|
|
65
42
|
additionalProperties: false,
|
|
66
43
|
})
|
package/src/helper.ts
CHANGED
|
@@ -1,31 +1,3 @@
|
|
|
1
|
-
import type { ListCriteria, ListOptions, ListPager } from './types.js'
|
|
2
|
-
|
|
3
|
-
export const prepareListOptions = (defPageSize: number = 10, criteria?: ListOptions | ListCriteria, opts?: ListOptions): ListOptions => {
|
|
4
|
-
if (opts == null) {
|
|
5
|
-
if (criteria == null) {
|
|
6
|
-
criteria = {}
|
|
7
|
-
opts = {}
|
|
8
|
-
} else if ('criteria' in criteria || 'pager' in criteria) {
|
|
9
|
-
/**
|
|
10
|
-
* Both keys have to be read off the original object before it's reassigned —
|
|
11
|
-
* reading `pager` after narrowing to `.criteria` silently drops the pager.
|
|
12
|
-
*/
|
|
13
|
-
const options = criteria as ListOptions
|
|
14
|
-
opts = { pager: options.pager }
|
|
15
|
-
criteria = (options.criteria ?? {}) as ListCriteria
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const size = (opts?.pager?.size ?? defPageSize)
|
|
20
|
-
const pager: ListPager = {
|
|
21
|
-
sort: opts?.pager?.sort,
|
|
22
|
-
page: opts?.pager?.page ?? 0,
|
|
23
|
-
size, total: 0
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return { criteria: criteria as ListCriteria, pager }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
1
|
export const filterObject = <T extends {}>(obj: T, keep?: string[]): T =>
|
|
30
2
|
Object.entries(obj).reduce((obj, [key, value]) => {
|
|
31
3
|
if (value == null && !keep?.includes(key)) {
|
package/src/index.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -1,63 +1,150 @@
|
|
|
1
1
|
import type { BasicConfig, BasicContext, BasicResource, LazyService } from '@owlmeans/context'
|
|
2
2
|
import type { MigrationStage } from './consts.js'
|
|
3
3
|
|
|
4
|
+
export interface ResourceRecord {
|
|
5
|
+
id?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Operators a single field can be constrained by. */
|
|
9
|
+
export interface FieldOperators<V> {
|
|
10
|
+
$eq?: V
|
|
11
|
+
$ne?: V
|
|
12
|
+
$gt?: V
|
|
13
|
+
$gte?: V
|
|
14
|
+
$lt?: V
|
|
15
|
+
$lte?: V
|
|
16
|
+
$in?: Array<V | null>
|
|
17
|
+
$nin?: Array<V | null>
|
|
18
|
+
$exists?: boolean
|
|
19
|
+
$null?: boolean
|
|
20
|
+
$like?: string
|
|
21
|
+
$ilike?: string
|
|
22
|
+
$regex?: string
|
|
23
|
+
$startsWith?: string
|
|
24
|
+
$endsWith?: string
|
|
25
|
+
$between?: [V, V]
|
|
26
|
+
$contains?: V | V[]
|
|
27
|
+
$contained?: V[]
|
|
28
|
+
$overlaps?: V[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* How one field is constrained: a bare value is equality, a bare ARRAY is "any of these", `null`
|
|
33
|
+
* asks for the absence of a value, and `undefined` is SKIPPED — an untouched filter must never
|
|
34
|
+
* empty a list.
|
|
35
|
+
*/
|
|
36
|
+
export type FieldCriteria<V> = V | Array<V | null> | FieldOperators<NonNullable<V>> | null | undefined
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* A query over records of `T`. Keys are the record's own fields, so a typo is a compile error;
|
|
40
|
+
* a dotted key reaches into a nested value (or a jsonb column) and stays open.
|
|
41
|
+
*/
|
|
42
|
+
export type Criteria<T> =
|
|
43
|
+
& { [K in keyof T & string]?: FieldCriteria<T[K]> }
|
|
44
|
+
& { [path: `${string}.${string}`]: FieldCriteria<unknown> }
|
|
45
|
+
& { $and?: Criteria<T>[], $or?: Criteria<T>[], $not?: Criteria<T> }
|
|
46
|
+
|
|
47
|
+
export type SortField<T> = (keyof T & string) | `${string}.${string}`
|
|
48
|
+
|
|
49
|
+
/** A bare field name sorts ascending. */
|
|
50
|
+
export type Sort<T> = SortField<T> | { field: SortField<T>, order?: 'asc' | 'desc' }
|
|
51
|
+
|
|
52
|
+
export interface FirstOptions<T> {
|
|
53
|
+
sort?: Sort<T>[]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Paging is opt-in per call. A backend that cannot afford an unbounded read applies its own
|
|
58
|
+
* default size when none is asked for; the in-memory ones return everything.
|
|
59
|
+
*/
|
|
60
|
+
export interface ListOptions<T> extends FirstOptions<T> {
|
|
61
|
+
page?: number
|
|
62
|
+
size?: number
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The same question in one object — the shape an API carries over the wire. */
|
|
66
|
+
export interface ListQuery<T> extends ListOptions<T> {
|
|
67
|
+
where?: Criteria<T>
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface ListResult<T extends ResourceRecord> {
|
|
71
|
+
items: T[]
|
|
72
|
+
total: number
|
|
73
|
+
page?: number
|
|
74
|
+
size?: number
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type Ttl = number | Date
|
|
78
|
+
|
|
79
|
+
export interface WriteOptions {
|
|
80
|
+
/** Seconds from now, or the instant to expire at. Backends without expiry refuse it. */
|
|
81
|
+
ttl?: Ttl
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Typed CRUD over records. Reads take either an id or a criteria object, so fetching one record by
|
|
86
|
+
* several fields is a single call rather than a list whose first element is taken.
|
|
87
|
+
*/
|
|
4
88
|
export interface Resource<T extends ResourceRecord> extends BasicResource {
|
|
5
89
|
/**
|
|
6
90
|
* @throws {UnknownRecordError}
|
|
7
91
|
*/
|
|
8
|
-
get
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
92
|
+
get(id: string): Promise<T>
|
|
93
|
+
get(where: Criteria<T>, opts?: FirstOptions<T>): Promise<T>
|
|
94
|
+
load(id: string): Promise<T | null>
|
|
95
|
+
load(where: Criteria<T>, opts?: FirstOptions<T>): Promise<T | null>
|
|
96
|
+
list(where?: Criteria<T>, opts?: ListOptions<T>): Promise<ListResult<T>>
|
|
97
|
+
count(where?: Criteria<T>): Promise<number>
|
|
12
98
|
/**
|
|
13
99
|
* @throws {RecordExists}
|
|
14
100
|
*/
|
|
15
|
-
create
|
|
101
|
+
create(record: Partial<T>, opts?: WriteOptions): Promise<T>
|
|
16
102
|
/**
|
|
103
|
+
* Replaces the whole record.
|
|
17
104
|
* @throws {UnknownRecordError}
|
|
18
105
|
*/
|
|
19
|
-
update
|
|
20
|
-
|
|
106
|
+
update(record: Partial<T>, opts?: WriteOptions): Promise<T>
|
|
107
|
+
/** Creates when the record carries no id, replaces otherwise. */
|
|
108
|
+
save(record: Partial<T>, opts?: WriteOptions): Promise<T>
|
|
109
|
+
delete(id: string): Promise<T | null>
|
|
21
110
|
/**
|
|
111
|
+
* Delete-and-return — the consume-once read. Absence is an error here; use `delete` when it is not.
|
|
22
112
|
* @throws {UnknownRecordError}
|
|
23
113
|
*/
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
export interface ResourceRecord {
|
|
28
|
-
id?: string
|
|
114
|
+
take(id: string): Promise<T>
|
|
115
|
+
/** Bulk delete. Refuses an empty criteria object rather than emptying the resource. */
|
|
116
|
+
purge(where: Criteria<T>): Promise<number>
|
|
29
117
|
}
|
|
30
118
|
|
|
31
|
-
type
|
|
32
|
-
export interface ListCriteria extends Record<string, MaybeArray<ListCriteria | number | string | undefined>> { }
|
|
119
|
+
export type Unsubscribe = () => Promise<void>
|
|
33
120
|
|
|
34
|
-
export interface
|
|
35
|
-
|
|
36
|
-
|
|
121
|
+
export interface SubscribeOptions {
|
|
122
|
+
channel?: string
|
|
123
|
+
once?: boolean
|
|
124
|
+
ttl?: Ttl
|
|
37
125
|
}
|
|
38
126
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
page?: number
|
|
44
|
-
size?: number
|
|
45
|
-
total?: number
|
|
127
|
+
/** Optional capability: a backend that can carry messages beside its records. */
|
|
128
|
+
export interface PubSubResource<T> {
|
|
129
|
+
publish(value: T, channel?: string): Promise<void>
|
|
130
|
+
subscribe(handler: (value: T) => void | Promise<void>, opts?: SubscribeOptions): Promise<Unsubscribe>
|
|
46
131
|
}
|
|
47
132
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
133
|
+
/** Optional capability: watching ONE record for changes (redis keyspace notifications). */
|
|
134
|
+
export interface WatchableResource<T> {
|
|
135
|
+
watch(id: string, handler: (value: T | null) => void | Promise<void>, opts?: Omit<SubscribeOptions, 'channel'>): Promise<Unsubscribe>
|
|
51
136
|
}
|
|
52
137
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
138
|
+
/** Optional capability: an append-only log with consumer groups. */
|
|
139
|
+
export interface StreamResource<T> {
|
|
140
|
+
stream(key: string, value: T): Promise<void>
|
|
141
|
+
consume(key: string, opts?: { group?: string, consumer?: string, block?: number }): AsyncGenerator<T>
|
|
57
142
|
}
|
|
58
143
|
|
|
59
|
-
|
|
60
|
-
|
|
144
|
+
/** Optional capability: field level encryption at rest. */
|
|
145
|
+
export interface LockableResource<T extends ResourceRecord> {
|
|
146
|
+
lock: (record: Partial<T>, fields?: string[]) => Promise<T>
|
|
147
|
+
unlock: (record: Partial<T>, fields?: string[]) => Promise<T>
|
|
61
148
|
}
|
|
62
149
|
|
|
63
150
|
export interface ResourceMaker<R extends ResourceRecord, T extends Resource<R> = Resource<R>> {
|
|
@@ -102,11 +189,6 @@ export interface DbLocker<T extends ResourceRecord> {
|
|
|
102
189
|
unlock: (alias: string, record: Partial<T>, fields: string[]) => Promise<T>
|
|
103
190
|
}
|
|
104
191
|
|
|
105
|
-
export interface ResourceLocker<T extends ResourceRecord> {
|
|
106
|
-
lock: (record: Partial<T>, fields?: string[]) => Promise<T>
|
|
107
|
-
unlock: (record: Partial<T>, fields?: string[]) => Promise<T>
|
|
108
|
-
}
|
|
109
|
-
|
|
110
192
|
export interface DbConfig<P extends {} = {}> {
|
|
111
193
|
service: string
|
|
112
194
|
alias?: string
|
|
@@ -116,8 +198,6 @@ export interface DbConfig<P extends {} = {}> {
|
|
|
116
198
|
secret?: string
|
|
117
199
|
schema?: string
|
|
118
200
|
resourcePrefix?: string
|
|
119
|
-
entitySensitive?: boolean
|
|
120
|
-
serviceSensitive?: boolean
|
|
121
201
|
encryptionKey?: string
|
|
122
202
|
meta?: P
|
|
123
203
|
}
|
|
@@ -133,16 +213,15 @@ export interface DbConfig<P extends {} = {}> {
|
|
|
133
213
|
* invoked. Backends with a durable structure also implement {@link MigrationStore}, the
|
|
134
214
|
* register that tracks which migrations have been applied.
|
|
135
215
|
*/
|
|
136
|
-
export interface MigratableResource<Tx = unknown> {
|
|
216
|
+
export interface MigratableResource<Tx = unknown, Self = unknown> {
|
|
137
217
|
/**
|
|
138
218
|
* Register a migration, applied once per database in declaration order.
|
|
139
219
|
*
|
|
140
|
-
* Chainable and idempotent: re-registering the same name with the same body is a no-op,
|
|
141
|
-
*
|
|
142
|
-
*
|
|
143
|
-
* {@link MigrationConflict}.
|
|
220
|
+
* Chainable and idempotent: re-registering the same name with the same body is a no-op, which is
|
|
221
|
+
* what makes it safe to call from a resource maker that runs more than once for the same alias.
|
|
222
|
+
* Re-registering a *changed* body under a used name throws {@link MigrationConflict}.
|
|
144
223
|
*/
|
|
145
|
-
migration: (name: string, apply: (tx: Tx) => Promise<void>, stage?: MigrationStage) =>
|
|
224
|
+
migration: (name: string, apply: (tx: Tx) => Promise<void>, stage?: MigrationStage) => Self
|
|
146
225
|
/** The registered migrations for this resource's alias. Read-only; use {@link MigratableResource.migration}. */
|
|
147
226
|
migrations: () => MigrationRegistry<Tx>
|
|
148
227
|
}
|
package/src/utils/index.ts
CHANGED
package/src/utils/name.ts
CHANGED
|
@@ -1,27 +1,5 @@
|
|
|
1
|
-
import { Layer } from '@owlmeans/context'
|
|
2
1
|
import type {Context, Config, ResourceDbService, DbConfig} from '../types.js'
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
export const dbName = <C extends Config, T extends Context<C> = Context<C>>(context: T, service: ResourceDbService<any,any>, config: DbConfig) => {
|
|
8
|
-
let name = config.schema ?? config.alias ?? service.alias
|
|
9
|
-
|
|
10
|
-
if (context.cfg.layerId != null
|
|
11
|
-
&& [Layer.Entity, Layer.User].includes(context.cfg.layer)
|
|
12
|
-
&& service.layers?.includes(Layer.Entity)) {
|
|
13
|
-
name = format(name, context.cfg.layerId)
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return name
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const format = (name: string, suffix: string) => {
|
|
20
|
-
name = (name + '_' + suffix).replace(/\W/g, '_')
|
|
21
|
-
|
|
22
|
-
if (name.length > 64) {
|
|
23
|
-
name = name.substring(0, 31) + '_' + hex.encode(sha256(name.substring(31)))
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
return name
|
|
27
|
-
}
|
|
3
|
+
export const dbName = <C extends Config, T extends Context<C> = Context<C>>(
|
|
4
|
+
_context: T, service: ResourceDbService<any, any>, config: DbConfig
|
|
5
|
+
) => config.schema ?? config.alias ?? service.alias
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import { UnsupportedArgumentError } from '../errors.js'
|
|
2
|
+
import type { Criteria, ListOptions, ListResult, ResourceRecord, Sort } from '../types.js'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* In-memory evaluation of `Criteria<any>`.
|
|
6
|
+
*
|
|
7
|
+
* The operator set mirrors `@owlmeans/postgres-resource` on purpose: one criteria shape has to
|
|
8
|
+
* mean the same thing whether a list is filtered in SQL by an endpoint or locally by a screen
|
|
9
|
+
* reading the state resource. A client that learns `{ status: ['open', 'done'] }` means "either
|
|
10
|
+
* of these" must not meet a different answer when the same object reaches the server.
|
|
11
|
+
*
|
|
12
|
+
* Two deliberate differences, both because a state resource carries no schema:
|
|
13
|
+
*
|
|
14
|
+
* - An unknown key cannot be detected. Postgres throws on one (a typo silently widening a query
|
|
15
|
+
* to the whole table is worth being loud about); here the property is simply absent from the
|
|
16
|
+
* record, so the record does not match. There is nothing to compare it against.
|
|
17
|
+
* - There is no jsonb/column distinction, so a dotted key always reaches into the record.
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
const COMPARISON = new Set(['$eq', '$ne', '$gt', '$gte', '$lt', '$lte'])
|
|
21
|
+
|
|
22
|
+
/** Dates compare by instant; everything else compares as it is. */
|
|
23
|
+
const comparable = (value: unknown): unknown =>
|
|
24
|
+
value instanceof Date ? value.getTime() : value
|
|
25
|
+
|
|
26
|
+
/** Reach `profile.city` inside a record. A missing step yields `undefined`, never a throw. */
|
|
27
|
+
const reach = (record: unknown, path: string[]): unknown =>
|
|
28
|
+
path.reduce<unknown>(
|
|
29
|
+
(value, step) => value == null || typeof value !== 'object'
|
|
30
|
+
? undefined
|
|
31
|
+
: (value as Record<string, unknown>)[step],
|
|
32
|
+
record
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* An object naming at least one `$` key is a spec, not a value to compare against.
|
|
37
|
+
* A Date and an array are values even though both are objects.
|
|
38
|
+
*/
|
|
39
|
+
const isOperatorSpec = (value: unknown): value is Record<string, unknown> =>
|
|
40
|
+
value != null && typeof value === 'object' && !Array.isArray(value) && !(value instanceof Date)
|
|
41
|
+
&& Object.keys(value).some(key => key.startsWith('$'))
|
|
42
|
+
|
|
43
|
+
const equal = (left: unknown, right: unknown): boolean => {
|
|
44
|
+
if (left instanceof Date || right instanceof Date) {
|
|
45
|
+
return comparable(left) === comparable(right)
|
|
46
|
+
}
|
|
47
|
+
if (Array.isArray(left) && Array.isArray(right)) {
|
|
48
|
+
return left.length === right.length && left.every((entry, index) => equal(entry, right[index]))
|
|
49
|
+
}
|
|
50
|
+
return left === right
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** `$in` / `$nin`. `null` in the list matches an absent property, matching the SQL widening. */
|
|
54
|
+
const inList = (value: unknown, operand: unknown): boolean => {
|
|
55
|
+
const values = Array.isArray(operand) ? operand : [operand]
|
|
56
|
+
return values.some(entry => entry == null ? value == null : equal(value, entry))
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const text = (value: unknown): string => value == null ? '' : `${value}`
|
|
60
|
+
|
|
61
|
+
/** `%` and `_` are the SQL wildcards; `\` escapes them. */
|
|
62
|
+
const likeToRegExp = (pattern: string, flags: string): RegExp => {
|
|
63
|
+
let source = ''
|
|
64
|
+
for (let index = 0; index < pattern.length; index++) {
|
|
65
|
+
const char = pattern[index]!
|
|
66
|
+
if (char === '\\' && index + 1 < pattern.length) {
|
|
67
|
+
source += pattern[++index]!.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
68
|
+
continue
|
|
69
|
+
}
|
|
70
|
+
if (char === '%') { source += '.*'; continue }
|
|
71
|
+
if (char === '_') { source += '.'; continue }
|
|
72
|
+
source += char.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return new RegExp(`^${source}$`, flags)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @throws {UnsupportedArgumentError} on an operator this store cannot answer.
|
|
80
|
+
*/
|
|
81
|
+
const applyOperators = (value: unknown, spec: Record<string, unknown>): boolean => {
|
|
82
|
+
for (const [operator, operand] of Object.entries(spec)) {
|
|
83
|
+
if (COMPARISON.has(operator)) {
|
|
84
|
+
const left = comparable(value)
|
|
85
|
+
const right = comparable(operand)
|
|
86
|
+
const matched =
|
|
87
|
+
operator === '$eq' ? equal(value, operand)
|
|
88
|
+
: operator === '$ne' ? !equal(value, operand)
|
|
89
|
+
: left == null || right == null ? false
|
|
90
|
+
: operator === '$gt' ? (left as never) > (right as never)
|
|
91
|
+
: operator === '$gte' ? (left as never) >= (right as never)
|
|
92
|
+
: operator === '$lt' ? (left as never) < (right as never)
|
|
93
|
+
: (left as never) <= (right as never)
|
|
94
|
+
if (!matched) return false
|
|
95
|
+
continue
|
|
96
|
+
}
|
|
97
|
+
switch (operator) {
|
|
98
|
+
case '$in':
|
|
99
|
+
if (!inList(value, operand)) return false
|
|
100
|
+
break
|
|
101
|
+
case '$nin':
|
|
102
|
+
if (inList(value, operand)) return false
|
|
103
|
+
break
|
|
104
|
+
case '$exists':
|
|
105
|
+
if ((value != null) !== (operand !== false)) return false
|
|
106
|
+
break
|
|
107
|
+
case '$null':
|
|
108
|
+
if ((value == null) !== (operand !== false)) return false
|
|
109
|
+
break
|
|
110
|
+
case '$like':
|
|
111
|
+
if (!likeToRegExp(text(operand), '').test(text(value))) return false
|
|
112
|
+
break
|
|
113
|
+
case '$ilike':
|
|
114
|
+
if (!likeToRegExp(text(operand), 'i').test(text(value))) return false
|
|
115
|
+
break
|
|
116
|
+
case '$regex':
|
|
117
|
+
if (!new RegExp(text(operand)).test(text(value))) return false
|
|
118
|
+
break
|
|
119
|
+
case '$startsWith':
|
|
120
|
+
if (!text(value).startsWith(text(operand))) return false
|
|
121
|
+
break
|
|
122
|
+
case '$endsWith':
|
|
123
|
+
if (!text(value).endsWith(text(operand))) return false
|
|
124
|
+
break
|
|
125
|
+
case '$between': {
|
|
126
|
+
if (!Array.isArray(operand) || operand.length !== 2) {
|
|
127
|
+
throw new UnsupportedArgumentError(`criteria:$between`)
|
|
128
|
+
}
|
|
129
|
+
const left = comparable(value)
|
|
130
|
+
if (left == null) return false
|
|
131
|
+
const from = comparable(operand[0])
|
|
132
|
+
const to = comparable(operand[1])
|
|
133
|
+
if ((left as never) < (from as never) || (left as never) > (to as never)) return false
|
|
134
|
+
break
|
|
135
|
+
}
|
|
136
|
+
case '$contains': {
|
|
137
|
+
const operands = Array.isArray(operand) ? operand : [operand]
|
|
138
|
+
const values = Array.isArray(value) ? value : [value]
|
|
139
|
+
if (!operands.every(entry => values.some(candidate => equal(candidate, entry)))) return false
|
|
140
|
+
break
|
|
141
|
+
}
|
|
142
|
+
case '$contained': {
|
|
143
|
+
const operands = Array.isArray(operand) ? operand : [operand]
|
|
144
|
+
const values = Array.isArray(value) ? value : [value]
|
|
145
|
+
if (!values.every(candidate => operands.some(entry => equal(candidate, entry)))) return false
|
|
146
|
+
break
|
|
147
|
+
}
|
|
148
|
+
case '$overlaps': {
|
|
149
|
+
const operands = Array.isArray(operand) ? operand : [operand]
|
|
150
|
+
const values = Array.isArray(value) ? value : [value]
|
|
151
|
+
if (!values.some(candidate => operands.some(entry => equal(candidate, entry)))) return false
|
|
152
|
+
break
|
|
153
|
+
}
|
|
154
|
+
default:
|
|
155
|
+
throw new UnsupportedArgumentError(`criteria-operator:${operator}`)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return true
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Whether one record satisfies the criteria.
|
|
164
|
+
*
|
|
165
|
+
* `undefined` as a criteria value is skipped rather than compared — it is what an optional filter
|
|
166
|
+
* looks like when nothing was chosen, and treating it as "must be undefined" makes an unset
|
|
167
|
+
* dropdown filter the list down to nothing.
|
|
168
|
+
*
|
|
169
|
+
* @throws {UnsupportedArgumentError} on an unknown operator.
|
|
170
|
+
*/
|
|
171
|
+
export const matchCriteria = <T extends ResourceRecord>(
|
|
172
|
+
record: T, criteria?: Criteria<any>
|
|
173
|
+
): boolean => {
|
|
174
|
+
for (const [key, raw] of Object.entries(criteria ?? {})) {
|
|
175
|
+
if (raw === undefined) {
|
|
176
|
+
continue
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
if (key === '$and' || key === '$or') {
|
|
180
|
+
const parts = (Array.isArray(raw) ? raw : [raw]) as Criteria<any>[]
|
|
181
|
+
if (parts.length < 1) continue
|
|
182
|
+
const matched = key === '$and'
|
|
183
|
+
? parts.every(part => matchCriteria(record, part))
|
|
184
|
+
: parts.some(part => matchCriteria(record, part))
|
|
185
|
+
if (!matched) return false
|
|
186
|
+
continue
|
|
187
|
+
}
|
|
188
|
+
if (key === '$not') {
|
|
189
|
+
if (matchCriteria(record, raw as Criteria<any>)) return false
|
|
190
|
+
continue
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const value = reach(record, key.split('.'))
|
|
194
|
+
|
|
195
|
+
if (raw === null) {
|
|
196
|
+
if (value != null) return false
|
|
197
|
+
continue
|
|
198
|
+
}
|
|
199
|
+
if (isOperatorSpec(raw)) {
|
|
200
|
+
if (!applyOperators(value, raw as Record<string, unknown>)) return false
|
|
201
|
+
continue
|
|
202
|
+
}
|
|
203
|
+
if (Array.isArray(raw)) {
|
|
204
|
+
/**
|
|
205
|
+
* A bare array means "any of these", exactly as it does against a relational store.
|
|
206
|
+
* Exact array equality stays reachable as `{ $eq: [...] }`.
|
|
207
|
+
*/
|
|
208
|
+
if (!inList(value, raw)) return false
|
|
209
|
+
continue
|
|
210
|
+
}
|
|
211
|
+
if (!equal(value, raw)) return false
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return true
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** Every record the criteria accepts, in insertion order. */
|
|
218
|
+
export const filterRecords = <T extends ResourceRecord>(
|
|
219
|
+
records: T[], criteria?: Criteria<any>
|
|
220
|
+
): T[] => criteria == null || Object.keys(criteria).length < 1
|
|
221
|
+
? [...records]
|
|
222
|
+
: records.filter(record => matchCriteria(record, criteria))
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Sort a copy. A bare field name is ascending; `{ field, order: 'desc' }` reverses it — the same
|
|
226
|
+
* meaning every backend gives it, so a sort written for one store orders the same way against
|
|
227
|
+
* another.
|
|
228
|
+
*/
|
|
229
|
+
export const sortRecords = <T extends ResourceRecord>(records: T[], sort?: Sort<any>[]): T[] => {
|
|
230
|
+
if (sort == null || sort.length < 1) {
|
|
231
|
+
return [...records]
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const rules = sort.map(entry => typeof entry === 'string'
|
|
235
|
+
? { field: entry, desc: false }
|
|
236
|
+
: { field: entry.field, desc: entry.order === 'desc' })
|
|
237
|
+
|
|
238
|
+
return [...records].sort((left, right) => {
|
|
239
|
+
for (const { field, desc } of rules) {
|
|
240
|
+
const a = comparable(reach(left, field.split('.')))
|
|
241
|
+
const b = comparable(reach(right, field.split('.')))
|
|
242
|
+
if (a == null && b == null) continue
|
|
243
|
+
/** An absent value sorts last ascending, which is where a reader expects "no value". */
|
|
244
|
+
if (a == null) return 1
|
|
245
|
+
if (b == null) return -1
|
|
246
|
+
if ((a as never) < (b as never)) return desc ? 1 : -1
|
|
247
|
+
if ((a as never) > (b as never)) return desc ? -1 : 1
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return 0
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/** The first record the criteria accepts, honouring the sort. */
|
|
255
|
+
export const firstMatch = <T extends ResourceRecord>(
|
|
256
|
+
records: T[], where?: Criteria<any>, opts?: { sort?: Sort<any>[] }
|
|
257
|
+
): T | null => sortRecords(filterRecords(records, where), opts?.sort)[0] ?? null
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Filter, sort and page an in-memory record set into the shape every resource answers with.
|
|
261
|
+
* `size` is opt-in: an in-memory store returns everything when none is asked for.
|
|
262
|
+
*/
|
|
263
|
+
export const applyQuery = <T extends ResourceRecord>(
|
|
264
|
+
records: T[], where?: Criteria<any>, opts?: ListOptions<any>
|
|
265
|
+
): ListResult<T> => {
|
|
266
|
+
const matched = sortRecords(filterRecords(records, where), opts?.sort)
|
|
267
|
+
const total = matched.length
|
|
268
|
+
// `size: 0` is the explicit ask for everything — the same meaning the paged backends give it, so
|
|
269
|
+
// one query object means one thing whichever store answers it. An omitted size is unpaged here.
|
|
270
|
+
if (opts?.size == null || opts.size === 0) {
|
|
271
|
+
return { items: matched, total }
|
|
272
|
+
}
|
|
273
|
+
const page = opts.page ?? 0
|
|
274
|
+
const from = page * opts.size
|
|
275
|
+
|
|
276
|
+
return { items: matched.slice(from, from + opts.size), total, page, size: opts.size }
|
|
277
|
+
}
|
package/build/.gitkeep
DELETED
|
File without changes
|