@open-predicate/open-predicate 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/CHANGELOG.md +532 -0
- package/LICENSE +21 -0
- package/README.md +483 -0
- package/SPEC.md +534 -0
- package/open-predicate-schema.json +395 -0
- package/package.json +67 -0
- package/tools/generate-filter-schema.mjs +1153 -0
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://openpredicate.tech/schema/v0.4.0/open-predicate-schema.json",
|
|
4
|
+
"title": "OpenPredicate — Filter",
|
|
5
|
+
"description": "A JSON-encoded, SQL-flavoured predicate language for describing the request body of search operations (POST /…/search and QUERY /…). Everything needed to write a correct filter is described here, operator by operator, so one grammar can be shared by every search endpoint in an API: reference this schema from an OpenAPI document, or inline it as the input schema of a search tool. Normative semantics are defined in SPEC.md.",
|
|
6
|
+
"$comment": "Version 0.4.0. See https://github.com/OpenPredicate/open-predicate",
|
|
7
|
+
|
|
8
|
+
"$ref": "#/$defs/Filter",
|
|
9
|
+
|
|
10
|
+
"x-profiles": {
|
|
11
|
+
"core": ["$and", "$or", "$nor", "$not", "$eq", "$ne", "$in", "$nin", "$gt", "$gte", "$lt", "$lte", "$exists", "$isNull", "$unknownAs"],
|
|
12
|
+
"strings": ["$like", "$nlike", "$ilike", "$nilike", "$startsWith", "$endsWith", "$contains"],
|
|
13
|
+
"regex": ["$regex", "$flags"],
|
|
14
|
+
"ranges": ["$between", "$nbetween"],
|
|
15
|
+
"types": ["$type"],
|
|
16
|
+
"collections": ["$some", "$every", "$hasAll", "$size"],
|
|
17
|
+
"refs": ["$field", "$literal"],
|
|
18
|
+
"text": ["$search"]
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
"$defs": {
|
|
22
|
+
"Filter": {
|
|
23
|
+
"title": "Filter",
|
|
24
|
+
"description": "A predicate over a record. Sibling members are combined with implicit AND: every member must evaluate TRUE for the filter to match. Keys beginning with '$' are reserved for logical operators; a field whose real name begins with '$' is escaped by doubling it ('$$price' means the field '$price').",
|
|
25
|
+
"type": "object",
|
|
26
|
+
"minProperties": 1,
|
|
27
|
+
"properties": {
|
|
28
|
+
"$and": {
|
|
29
|
+
"title": "$and",
|
|
30
|
+
"description": "All of the listed filters must evaluate TRUE.",
|
|
31
|
+
"type": "array",
|
|
32
|
+
"minItems": 1,
|
|
33
|
+
"items": { "$ref": "#/$defs/Filter" }
|
|
34
|
+
},
|
|
35
|
+
"$or": {
|
|
36
|
+
"title": "$or",
|
|
37
|
+
"description": "At least one of the listed filters must evaluate TRUE.",
|
|
38
|
+
"type": "array",
|
|
39
|
+
"minItems": 1,
|
|
40
|
+
"items": { "$ref": "#/$defs/Filter" }
|
|
41
|
+
},
|
|
42
|
+
"$nor": {
|
|
43
|
+
"title": "$nor",
|
|
44
|
+
"description": "NOT (a OR b OR …), evaluated under three-valued logic. It is TRUE only when every listed filter is FALSE: if one of them is UNKNOWN the result is UNKNOWN, not TRUE, so a $nor over a nullable field excludes the records whose field is null or absent. See SPEC.md §4.1.",
|
|
45
|
+
"type": "array",
|
|
46
|
+
"minItems": 1,
|
|
47
|
+
"items": { "$ref": "#/$defs/Filter" }
|
|
48
|
+
},
|
|
49
|
+
"$not": {
|
|
50
|
+
"title": "$not",
|
|
51
|
+
"description": "Negates a filter. Under three-valued logic NOT UNKNOWN is UNKNOWN, not TRUE — so this does not match records whose field is null or absent. Add $unknownAs to the inner constraint to include them. See SPEC.md §4.1.",
|
|
52
|
+
"$ref": "#/$defs/Filter"
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
"patternProperties": {
|
|
56
|
+
"^(?:[^$]|\\$\\$)": { "$ref": "#/$defs/Constraint" }
|
|
57
|
+
},
|
|
58
|
+
"propertyNames": {
|
|
59
|
+
"$comment": "Deliberately overlaps with patternProperties/additionalProperties above: those two enforce the default grammar, while this is the single named override point. Narrow #/$defs/FieldPath in a bundled copy of this schema to restrict an endpoint to a fixed field set — the narrowing then applies at every nesting level, because Filter recurses through this same $ref. See README §Restricting the queryable field set.",
|
|
60
|
+
"anyOf": [
|
|
61
|
+
{ "enum": ["$and", "$or", "$nor", "$not"] },
|
|
62
|
+
{ "$ref": "#/$defs/FieldPath" }
|
|
63
|
+
]
|
|
64
|
+
},
|
|
65
|
+
"additionalProperties": false,
|
|
66
|
+
"examples": [
|
|
67
|
+
{ "status": "open" },
|
|
68
|
+
{ "department": { "$eq": "sales" }, "age": { "$gte": 18 } },
|
|
69
|
+
{
|
|
70
|
+
"$and": [
|
|
71
|
+
{ "department": { "$eq": "sales" } },
|
|
72
|
+
{ "$or": [{ "role": "admin" }, { "role": "manager" }] }
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
|
|
78
|
+
"FieldPath": {
|
|
79
|
+
"title": "Field path",
|
|
80
|
+
"description": "A dotted path to a member of the record: 'a.b.c'. An array element is addressed by index ('items[0]'); to say something about the elements of an array without naming an index, use $some or $every, which quantify explicitly. A literal '.' inside a key is escaped as '\\.'. The default rule is permissive — any non-empty string not beginning with a single '$' — because the set of queryable paths is a property of the resource, not of the language. Full grammar in SPEC.md §3.",
|
|
81
|
+
"type": "string",
|
|
82
|
+
"minLength": 1,
|
|
83
|
+
"pattern": "^(?:[^$]|\\$\\$)",
|
|
84
|
+
"not": {
|
|
85
|
+
"$comment": "The '[*]' wildcard was removed in v0.4.0; $some and $every replace it. Rejecting it here turns a stale filter into a validation error instead of a path that would be read as a literal key name.",
|
|
86
|
+
"pattern": "\\[\\*\\]"
|
|
87
|
+
},
|
|
88
|
+
"examples": ["name", "address.city", "items[0].sku", "$$internal"]
|
|
89
|
+
},
|
|
90
|
+
|
|
91
|
+
"Constraint": {
|
|
92
|
+
"title": "Constraint",
|
|
93
|
+
"description": "The condition applied to one field. Either a scalar shorthand — {\"status\": \"open\"} is exactly {\"status\": {\"$eq\": \"open\"}} — or an object of operators. Arrays and objects are deliberately excluded from the shorthand so that {\"tags\": [\"a\"]} can never be read ambiguously as either $eq or $in.",
|
|
94
|
+
"anyOf": [
|
|
95
|
+
{ "$ref": "#/$defs/ScalarOrNull" },
|
|
96
|
+
{ "$ref": "#/$defs/ConstraintObject" }
|
|
97
|
+
]
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
"ConstraintObject": {
|
|
101
|
+
"title": "Constraint object",
|
|
102
|
+
"description": "One or more operators applied to the same field. Sibling operators are combined with implicit AND, so {\"age\": {\"$gt\": 18, \"$ne\": 30}} is a single well-formed constraint.",
|
|
103
|
+
"type": "object",
|
|
104
|
+
"minProperties": 1,
|
|
105
|
+
"dependentSchemas": {
|
|
106
|
+
"$unknownAs": {
|
|
107
|
+
"$comment": "$unknownAs is a modifier, not a predicate: it needs at least one operator to modify. Requiring a second member forbids {\"$unknownAs\": true} standing alone.",
|
|
108
|
+
"minProperties": 2
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
"properties": {
|
|
112
|
+
"$eq": {
|
|
113
|
+
"title": "$eq",
|
|
114
|
+
"description": "Field equals the operand. Any JSON value, including null, arrays and objects. A value of a different JSON type is unequal — FALSE, not UNKNOWN.",
|
|
115
|
+
"$ref": "#/$defs/Operand"
|
|
116
|
+
},
|
|
117
|
+
"$ne": {
|
|
118
|
+
"title": "$ne",
|
|
119
|
+
"description": "Field does not equal the operand. The negation of $eq under three-valued logic, so this is UNKNOWN — and therefore does not match — when the field is null or absent. Add $unknownAs: true to include those records. See SPEC.md §4.1.",
|
|
120
|
+
"$ref": "#/$defs/Operand"
|
|
121
|
+
},
|
|
122
|
+
|
|
123
|
+
"$gt": {
|
|
124
|
+
"title": "$gt",
|
|
125
|
+
"description": "Field is strictly greater than the operand.",
|
|
126
|
+
"$ref": "#/$defs/OrderedOperand"
|
|
127
|
+
},
|
|
128
|
+
"$gte": {
|
|
129
|
+
"title": "$gte",
|
|
130
|
+
"description": "Field is greater than or equal to the operand.",
|
|
131
|
+
"$ref": "#/$defs/OrderedOperand"
|
|
132
|
+
},
|
|
133
|
+
"$lt": {
|
|
134
|
+
"title": "$lt",
|
|
135
|
+
"description": "Field is strictly less than the operand.",
|
|
136
|
+
"$ref": "#/$defs/OrderedOperand"
|
|
137
|
+
},
|
|
138
|
+
"$lte": {
|
|
139
|
+
"title": "$lte",
|
|
140
|
+
"description": "Field is less than or equal to the operand.",
|
|
141
|
+
"$ref": "#/$defs/OrderedOperand"
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
"$between": {
|
|
145
|
+
"title": "$between",
|
|
146
|
+
"description": "Field falls within [lower, upper], inclusive of both bounds.",
|
|
147
|
+
"$ref": "#/$defs/Bounds"
|
|
148
|
+
},
|
|
149
|
+
"$nbetween": {
|
|
150
|
+
"title": "$nbetween",
|
|
151
|
+
"description": "Field falls outside [lower, upper]. The negation of $between under three-valued logic, so it is UNKNOWN when the field is null or absent.",
|
|
152
|
+
"$ref": "#/$defs/Bounds"
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
"$in": {
|
|
156
|
+
"title": "$in",
|
|
157
|
+
"description": "Field equals at least one member of the list. Compares the whole value — it does NOT test membership inside an array-valued field: {\"tags\": {\"$in\": [\"a\"]}} asks whether tags equals \"a\". For array elements quantify explicitly: {\"tags\": {\"$some\": {\"$in\": [\"a\"]}}}.",
|
|
158
|
+
"$ref": "#/$defs/OperandSet"
|
|
159
|
+
},
|
|
160
|
+
"$nin": {
|
|
161
|
+
"title": "$nin",
|
|
162
|
+
"description": "Field equals no member of the list. Like $in, compares the whole value rather than array elements. The negation of $in under three-valued logic, so it is UNKNOWN when the field is null or absent.",
|
|
163
|
+
"$ref": "#/$defs/OperandSet"
|
|
164
|
+
},
|
|
165
|
+
|
|
166
|
+
"$like": {
|
|
167
|
+
"title": "$like",
|
|
168
|
+
"description": "SQL LIKE pattern: '%' matches any run of characters, '_' matches exactly one, and '\\' escapes either. Case-sensitive.",
|
|
169
|
+
"type": "string",
|
|
170
|
+
"examples": ["%urgent%", "INV-____", "100\\%"]
|
|
171
|
+
},
|
|
172
|
+
"$nlike": {
|
|
173
|
+
"title": "$nlike",
|
|
174
|
+
"description": "Negated $like, under three-valued logic: UNKNOWN when the field is null, absent or not a string.",
|
|
175
|
+
"type": "string"
|
|
176
|
+
},
|
|
177
|
+
"$ilike": {
|
|
178
|
+
"title": "$ilike",
|
|
179
|
+
"description": "Case-insensitive $like. Collation is server-defined; see SPEC.md §5.5.",
|
|
180
|
+
"type": "string"
|
|
181
|
+
},
|
|
182
|
+
"$nilike": {
|
|
183
|
+
"title": "$nilike",
|
|
184
|
+
"description": "Negated $ilike, under three-valued logic: UNKNOWN when the field is null, absent or not a string.",
|
|
185
|
+
"type": "string"
|
|
186
|
+
},
|
|
187
|
+
"$startsWith": {
|
|
188
|
+
"title": "$startsWith",
|
|
189
|
+
"description": "Field begins with this literal substring. Wildcards are not interpreted.",
|
|
190
|
+
"type": "string"
|
|
191
|
+
},
|
|
192
|
+
"$endsWith": {
|
|
193
|
+
"title": "$endsWith",
|
|
194
|
+
"description": "Field ends with this literal substring. Wildcards are not interpreted.",
|
|
195
|
+
"type": "string"
|
|
196
|
+
},
|
|
197
|
+
"$contains": {
|
|
198
|
+
"title": "$contains",
|
|
199
|
+
"description": "Field contains this literal substring. String-only — for arrays, quantify over the elements with $some or $every.",
|
|
200
|
+
"type": "string"
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
"$regex": {
|
|
204
|
+
"title": "$regex",
|
|
205
|
+
"description": "Field matches this regular expression (ECMA-262 syntax). Servers SHOULD bound execution time; see SPEC.md §7.",
|
|
206
|
+
"type": "string",
|
|
207
|
+
"format": "regex"
|
|
208
|
+
},
|
|
209
|
+
"$flags": {
|
|
210
|
+
"title": "$flags",
|
|
211
|
+
"description": "Modifiers for $regex: 'i' case-insensitive, 'm' multiline, 's' dot-matches-newline. Only valid alongside $regex.",
|
|
212
|
+
"type": "string",
|
|
213
|
+
"pattern": "^[ims]{0,3}$"
|
|
214
|
+
},
|
|
215
|
+
|
|
216
|
+
"$exists": {
|
|
217
|
+
"title": "$exists",
|
|
218
|
+
"description": "true if the field is present on the record, even when its value is null; false if absent. Total — never UNKNOWN. Distinct from $isNull — see SPEC.md §4.2.",
|
|
219
|
+
"type": "boolean"
|
|
220
|
+
},
|
|
221
|
+
"$isNull": {
|
|
222
|
+
"title": "$isNull",
|
|
223
|
+
"description": "true if the field's value is null (equivalent to {\"$eq\": null}); false if it is non-null (equivalent to {\"$ne\": null}). UNKNOWN only when the path resolves to nothing.",
|
|
224
|
+
"type": "boolean"
|
|
225
|
+
},
|
|
226
|
+
"$type": {
|
|
227
|
+
"title": "$type",
|
|
228
|
+
"description": "Field's JSON type. 'integer' matches a number with no fractional part.",
|
|
229
|
+
"type": "string",
|
|
230
|
+
"enum": ["string", "number", "integer", "boolean", "object", "array", "null"]
|
|
231
|
+
},
|
|
232
|
+
|
|
233
|
+
"$some": {
|
|
234
|
+
"title": "$some",
|
|
235
|
+
"description": "At least one element of the array-valued field satisfies this condition. Supply a Filter for arrays of objects (paths are relative to the element) or a constraint object for arrays of scalars. An element for which the condition is UNKNOWN does not satisfy it, so the quantifier yields TRUE or FALSE over any array — FALSE for an empty one. It is UNKNOWN only when the field is not an array, including when it is null or absent. See SPEC.md §5.8.",
|
|
236
|
+
"anyOf": [
|
|
237
|
+
{ "$ref": "#/$defs/Filter" },
|
|
238
|
+
{ "$ref": "#/$defs/ConstraintObject" }
|
|
239
|
+
],
|
|
240
|
+
"examples": [{ "qty": { "$gt": 2 } }, { "$in": ["urgent", "p1"] }]
|
|
241
|
+
},
|
|
242
|
+
"$every": {
|
|
243
|
+
"title": "$every",
|
|
244
|
+
"description": "Every element of the array-valued field satisfies this condition; an element for which it is UNKNOWN does not. TRUE for an empty array, vacuously. UNKNOWN only when the field is not an array, including when it is null or absent — a missing array is not vacuously TRUE. Same operand shape as $some. See SPEC.md §5.8.",
|
|
245
|
+
"anyOf": [
|
|
246
|
+
{ "$ref": "#/$defs/Filter" },
|
|
247
|
+
{ "$ref": "#/$defs/ConstraintObject" }
|
|
248
|
+
],
|
|
249
|
+
"examples": [{ "status": "settled" }, { "$gte": 0 }]
|
|
250
|
+
},
|
|
251
|
+
"$hasAll": {
|
|
252
|
+
"title": "$hasAll",
|
|
253
|
+
"description": "The array-valued field contains every member of this list. Quantifies over the operand rather than over the elements, which is why it is not expressible as a $some or $every. Set semantics: multiplicity is ignored. UNKNOWN when the field is not an array.",
|
|
254
|
+
"$ref": "#/$defs/OperandSet"
|
|
255
|
+
},
|
|
256
|
+
"$size": {
|
|
257
|
+
"title": "$size",
|
|
258
|
+
"description": "Length of the array-valued field: either an exact count or a nested numeric constraint.",
|
|
259
|
+
"$ref": "#/$defs/SizeConstraint"
|
|
260
|
+
},
|
|
261
|
+
|
|
262
|
+
"$search": {
|
|
263
|
+
"title": "$search",
|
|
264
|
+
"description": "Free-text match against the field. Tokenisation, stemming and relevance are server-defined.",
|
|
265
|
+
"type": "string",
|
|
266
|
+
"minLength": 1
|
|
267
|
+
},
|
|
268
|
+
|
|
269
|
+
"$not": {
|
|
270
|
+
"title": "$not (field-level)",
|
|
271
|
+
"description": "Negates the constraint on this field. {\"age\": {\"$not\": {\"$gt\": 5}}} is TRUE when age is not greater than 5 and UNKNOWN when age is null or absent.",
|
|
272
|
+
"$ref": "#/$defs/ConstraintObject"
|
|
273
|
+
},
|
|
274
|
+
"$unknownAs": {
|
|
275
|
+
"title": "$unknownAs",
|
|
276
|
+
"description": "Resolves UNKNOWN to TRUE or FALSE for this constraint, applied last — after every sibling operator, including a field-level $not. {\"status\": {\"$ne\": \"archived\", \"$unknownAs\": true}} means 'not archived, and count the records where status is null or absent'. A modifier, not a predicate: it needs at least one operator beside it. No-op on $exists, which is already total. See SPEC.md §4.6.",
|
|
277
|
+
"type": "boolean"
|
|
278
|
+
}
|
|
279
|
+
},
|
|
280
|
+
"dependentRequired": {
|
|
281
|
+
"$flags": ["$regex"]
|
|
282
|
+
},
|
|
283
|
+
"additionalProperties": false,
|
|
284
|
+
"examples": [
|
|
285
|
+
{ "$eq": "open" },
|
|
286
|
+
{ "$gt": 18, "$ne": 30 },
|
|
287
|
+
{ "$in": ["red", "green"] },
|
|
288
|
+
{ "$regex": "^inv-", "$flags": "i" },
|
|
289
|
+
{ "$ne": "archived", "$unknownAs": true },
|
|
290
|
+
{ "$some": { "$in": ["urgent", "p1"] } }
|
|
291
|
+
]
|
|
292
|
+
},
|
|
293
|
+
|
|
294
|
+
"ScalarOrNull": {
|
|
295
|
+
"title": "Scalar value",
|
|
296
|
+
"description": "A JSON scalar or null.",
|
|
297
|
+
"type": ["string", "number", "boolean", "null"]
|
|
298
|
+
},
|
|
299
|
+
|
|
300
|
+
"Operand": {
|
|
301
|
+
"title": "Operand",
|
|
302
|
+
"description": "A value to compare against: any JSON value, a reference to another field of the same record, or a $literal-wrapped value.",
|
|
303
|
+
"anyOf": [
|
|
304
|
+
{ "$ref": "#/$defs/ValueRef" },
|
|
305
|
+
{ "$ref": "#/$defs/LiteralWrapper" },
|
|
306
|
+
{ "$ref": "#/$defs/PlainValue" }
|
|
307
|
+
]
|
|
308
|
+
},
|
|
309
|
+
|
|
310
|
+
"OrderedOperand": {
|
|
311
|
+
"title": "Ordered operand",
|
|
312
|
+
"description": "A value on which an ordering is defined: a number, or a string compared lexicographically — which for RFC 3339 date, date-time and time strings coincides with chronological order.",
|
|
313
|
+
"anyOf": [
|
|
314
|
+
{ "type": "number" },
|
|
315
|
+
{ "type": "string" },
|
|
316
|
+
{ "type": "string", "format": "date" },
|
|
317
|
+
{ "type": "string", "format": "date-time" },
|
|
318
|
+
{ "type": "string", "format": "time" },
|
|
319
|
+
{ "$ref": "#/$defs/ValueRef" }
|
|
320
|
+
],
|
|
321
|
+
"examples": [50, "2023-01-01", "2023-01-01T00:00:00Z", { "$field": "cost" }]
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
"ValueRef": {
|
|
325
|
+
"title": "Field reference",
|
|
326
|
+
"description": "Compares against another field of the same record rather than a constant: {\"price\": {\"$gt\": {\"$field\": \"cost\"}}} is SQL's WHERE price > cost.",
|
|
327
|
+
"type": "object",
|
|
328
|
+
"required": ["$field"],
|
|
329
|
+
"properties": {
|
|
330
|
+
"$field": { "$ref": "#/$defs/FieldPath" }
|
|
331
|
+
},
|
|
332
|
+
"additionalProperties": false
|
|
333
|
+
},
|
|
334
|
+
|
|
335
|
+
"LiteralWrapper": {
|
|
336
|
+
"title": "Literal escape",
|
|
337
|
+
"description": "Forces the wrapped value to be treated as data. Required only when an object operand would otherwise look like a field reference, e.g. {\"$literal\": {\"$field\": \"not-a-reference\"}}.",
|
|
338
|
+
"type": "object",
|
|
339
|
+
"required": ["$literal"],
|
|
340
|
+
"properties": {
|
|
341
|
+
"$literal": true
|
|
342
|
+
},
|
|
343
|
+
"additionalProperties": false
|
|
344
|
+
},
|
|
345
|
+
|
|
346
|
+
"PlainValue": {
|
|
347
|
+
"title": "Plain value",
|
|
348
|
+
"description": "Any JSON value that is not an object carrying '$'-prefixed keys. The '$' prefix is reserved for $field and $literal, so plain object operands must not use it.",
|
|
349
|
+
"if": { "type": "object" },
|
|
350
|
+
"then": { "type": "object", "propertyNames": { "pattern": "^[^$]" } }
|
|
351
|
+
},
|
|
352
|
+
|
|
353
|
+
"Bounds": {
|
|
354
|
+
"title": "Bounds",
|
|
355
|
+
"description": "An inclusive [lower, upper] pair. Both bounds SHOULD be of the same type.",
|
|
356
|
+
"type": "array",
|
|
357
|
+
"minItems": 2,
|
|
358
|
+
"maxItems": 2,
|
|
359
|
+
"items": { "$ref": "#/$defs/OrderedOperand" },
|
|
360
|
+
"examples": [[10, 20], ["2023-01-01", "2023-12-31"]]
|
|
361
|
+
},
|
|
362
|
+
|
|
363
|
+
"OperandSet": {
|
|
364
|
+
"title": "Operand set",
|
|
365
|
+
"description": "A non-empty list of distinct values. Servers MAY cap its length; see SPEC.md §7.",
|
|
366
|
+
"type": "array",
|
|
367
|
+
"minItems": 1,
|
|
368
|
+
"uniqueItems": true,
|
|
369
|
+
"items": { "$ref": "#/$defs/Operand" },
|
|
370
|
+
"examples": [["red", "green", "blue"], [1, 2, 3]]
|
|
371
|
+
},
|
|
372
|
+
|
|
373
|
+
"SizeConstraint": {
|
|
374
|
+
"title": "Size constraint",
|
|
375
|
+
"description": "An exact array length, or a comparison on that length.",
|
|
376
|
+
"anyOf": [
|
|
377
|
+
{ "type": "integer", "minimum": 0 },
|
|
378
|
+
{
|
|
379
|
+
"type": "object",
|
|
380
|
+
"minProperties": 1,
|
|
381
|
+
"properties": {
|
|
382
|
+
"$eq": { "type": "integer", "minimum": 0 },
|
|
383
|
+
"$ne": { "type": "integer", "minimum": 0 },
|
|
384
|
+
"$gt": { "type": "integer", "minimum": 0 },
|
|
385
|
+
"$gte": { "type": "integer", "minimum": 0 },
|
|
386
|
+
"$lt": { "type": "integer", "minimum": 0 },
|
|
387
|
+
"$lte": { "type": "integer", "minimum": 0 }
|
|
388
|
+
},
|
|
389
|
+
"additionalProperties": false
|
|
390
|
+
}
|
|
391
|
+
],
|
|
392
|
+
"examples": [3, { "$gte": 1 }]
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@open-predicate/open-predicate",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "OpenPredicate — an open standard for JSON-encoded, SQL-flavoured predicates, described by a single JSON Schema: $ref it from OpenAPI, or inline it into an MCP tool's inputSchema.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"author": "Christos Gkoros",
|
|
8
|
+
"contributors": [
|
|
9
|
+
"OpenPredicate (https://openpredicate.tech)"
|
|
10
|
+
],
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "git+https://github.com/OpenPredicate/open-predicate.git"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/OpenPredicate/open-predicate#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/OpenPredicate/open-predicate/issues"
|
|
18
|
+
},
|
|
19
|
+
"keywords": [
|
|
20
|
+
"open-predicate",
|
|
21
|
+
"openpredicate",
|
|
22
|
+
"predicate",
|
|
23
|
+
"json-schema",
|
|
24
|
+
"openapi",
|
|
25
|
+
"query-language",
|
|
26
|
+
"filter",
|
|
27
|
+
"search",
|
|
28
|
+
"mcp",
|
|
29
|
+
"model-context-protocol",
|
|
30
|
+
"agents",
|
|
31
|
+
"llm-tools",
|
|
32
|
+
"http-query-method"
|
|
33
|
+
],
|
|
34
|
+
"main": "open-predicate-schema.json",
|
|
35
|
+
"bin": {
|
|
36
|
+
"open-predicate-generate": "tools/generate-filter-schema.mjs"
|
|
37
|
+
},
|
|
38
|
+
"exports": {
|
|
39
|
+
".": "./open-predicate-schema.json",
|
|
40
|
+
"./open-predicate-schema.json": "./open-predicate-schema.json",
|
|
41
|
+
"./generate": "./tools/generate-filter-schema.mjs"
|
|
42
|
+
},
|
|
43
|
+
"files": [
|
|
44
|
+
"open-predicate-schema.json",
|
|
45
|
+
"tools/",
|
|
46
|
+
"SPEC.md",
|
|
47
|
+
"README.md",
|
|
48
|
+
"CHANGELOG.md"
|
|
49
|
+
],
|
|
50
|
+
"scripts": {
|
|
51
|
+
"test": "node --test tests/*.test.mjs",
|
|
52
|
+
"generate": "node tools/generate-filter-schema.mjs",
|
|
53
|
+
"generate:example": "node tools/generate-filter-schema.mjs --config examples/pet.open-predicate.config.json",
|
|
54
|
+
"example:mcp": "node examples/mcp-server/server.mjs",
|
|
55
|
+
"example:mcp:demo": "node examples/mcp-server/demo.mjs",
|
|
56
|
+
"test:experiment": "node --test experiments/filter-to-sql/compile.test.mjs",
|
|
57
|
+
"experiment": "node experiments/filter-to-sql/run.mjs"
|
|
58
|
+
},
|
|
59
|
+
"devDependencies": {
|
|
60
|
+
"ajv": "^8.17.1",
|
|
61
|
+
"ajv-formats": "^3.0.1"
|
|
62
|
+
},
|
|
63
|
+
"publishConfig": {
|
|
64
|
+
"access": "public",
|
|
65
|
+
"registry": "https://registry.npmjs.org"
|
|
66
|
+
}
|
|
67
|
+
}
|