@panneau/protocol 0.0.0-stage → 5.0.0-alpha.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 +31 -2
- package/dist/conditions.d.ts +7 -0
- package/dist/conditions.d.ts.map +1 -0
- package/dist/conditions.js +21 -0
- package/dist/conditions.js.map +1 -0
- package/dist/conformance/index.d.ts +42 -0
- package/dist/conformance/index.d.ts.map +1 -0
- package/dist/conformance/index.js +243 -0
- package/dist/conformance/index.js.map +1 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +19 -0
- package/dist/lint.d.ts +8 -0
- package/dist/lint.d.ts.map +1 -0
- package/dist/lint.js +73 -0
- package/dist/lint.js.map +1 -0
- package/dist/mock/example.d.ts +8 -0
- package/dist/mock/example.d.ts.map +1 -0
- package/dist/mock/example.js +186 -0
- package/dist/mock/example.js.map +1 -0
- package/dist/mock/index.d.ts +24 -0
- package/dist/mock/index.d.ts.map +1 -0
- package/dist/mock/index.js +250 -0
- package/dist/mock/index.js.map +1 -0
- package/dist/openapi.d.ts +11 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +540 -0
- package/dist/openapi.js.map +1 -0
- package/dist/query.d.ts +17 -0
- package/dist/query.d.ts.map +1 -0
- package/dist/query.js +49 -0
- package/dist/query.js.map +1 -0
- package/dist/rules.d.ts +29 -0
- package/dist/rules.d.ts.map +1 -0
- package/dist/rules.js +143 -0
- package/dist/rules.js.map +1 -0
- package/dist/schemas/action.d.ts +166 -0
- package/dist/schemas/action.d.ts.map +1 -0
- package/dist/schemas/action.js +66 -0
- package/dist/schemas/action.js.map +1 -0
- package/dist/schemas/api.d.ts +371 -0
- package/dist/schemas/api.d.ts.map +1 -0
- package/dist/schemas/api.js +140 -0
- package/dist/schemas/api.js.map +1 -0
- package/dist/schemas/boot.d.ts +19 -0
- package/dist/schemas/boot.d.ts.map +1 -0
- package/dist/schemas/boot.js +21 -0
- package/dist/schemas/boot.js.map +1 -0
- package/dist/schemas/common.d.ts +28 -0
- package/dist/schemas/common.d.ts.map +1 -0
- package/dist/schemas/common.js +35 -0
- package/dist/schemas/common.js.map +1 -0
- package/dist/schemas/definition.d.ts +462 -0
- package/dist/schemas/definition.d.ts.map +1 -0
- package/dist/schemas/definition.js +101 -0
- package/dist/schemas/definition.js.map +1 -0
- package/dist/schemas/field.d.ts +145 -0
- package/dist/schemas/field.d.ts.map +1 -0
- package/dist/schemas/field.js +127 -0
- package/dist/schemas/field.js.map +1 -0
- package/dist/schemas/index.d.ts +1536 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +68 -0
- package/dist/schemas/index.js.map +1 -0
- package/dist/schemas/layout.d.ts +29 -0
- package/dist/schemas/layout.d.ts.map +1 -0
- package/dist/schemas/layout.js +35 -0
- package/dist/schemas/layout.js.map +1 -0
- package/dist/schemas/list.d.ts +72 -0
- package/dist/schemas/list.d.ts.map +1 -0
- package/dist/schemas/list.js +48 -0
- package/dist/schemas/list.js.map +1 -0
- package/dist/schemas/resource.d.ts +328 -0
- package/dist/schemas/resource.d.ts.map +1 -0
- package/dist/schemas/resource.js +38 -0
- package/dist/schemas/resource.js.map +1 -0
- package/dist/validate.d.ts +31 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +57 -0
- package/dist/validate.js.map +1 -0
- package/dist/values.d.ts +7 -0
- package/dist/values.d.ts.map +1 -0
- package/dist/values.js +29 -0
- package/dist/values.js.map +1 -0
- package/dist/version.d.ts +5 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +9 -0
- package/dist/version.js.map +1 -0
- package/package.json +48 -5
- package/schema/boot.schema.json +1229 -0
- package/schema/definition.schema.json +1195 -0
- package/schema/openapi.json +3838 -0
package/dist/openapi.js
ADDED
|
@@ -0,0 +1,540 @@
|
|
|
1
|
+
import { PROBLEM_CONTENT_TYPE } from "./version.js";
|
|
2
|
+
import { Definition } from "./schemas/definition.js";
|
|
3
|
+
import { BootConfig } from "./schemas/boot.js";
|
|
4
|
+
import { createSchemaRegistry } from "./schemas/index.js";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
//#region src/openapi.ts
|
|
7
|
+
var ref = (id) => ({ $ref: `#/components/schemas/${id}` });
|
|
8
|
+
var json = (schema) => ({ "application/json": { schema } });
|
|
9
|
+
var ok = (description, schema, headers) => ({
|
|
10
|
+
description,
|
|
11
|
+
...schema !== void 0 ? { content: json(schema) } : {},
|
|
12
|
+
...headers !== void 0 ? { headers } : {}
|
|
13
|
+
});
|
|
14
|
+
var PROBLEMS = {
|
|
15
|
+
401: "Not authenticated.",
|
|
16
|
+
403: "Not allowed.",
|
|
17
|
+
404: "Not found.",
|
|
18
|
+
409: "Conflict with a business rule.",
|
|
19
|
+
412: "Version conflict: `If-Match` does not match the current version, which is in `current`.",
|
|
20
|
+
422: "Invalid values: messages are in `errors`, keyed by dot path."
|
|
21
|
+
};
|
|
22
|
+
function problems(...statuses) {
|
|
23
|
+
return Object.fromEntries(statuses.map((status) => [String(status), {
|
|
24
|
+
description: PROBLEMS[status],
|
|
25
|
+
content: { [PROBLEM_CONTENT_TYPE]: { schema: ref("Problem") } }
|
|
26
|
+
}]));
|
|
27
|
+
}
|
|
28
|
+
var etag = { ETag: {
|
|
29
|
+
description: "The version of the item.",
|
|
30
|
+
schema: { type: "string" }
|
|
31
|
+
} };
|
|
32
|
+
var pathParameter = (name, description) => ({
|
|
33
|
+
name,
|
|
34
|
+
in: "path",
|
|
35
|
+
required: true,
|
|
36
|
+
description,
|
|
37
|
+
schema: { type: "string" }
|
|
38
|
+
});
|
|
39
|
+
var queryParameter = (name, description, schema) => ({
|
|
40
|
+
name,
|
|
41
|
+
in: "query",
|
|
42
|
+
required: false,
|
|
43
|
+
description,
|
|
44
|
+
schema
|
|
45
|
+
});
|
|
46
|
+
var listParameters = [
|
|
47
|
+
queryParameter("page", "Page number, from 1.", {
|
|
48
|
+
type: "integer",
|
|
49
|
+
minimum: 1
|
|
50
|
+
}),
|
|
51
|
+
queryParameter("perPage", "Items per page.", {
|
|
52
|
+
type: "integer",
|
|
53
|
+
minimum: 1
|
|
54
|
+
}),
|
|
55
|
+
queryParameter("sort", "Comma-separated fields; a `-` prefix sorts descending.", { type: "string" }),
|
|
56
|
+
{
|
|
57
|
+
name: "filter",
|
|
58
|
+
in: "query",
|
|
59
|
+
required: false,
|
|
60
|
+
style: "deepObject",
|
|
61
|
+
explode: true,
|
|
62
|
+
description: "`filter[status]=published`, `filter[tags][]=a`.",
|
|
63
|
+
schema: {
|
|
64
|
+
type: "object",
|
|
65
|
+
additionalProperties: true
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
queryParameter("q", "Search.", { type: "string" }),
|
|
69
|
+
queryParameter("fields", "Comma-separated fields to include in each item.", { type: "string" })
|
|
70
|
+
];
|
|
71
|
+
var resource = pathParameter("resource", "Resource id.");
|
|
72
|
+
var id = pathParameter("id", "Item id.");
|
|
73
|
+
var values = {
|
|
74
|
+
type: "object",
|
|
75
|
+
additionalProperties: true
|
|
76
|
+
};
|
|
77
|
+
/** Component schemas, generated from the protocol's Zod schemas. */
|
|
78
|
+
function createComponentSchemas() {
|
|
79
|
+
const { schemas } = z.toJSONSchema(createSchemaRegistry(), {
|
|
80
|
+
io: "input",
|
|
81
|
+
uri: (schemaId) => `#/components/schemas/${schemaId}`
|
|
82
|
+
});
|
|
83
|
+
return Object.fromEntries(Object.entries(schemas).map(([schemaId, schema]) => {
|
|
84
|
+
const { $schema: _schema, $id: _id, ...rest } = schema;
|
|
85
|
+
return [schemaId, rest];
|
|
86
|
+
}));
|
|
87
|
+
}
|
|
88
|
+
/** The JSON Schema of the definition (`GET /definition`). */
|
|
89
|
+
function createDefinitionJsonSchema() {
|
|
90
|
+
return {
|
|
91
|
+
$id: "https://github.com/folkloreinc/panneau-js/blob/v5.x/packages/protocol/schema/definition.schema.json",
|
|
92
|
+
...z.toJSONSchema(Definition, {
|
|
93
|
+
io: "input",
|
|
94
|
+
metadata: createSchemaRegistry()
|
|
95
|
+
})
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/** The JSON Schema of the boot config, which a server writes in the page of the app. */
|
|
99
|
+
function createBootConfigJsonSchema() {
|
|
100
|
+
return {
|
|
101
|
+
$id: "https://github.com/folkloreinc/panneau-js/blob/v5.x/packages/protocol/schema/boot.schema.json",
|
|
102
|
+
...z.toJSONSchema(BootConfig, {
|
|
103
|
+
io: "input",
|
|
104
|
+
metadata: createSchemaRegistry()
|
|
105
|
+
})
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
/** The OpenAPI 3.1 document of the Panneau HTTP API. */
|
|
109
|
+
function createOpenApiDocument() {
|
|
110
|
+
return {
|
|
111
|
+
openapi: "3.1.0",
|
|
112
|
+
info: {
|
|
113
|
+
title: "Panneau protocol",
|
|
114
|
+
version: String(1),
|
|
115
|
+
description: "The HTTP API between a Panneau frontend and its backend. Paths are relative to `api.baseUrl` of the definition. Authentication is up to the backend (session cookie or bearer token). Errors use `application/problem+json`."
|
|
116
|
+
},
|
|
117
|
+
paths: {
|
|
118
|
+
"/definition": { get: {
|
|
119
|
+
operationId: "getDefinition",
|
|
120
|
+
summary: "The definition for the current user, in the current locale.",
|
|
121
|
+
responses: {
|
|
122
|
+
200: ok("The definition.", ref("Definition")),
|
|
123
|
+
...problems(401, 403)
|
|
124
|
+
}
|
|
125
|
+
} },
|
|
126
|
+
"/resources/{resource}": {
|
|
127
|
+
parameters: [resource],
|
|
128
|
+
get: {
|
|
129
|
+
operationId: "listItems",
|
|
130
|
+
parameters: listParameters,
|
|
131
|
+
responses: {
|
|
132
|
+
200: ok("A page of items.", ref("ListResponse")),
|
|
133
|
+
...problems(401, 403, 404)
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
post: {
|
|
137
|
+
operationId: "createItem",
|
|
138
|
+
requestBody: {
|
|
139
|
+
required: true,
|
|
140
|
+
content: json(values)
|
|
141
|
+
},
|
|
142
|
+
responses: {
|
|
143
|
+
201: ok("The created item.", ref("ItemResponse"), etag),
|
|
144
|
+
...problems(401, 403, 404, 409, 422)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"/resources/{resource}/{id}": {
|
|
149
|
+
parameters: [resource, id],
|
|
150
|
+
get: {
|
|
151
|
+
operationId: "getItem",
|
|
152
|
+
responses: {
|
|
153
|
+
200: ok("The item.", ref("ItemResponse"), etag),
|
|
154
|
+
...problems(401, 403, 404)
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
patch: {
|
|
158
|
+
operationId: "updateItem",
|
|
159
|
+
description: "Partial update: only the values sent are changed.",
|
|
160
|
+
parameters: [{
|
|
161
|
+
name: "If-Match",
|
|
162
|
+
in: "header",
|
|
163
|
+
required: false,
|
|
164
|
+
description: "The version the client edited.",
|
|
165
|
+
schema: { type: "string" }
|
|
166
|
+
}],
|
|
167
|
+
requestBody: {
|
|
168
|
+
required: true,
|
|
169
|
+
content: json(values)
|
|
170
|
+
},
|
|
171
|
+
responses: {
|
|
172
|
+
200: ok("The updated item.", ref("ItemResponse"), etag),
|
|
173
|
+
...problems(401, 403, 404, 409, 412, 422)
|
|
174
|
+
}
|
|
175
|
+
},
|
|
176
|
+
delete: {
|
|
177
|
+
operationId: "deleteItem",
|
|
178
|
+
responses: {
|
|
179
|
+
204: ok("Deleted."),
|
|
180
|
+
...problems(401, 403, 404, 409)
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
"/resources/{resource}/{id}/preview": {
|
|
185
|
+
parameters: [resource, id],
|
|
186
|
+
post: {
|
|
187
|
+
operationId: "previewItem",
|
|
188
|
+
requestBody: {
|
|
189
|
+
required: true,
|
|
190
|
+
content: json(values)
|
|
191
|
+
},
|
|
192
|
+
responses: {
|
|
193
|
+
200: ok("A short-lived preview URL.", ref("PreviewResponse")),
|
|
194
|
+
...problems(401, 403, 404, 422)
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
"/resources/{resource}/actions/{action}": {
|
|
199
|
+
parameters: [resource, pathParameter("action", "Action id.")],
|
|
200
|
+
post: {
|
|
201
|
+
operationId: "runAction",
|
|
202
|
+
requestBody: {
|
|
203
|
+
required: true,
|
|
204
|
+
content: {
|
|
205
|
+
...json(ref("ActionRequest")),
|
|
206
|
+
"multipart/form-data": { schema: ref("ActionRequest") }
|
|
207
|
+
}
|
|
208
|
+
},
|
|
209
|
+
responses: {
|
|
210
|
+
200: ok("The result.", ref("ActionResult")),
|
|
211
|
+
202: ok("The action runs in the background.", ref("ActionRunResponse")),
|
|
212
|
+
...problems(401, 403, 404, 409, 422)
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
"/actions/runs": { get: {
|
|
217
|
+
operationId: "listActionRuns",
|
|
218
|
+
summary: "The current user's recent runs.",
|
|
219
|
+
responses: {
|
|
220
|
+
200: ok("Runs, newest first.", {
|
|
221
|
+
type: "object",
|
|
222
|
+
required: ["data"],
|
|
223
|
+
properties: { data: {
|
|
224
|
+
type: "array",
|
|
225
|
+
items: ref("ActionRun")
|
|
226
|
+
} }
|
|
227
|
+
}),
|
|
228
|
+
...problems(401)
|
|
229
|
+
}
|
|
230
|
+
} },
|
|
231
|
+
"/actions/runs/{run}": {
|
|
232
|
+
parameters: [pathParameter("run", "Run id.")],
|
|
233
|
+
get: {
|
|
234
|
+
operationId: "getActionRun",
|
|
235
|
+
responses: {
|
|
236
|
+
200: ok("The run.", ref("ActionRunResponse")),
|
|
237
|
+
...problems(401, 403, 404)
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
delete: {
|
|
241
|
+
operationId: "cancelActionRun",
|
|
242
|
+
responses: {
|
|
243
|
+
204: ok("Cancelled."),
|
|
244
|
+
...problems(401, 403, 404, 409)
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
},
|
|
248
|
+
"/media": {
|
|
249
|
+
get: {
|
|
250
|
+
operationId: "listMedia",
|
|
251
|
+
parameters: listParameters,
|
|
252
|
+
responses: {
|
|
253
|
+
200: ok("A page of media.", ref("MediaListResponse")),
|
|
254
|
+
...problems(401, 403)
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
post: {
|
|
258
|
+
operationId: "uploadMedia",
|
|
259
|
+
requestBody: {
|
|
260
|
+
required: true,
|
|
261
|
+
content: { "multipart/form-data": { schema: {
|
|
262
|
+
type: "object",
|
|
263
|
+
required: ["file"],
|
|
264
|
+
properties: {
|
|
265
|
+
file: {
|
|
266
|
+
type: "string",
|
|
267
|
+
contentMediaType: "application/octet-stream"
|
|
268
|
+
},
|
|
269
|
+
name: { type: "string" },
|
|
270
|
+
tags: {
|
|
271
|
+
type: "array",
|
|
272
|
+
items: { type: "string" }
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
} } }
|
|
276
|
+
},
|
|
277
|
+
responses: {
|
|
278
|
+
201: ok("The created media.", ref("MediaResponse")),
|
|
279
|
+
...problems(401, 403, 422)
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
"/media/tags": { get: {
|
|
284
|
+
operationId: "listMediaTags",
|
|
285
|
+
responses: {
|
|
286
|
+
200: ok("Tags in use.", {
|
|
287
|
+
type: "object",
|
|
288
|
+
required: ["data"],
|
|
289
|
+
properties: { data: {
|
|
290
|
+
type: "array",
|
|
291
|
+
items: { type: "string" }
|
|
292
|
+
} }
|
|
293
|
+
}),
|
|
294
|
+
...problems(401, 403)
|
|
295
|
+
}
|
|
296
|
+
} },
|
|
297
|
+
"/media/{id}": {
|
|
298
|
+
parameters: [pathParameter("id", "Media id.")],
|
|
299
|
+
get: {
|
|
300
|
+
operationId: "getMedia",
|
|
301
|
+
responses: {
|
|
302
|
+
200: ok("The media.", ref("MediaResponse")),
|
|
303
|
+
...problems(401, 403, 404)
|
|
304
|
+
}
|
|
305
|
+
},
|
|
306
|
+
patch: {
|
|
307
|
+
operationId: "updateMedia",
|
|
308
|
+
description: "Updates metadata, tags or the image `transform`.",
|
|
309
|
+
requestBody: {
|
|
310
|
+
required: true,
|
|
311
|
+
content: json(values)
|
|
312
|
+
},
|
|
313
|
+
responses: {
|
|
314
|
+
200: ok("The updated media.", ref("MediaResponse")),
|
|
315
|
+
...problems(401, 403, 404, 422)
|
|
316
|
+
}
|
|
317
|
+
},
|
|
318
|
+
delete: {
|
|
319
|
+
operationId: "deleteMedia",
|
|
320
|
+
responses: {
|
|
321
|
+
204: ok("Moved to the trash."),
|
|
322
|
+
...problems(401, 403, 404, 409)
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
},
|
|
326
|
+
"/media/{id}/file": {
|
|
327
|
+
parameters: [pathParameter("id", "Media id.")],
|
|
328
|
+
post: {
|
|
329
|
+
operationId: "replaceMediaFile",
|
|
330
|
+
requestBody: {
|
|
331
|
+
required: true,
|
|
332
|
+
content: { "multipart/form-data": { schema: {
|
|
333
|
+
type: "object",
|
|
334
|
+
required: ["file"],
|
|
335
|
+
properties: { file: {
|
|
336
|
+
type: "string",
|
|
337
|
+
contentMediaType: "application/octet-stream"
|
|
338
|
+
} }
|
|
339
|
+
} } }
|
|
340
|
+
},
|
|
341
|
+
responses: {
|
|
342
|
+
200: ok("The media.", ref("MediaResponse")),
|
|
343
|
+
...problems(401, 403, 404, 422)
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
},
|
|
347
|
+
"/media/{id}/restore": {
|
|
348
|
+
parameters: [pathParameter("id", "Media id.")],
|
|
349
|
+
post: {
|
|
350
|
+
operationId: "restoreMedia",
|
|
351
|
+
responses: {
|
|
352
|
+
200: ok("The restored media.", ref("MediaResponse")),
|
|
353
|
+
...problems(401, 403, 404)
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
"/auth/session": { get: {
|
|
358
|
+
operationId: "getSession",
|
|
359
|
+
responses: {
|
|
360
|
+
200: ok("The current user.", ref("Session")),
|
|
361
|
+
...problems(401)
|
|
362
|
+
}
|
|
363
|
+
} },
|
|
364
|
+
"/auth/login": { post: {
|
|
365
|
+
operationId: "login",
|
|
366
|
+
requestBody: {
|
|
367
|
+
required: true,
|
|
368
|
+
content: json({
|
|
369
|
+
type: "object",
|
|
370
|
+
required: ["email", "password"],
|
|
371
|
+
properties: {
|
|
372
|
+
email: { type: "string" },
|
|
373
|
+
password: { type: "string" },
|
|
374
|
+
remember: { type: "boolean" }
|
|
375
|
+
}
|
|
376
|
+
})
|
|
377
|
+
},
|
|
378
|
+
responses: {
|
|
379
|
+
200: ok("Logged in.", ref("Session")),
|
|
380
|
+
202: ok("A second factor is required: answer `/auth/two-factor/challenge`.", {
|
|
381
|
+
type: "object",
|
|
382
|
+
properties: { twoFactor: {
|
|
383
|
+
type: "boolean",
|
|
384
|
+
const: true
|
|
385
|
+
} }
|
|
386
|
+
}),
|
|
387
|
+
...problems(422)
|
|
388
|
+
}
|
|
389
|
+
} },
|
|
390
|
+
"/auth/logout": { post: {
|
|
391
|
+
operationId: "logout",
|
|
392
|
+
responses: { 204: ok("Logged out.") }
|
|
393
|
+
} },
|
|
394
|
+
"/auth/providers/{provider}/redirect": {
|
|
395
|
+
parameters: [pathParameter("provider", "Provider id.")],
|
|
396
|
+
get: {
|
|
397
|
+
operationId: "redirectToProvider",
|
|
398
|
+
parameters: [queryParameter("next", "Local path to go to after login.", { type: "string" })],
|
|
399
|
+
responses: {
|
|
400
|
+
302: { description: "Redirect to the provider." },
|
|
401
|
+
...problems(404)
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
},
|
|
405
|
+
"/auth/password/forgot": { post: {
|
|
406
|
+
operationId: "forgotPassword",
|
|
407
|
+
requestBody: {
|
|
408
|
+
required: true,
|
|
409
|
+
content: json({
|
|
410
|
+
type: "object",
|
|
411
|
+
required: ["email"],
|
|
412
|
+
properties: { email: { type: "string" } }
|
|
413
|
+
})
|
|
414
|
+
},
|
|
415
|
+
responses: {
|
|
416
|
+
204: ok("A reset link was sent, if the account exists."),
|
|
417
|
+
...problems(422)
|
|
418
|
+
}
|
|
419
|
+
} },
|
|
420
|
+
"/auth/password/reset": { post: {
|
|
421
|
+
operationId: "resetPassword",
|
|
422
|
+
requestBody: {
|
|
423
|
+
required: true,
|
|
424
|
+
content: json({
|
|
425
|
+
type: "object",
|
|
426
|
+
required: [
|
|
427
|
+
"token",
|
|
428
|
+
"email",
|
|
429
|
+
"password"
|
|
430
|
+
],
|
|
431
|
+
properties: {
|
|
432
|
+
token: { type: "string" },
|
|
433
|
+
email: { type: "string" },
|
|
434
|
+
password: { type: "string" }
|
|
435
|
+
}
|
|
436
|
+
})
|
|
437
|
+
},
|
|
438
|
+
responses: {
|
|
439
|
+
204: ok("The password was reset."),
|
|
440
|
+
...problems(422)
|
|
441
|
+
}
|
|
442
|
+
} },
|
|
443
|
+
"/auth/two-factor/challenge": { post: {
|
|
444
|
+
operationId: "twoFactorChallenge",
|
|
445
|
+
requestBody: {
|
|
446
|
+
required: true,
|
|
447
|
+
content: json({
|
|
448
|
+
type: "object",
|
|
449
|
+
properties: {
|
|
450
|
+
code: { type: "string" },
|
|
451
|
+
recoveryCode: { type: "string" }
|
|
452
|
+
}
|
|
453
|
+
})
|
|
454
|
+
},
|
|
455
|
+
responses: {
|
|
456
|
+
200: ok("Logged in.", ref("Session")),
|
|
457
|
+
...problems(422)
|
|
458
|
+
}
|
|
459
|
+
} },
|
|
460
|
+
"/auth/passkeys/options": { post: {
|
|
461
|
+
operationId: "passkeyOptions",
|
|
462
|
+
responses: { 200: ok("WebAuthn request options.", {
|
|
463
|
+
type: "object",
|
|
464
|
+
additionalProperties: true
|
|
465
|
+
}) }
|
|
466
|
+
} },
|
|
467
|
+
"/auth/passkeys/login": { post: {
|
|
468
|
+
operationId: "passkeyLogin",
|
|
469
|
+
requestBody: {
|
|
470
|
+
required: true,
|
|
471
|
+
content: json({
|
|
472
|
+
type: "object",
|
|
473
|
+
additionalProperties: true
|
|
474
|
+
})
|
|
475
|
+
},
|
|
476
|
+
responses: {
|
|
477
|
+
200: ok("Logged in.", ref("Session")),
|
|
478
|
+
...problems(422)
|
|
479
|
+
}
|
|
480
|
+
} },
|
|
481
|
+
"/account": {
|
|
482
|
+
get: {
|
|
483
|
+
operationId: "getAccount",
|
|
484
|
+
responses: {
|
|
485
|
+
200: ok("The current user.", {
|
|
486
|
+
type: "object",
|
|
487
|
+
required: ["data"],
|
|
488
|
+
properties: { data: ref("User") }
|
|
489
|
+
}),
|
|
490
|
+
...problems(401)
|
|
491
|
+
}
|
|
492
|
+
},
|
|
493
|
+
patch: {
|
|
494
|
+
operationId: "updateAccount",
|
|
495
|
+
requestBody: {
|
|
496
|
+
required: true,
|
|
497
|
+
content: json(values)
|
|
498
|
+
},
|
|
499
|
+
responses: {
|
|
500
|
+
200: ok("The updated user.", {
|
|
501
|
+
type: "object",
|
|
502
|
+
required: ["data"],
|
|
503
|
+
properties: { data: ref("User") }
|
|
504
|
+
}),
|
|
505
|
+
...problems(401, 422)
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
},
|
|
509
|
+
"/search": { get: {
|
|
510
|
+
operationId: "search",
|
|
511
|
+
parameters: [{
|
|
512
|
+
...queryParameter("q", "Search terms.", { type: "string" }),
|
|
513
|
+
required: true
|
|
514
|
+
}],
|
|
515
|
+
responses: {
|
|
516
|
+
200: ok("Results grouped by resource.", ref("SearchResponse")),
|
|
517
|
+
...problems(401)
|
|
518
|
+
}
|
|
519
|
+
} },
|
|
520
|
+
"/dashboard/widgets/{widget}": {
|
|
521
|
+
parameters: [pathParameter("widget", "Widget id.")],
|
|
522
|
+
get: {
|
|
523
|
+
operationId: "getWidgetData",
|
|
524
|
+
responses: {
|
|
525
|
+
200: ok("The widget's data. Its shape depends on the widget type.", {
|
|
526
|
+
type: "object",
|
|
527
|
+
additionalProperties: true
|
|
528
|
+
}),
|
|
529
|
+
...problems(401, 403, 404)
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
components: { schemas: createComponentSchemas() }
|
|
535
|
+
};
|
|
536
|
+
}
|
|
537
|
+
//#endregion
|
|
538
|
+
export { createBootConfigJsonSchema, createComponentSchemas, createDefinitionJsonSchema, createOpenApiDocument };
|
|
539
|
+
|
|
540
|
+
//# sourceMappingURL=openapi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi.js","names":[],"sources":["../src/openapi.ts"],"sourcesContent":["import { z } from 'zod';\n\nimport { createSchemaRegistry } from './schemas';\nimport { BootConfig } from './schemas/boot';\nimport { Definition } from './schemas/definition';\nimport { PROBLEM_CONTENT_TYPE, PROTOCOL_VERSION } from './version';\n\ntype JsonObject = Record<string, unknown>;\n\nconst ref = (id: string): JsonObject => ({ $ref: `#/components/schemas/${id}` });\n\nconst json = (schema: JsonObject): JsonObject => ({ 'application/json': { schema } });\n\nconst ok = (description: string, schema?: JsonObject, headers?: JsonObject): JsonObject => ({\n description,\n ...(schema !== undefined ? { content: json(schema) } : {}),\n ...(headers !== undefined ? { headers } : {}),\n});\n\nconst PROBLEMS: Record<number, string> = {\n 401: 'Not authenticated.',\n 403: 'Not allowed.',\n 404: 'Not found.',\n 409: 'Conflict with a business rule.',\n 412: 'Version conflict: `If-Match` does not match the current version, which is in `current`.',\n 422: 'Invalid values: messages are in `errors`, keyed by dot path.',\n};\n\nfunction problems(...statuses: number[]): JsonObject {\n return Object.fromEntries(\n statuses.map((status) => [\n String(status),\n {\n description: PROBLEMS[status],\n content: { [PROBLEM_CONTENT_TYPE]: { schema: ref('Problem') } },\n },\n ]),\n );\n}\n\nconst etag = { ETag: { description: 'The version of the item.', schema: { type: 'string' } } };\n\nconst pathParameter = (name: string, description: string): JsonObject => ({\n name,\n in: 'path',\n required: true,\n description,\n schema: { type: 'string' },\n});\n\nconst queryParameter = (name: string, description: string, schema: JsonObject): JsonObject => ({\n name,\n in: 'query',\n required: false,\n description,\n schema,\n});\n\nconst listParameters = [\n queryParameter('page', 'Page number, from 1.', { type: 'integer', minimum: 1 }),\n queryParameter('perPage', 'Items per page.', { type: 'integer', minimum: 1 }),\n queryParameter('sort', 'Comma-separated fields; a `-` prefix sorts descending.', {\n type: 'string',\n }),\n {\n name: 'filter',\n in: 'query',\n required: false,\n style: 'deepObject',\n explode: true,\n description: '`filter[status]=published`, `filter[tags][]=a`.',\n schema: { type: 'object', additionalProperties: true },\n },\n queryParameter('q', 'Search.', { type: 'string' }),\n queryParameter('fields', 'Comma-separated fields to include in each item.', { type: 'string' }),\n];\n\nconst resource = pathParameter('resource', 'Resource id.');\nconst id = pathParameter('id', 'Item id.');\nconst values = { type: 'object', additionalProperties: true };\n\n/** Component schemas, generated from the protocol's Zod schemas. */\nexport function createComponentSchemas(): Record<string, JsonObject> {\n const { schemas } = z.toJSONSchema(createSchemaRegistry(), {\n io: 'input',\n uri: (schemaId) => `#/components/schemas/${schemaId}`,\n });\n return Object.fromEntries(\n Object.entries(schemas).map(([schemaId, schema]) => {\n const { $schema: _schema, $id: _id, ...rest } = schema as JsonObject;\n return [schemaId, rest];\n }),\n );\n}\n\n/** The JSON Schema of the definition (`GET /definition`). */\nexport function createDefinitionJsonSchema(): JsonObject {\n return {\n $id: 'https://github.com/folkloreinc/panneau-js/blob/v5.x/packages/protocol/schema/definition.schema.json',\n ...(z.toJSONSchema(Definition, {\n io: 'input',\n metadata: createSchemaRegistry(),\n }) as JsonObject),\n };\n}\n\n/** The JSON Schema of the boot config, which a server writes in the page of the app. */\nexport function createBootConfigJsonSchema(): JsonObject {\n return {\n $id: 'https://github.com/folkloreinc/panneau-js/blob/v5.x/packages/protocol/schema/boot.schema.json',\n ...(z.toJSONSchema(BootConfig, {\n io: 'input',\n metadata: createSchemaRegistry(),\n }) as JsonObject),\n };\n}\n\n/** The OpenAPI 3.1 document of the Panneau HTTP API. */\nexport function createOpenApiDocument(): JsonObject {\n return {\n openapi: '3.1.0',\n info: {\n title: 'Panneau protocol',\n version: String(PROTOCOL_VERSION),\n description:\n 'The HTTP API between a Panneau frontend and its backend. Paths are relative to `api.baseUrl` of the definition. Authentication is up to the backend (session cookie or bearer token). Errors use `application/problem+json`.',\n },\n paths: {\n '/definition': {\n get: {\n operationId: 'getDefinition',\n summary: 'The definition for the current user, in the current locale.',\n responses: {\n 200: ok('The definition.', ref('Definition')),\n ...problems(401, 403),\n },\n },\n },\n '/resources/{resource}': {\n parameters: [resource],\n get: {\n operationId: 'listItems',\n parameters: listParameters,\n responses: {\n 200: ok('A page of items.', ref('ListResponse')),\n ...problems(401, 403, 404),\n },\n },\n post: {\n operationId: 'createItem',\n requestBody: { required: true, content: json(values) },\n responses: {\n 201: ok('The created item.', ref('ItemResponse'), etag),\n ...problems(401, 403, 404, 409, 422),\n },\n },\n },\n '/resources/{resource}/{id}': {\n parameters: [resource, id],\n get: {\n operationId: 'getItem',\n responses: {\n 200: ok('The item.', ref('ItemResponse'), etag),\n ...problems(401, 403, 404),\n },\n },\n patch: {\n operationId: 'updateItem',\n description: 'Partial update: only the values sent are changed.',\n parameters: [\n {\n name: 'If-Match',\n in: 'header',\n required: false,\n description: 'The version the client edited.',\n schema: { type: 'string' },\n },\n ],\n requestBody: { required: true, content: json(values) },\n responses: {\n 200: ok('The updated item.', ref('ItemResponse'), etag),\n ...problems(401, 403, 404, 409, 412, 422),\n },\n },\n delete: {\n operationId: 'deleteItem',\n responses: { 204: ok('Deleted.'), ...problems(401, 403, 404, 409) },\n },\n },\n '/resources/{resource}/{id}/preview': {\n parameters: [resource, id],\n post: {\n operationId: 'previewItem',\n requestBody: { required: true, content: json(values) },\n responses: {\n 200: ok('A short-lived preview URL.', ref('PreviewResponse')),\n ...problems(401, 403, 404, 422),\n },\n },\n },\n '/resources/{resource}/actions/{action}': {\n parameters: [resource, pathParameter('action', 'Action id.')],\n post: {\n operationId: 'runAction',\n requestBody: {\n required: true,\n content: {\n ...json(ref('ActionRequest')),\n 'multipart/form-data': { schema: ref('ActionRequest') },\n },\n },\n responses: {\n 200: ok('The result.', ref('ActionResult')),\n 202: ok('The action runs in the background.', ref('ActionRunResponse')),\n ...problems(401, 403, 404, 409, 422),\n },\n },\n },\n '/actions/runs': {\n get: {\n operationId: 'listActionRuns',\n summary: \"The current user's recent runs.\",\n responses: {\n 200: ok('Runs, newest first.', {\n type: 'object',\n required: ['data'],\n properties: { data: { type: 'array', items: ref('ActionRun') } },\n }),\n ...problems(401),\n },\n },\n },\n '/actions/runs/{run}': {\n parameters: [pathParameter('run', 'Run id.')],\n get: {\n operationId: 'getActionRun',\n responses: {\n 200: ok('The run.', ref('ActionRunResponse')),\n ...problems(401, 403, 404),\n },\n },\n delete: {\n operationId: 'cancelActionRun',\n responses: { 204: ok('Cancelled.'), ...problems(401, 403, 404, 409) },\n },\n },\n '/media': {\n get: {\n operationId: 'listMedia',\n parameters: listParameters,\n responses: {\n 200: ok('A page of media.', ref('MediaListResponse')),\n ...problems(401, 403),\n },\n },\n post: {\n operationId: 'uploadMedia',\n requestBody: {\n required: true,\n content: {\n 'multipart/form-data': {\n schema: {\n type: 'object',\n required: ['file'],\n properties: {\n file: {\n type: 'string',\n contentMediaType: 'application/octet-stream',\n },\n name: { type: 'string' },\n tags: { type: 'array', items: { type: 'string' } },\n },\n },\n },\n },\n },\n responses: {\n 201: ok('The created media.', ref('MediaResponse')),\n ...problems(401, 403, 422),\n },\n },\n },\n '/media/tags': {\n get: {\n operationId: 'listMediaTags',\n responses: {\n 200: ok('Tags in use.', {\n type: 'object',\n required: ['data'],\n properties: { data: { type: 'array', items: { type: 'string' } } },\n }),\n ...problems(401, 403),\n },\n },\n },\n '/media/{id}': {\n parameters: [pathParameter('id', 'Media id.')],\n get: {\n operationId: 'getMedia',\n responses: {\n 200: ok('The media.', ref('MediaResponse')),\n ...problems(401, 403, 404),\n },\n },\n patch: {\n operationId: 'updateMedia',\n description: 'Updates metadata, tags or the image `transform`.',\n requestBody: { required: true, content: json(values) },\n responses: {\n 200: ok('The updated media.', ref('MediaResponse')),\n ...problems(401, 403, 404, 422),\n },\n },\n delete: {\n operationId: 'deleteMedia',\n responses: { 204: ok('Moved to the trash.'), ...problems(401, 403, 404, 409) },\n },\n },\n '/media/{id}/file': {\n parameters: [pathParameter('id', 'Media id.')],\n post: {\n operationId: 'replaceMediaFile',\n requestBody: {\n required: true,\n content: {\n 'multipart/form-data': {\n schema: {\n type: 'object',\n required: ['file'],\n properties: {\n file: {\n type: 'string',\n contentMediaType: 'application/octet-stream',\n },\n },\n },\n },\n },\n },\n responses: {\n 200: ok('The media.', ref('MediaResponse')),\n ...problems(401, 403, 404, 422),\n },\n },\n },\n '/media/{id}/restore': {\n parameters: [pathParameter('id', 'Media id.')],\n post: {\n operationId: 'restoreMedia',\n responses: {\n 200: ok('The restored media.', ref('MediaResponse')),\n ...problems(401, 403, 404),\n },\n },\n },\n '/auth/session': {\n get: {\n operationId: 'getSession',\n responses: { 200: ok('The current user.', ref('Session')), ...problems(401) },\n },\n },\n '/auth/login': {\n post: {\n operationId: 'login',\n requestBody: {\n required: true,\n content: json({\n type: 'object',\n required: ['email', 'password'],\n properties: {\n email: { type: 'string' },\n password: { type: 'string' },\n remember: { type: 'boolean' },\n },\n }),\n },\n responses: {\n 200: ok('Logged in.', ref('Session')),\n 202: ok(\n 'A second factor is required: answer `/auth/two-factor/challenge`.',\n {\n type: 'object',\n properties: { twoFactor: { type: 'boolean', const: true } },\n },\n ),\n ...problems(422),\n },\n },\n },\n '/auth/logout': {\n post: { operationId: 'logout', responses: { 204: ok('Logged out.') } },\n },\n '/auth/providers/{provider}/redirect': {\n parameters: [pathParameter('provider', 'Provider id.')],\n get: {\n operationId: 'redirectToProvider',\n parameters: [\n queryParameter('next', 'Local path to go to after login.', {\n type: 'string',\n }),\n ],\n responses: {\n 302: { description: 'Redirect to the provider.' },\n ...problems(404),\n },\n },\n },\n '/auth/password/forgot': {\n post: {\n operationId: 'forgotPassword',\n requestBody: {\n required: true,\n content: json({\n type: 'object',\n required: ['email'],\n properties: { email: { type: 'string' } },\n }),\n },\n responses: {\n 204: ok('A reset link was sent, if the account exists.'),\n ...problems(422),\n },\n },\n },\n '/auth/password/reset': {\n post: {\n operationId: 'resetPassword',\n requestBody: {\n required: true,\n content: json({\n type: 'object',\n required: ['token', 'email', 'password'],\n properties: {\n token: { type: 'string' },\n email: { type: 'string' },\n password: { type: 'string' },\n },\n }),\n },\n responses: { 204: ok('The password was reset.'), ...problems(422) },\n },\n },\n '/auth/two-factor/challenge': {\n post: {\n operationId: 'twoFactorChallenge',\n requestBody: {\n required: true,\n content: json({\n type: 'object',\n properties: {\n code: { type: 'string' },\n recoveryCode: { type: 'string' },\n },\n }),\n },\n responses: { 200: ok('Logged in.', ref('Session')), ...problems(422) },\n },\n },\n '/auth/passkeys/options': {\n post: {\n operationId: 'passkeyOptions',\n responses: {\n 200: ok('WebAuthn request options.', {\n type: 'object',\n additionalProperties: true,\n }),\n },\n },\n },\n '/auth/passkeys/login': {\n post: {\n operationId: 'passkeyLogin',\n requestBody: {\n required: true,\n content: json({ type: 'object', additionalProperties: true }),\n },\n responses: { 200: ok('Logged in.', ref('Session')), ...problems(422) },\n },\n },\n '/account': {\n get: {\n operationId: 'getAccount',\n responses: {\n 200: ok('The current user.', {\n type: 'object',\n required: ['data'],\n properties: { data: ref('User') },\n }),\n ...problems(401),\n },\n },\n patch: {\n operationId: 'updateAccount',\n requestBody: { required: true, content: json(values) },\n responses: {\n 200: ok('The updated user.', {\n type: 'object',\n required: ['data'],\n properties: { data: ref('User') },\n }),\n ...problems(401, 422),\n },\n },\n },\n '/search': {\n get: {\n operationId: 'search',\n parameters: [\n {\n ...queryParameter('q', 'Search terms.', { type: 'string' }),\n required: true,\n },\n ],\n responses: {\n 200: ok('Results grouped by resource.', ref('SearchResponse')),\n ...problems(401),\n },\n },\n },\n '/dashboard/widgets/{widget}': {\n parameters: [pathParameter('widget', 'Widget id.')],\n get: {\n operationId: 'getWidgetData',\n responses: {\n 200: ok(\"The widget's data. Its shape depends on the widget type.\", {\n type: 'object',\n additionalProperties: true,\n }),\n ...problems(401, 403, 404),\n },\n },\n },\n },\n components: { schemas: createComponentSchemas() },\n };\n}\n"],"mappings":"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"}
|
package/dist/query.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export type FilterValue = string | number | boolean | Array<string | number | boolean>;
|
|
2
|
+
/** Query of a list request (`GET /resources/{resource}`). */
|
|
3
|
+
export interface ListQuery {
|
|
4
|
+
page?: number;
|
|
5
|
+
perPage?: number;
|
|
6
|
+
/** Fields to sort by, in order. A `-` prefix sorts descending. */
|
|
7
|
+
sort?: string[];
|
|
8
|
+
filter?: Record<string, FilterValue>;
|
|
9
|
+
q?: string;
|
|
10
|
+
/** Fields to include in each item. */
|
|
11
|
+
fields?: string[];
|
|
12
|
+
}
|
|
13
|
+
/** Serializes a list query to the protocol's query string format. */
|
|
14
|
+
export declare function toSearchParams(query: ListQuery): URLSearchParams;
|
|
15
|
+
/** Parses a list query from the protocol's query string format. Filter values stay strings. */
|
|
16
|
+
export declare function fromSearchParams(params: URLSearchParams): ListQuery;
|
|
17
|
+
//# sourceMappingURL=query.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../src/query.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,KAAK,CAAC,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,CAAC;AAEvF,6DAA6D;AAC7D,MAAM,WAAW,SAAS;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IACrC,CAAC,CAAC,EAAE,MAAM,CAAC;IACX,sCAAsC;IACtC,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,qEAAqE;AACrE,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,eAAe,CAyBhE;AAID,+FAA+F;AAC/F,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,eAAe,GAAG,SAAS,CA2CnE"}
|
package/dist/query.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
//#region src/query.ts
|
|
2
|
+
/** Serializes a list query to the protocol's query string format. */
|
|
3
|
+
function toSearchParams(query) {
|
|
4
|
+
const params = new URLSearchParams();
|
|
5
|
+
if (query.page !== void 0) params.set("page", String(query.page));
|
|
6
|
+
if (query.perPage !== void 0) params.set("perPage", String(query.perPage));
|
|
7
|
+
if (query.sort !== void 0 && query.sort.length > 0) params.set("sort", query.sort.join(","));
|
|
8
|
+
Object.entries(query.filter ?? {}).forEach(([key, value]) => {
|
|
9
|
+
if (Array.isArray(value)) value.forEach((item) => params.append(`filter[${key}][]`, String(item)));
|
|
10
|
+
else params.set(`filter[${key}]`, String(value));
|
|
11
|
+
});
|
|
12
|
+
if (query.q !== void 0 && query.q !== "") params.set("q", query.q);
|
|
13
|
+
if (query.fields !== void 0 && query.fields.length > 0) params.set("fields", query.fields.join(","));
|
|
14
|
+
return params;
|
|
15
|
+
}
|
|
16
|
+
var FILTER_KEY = /^filter\[([^\]]+)\](\[\])?$/;
|
|
17
|
+
/** Parses a list query from the protocol's query string format. Filter values stay strings. */
|
|
18
|
+
function fromSearchParams(params) {
|
|
19
|
+
const query = {};
|
|
20
|
+
const integer = (value) => {
|
|
21
|
+
if (value === null || !/^\d+$/.test(value)) return;
|
|
22
|
+
return Number.parseInt(value, 10);
|
|
23
|
+
};
|
|
24
|
+
const page = integer(params.get("page"));
|
|
25
|
+
if (page !== void 0) query.page = page;
|
|
26
|
+
const perPage = integer(params.get("perPage"));
|
|
27
|
+
if (perPage !== void 0) query.perPage = perPage;
|
|
28
|
+
const sort = params.get("sort");
|
|
29
|
+
if (sort !== null && sort !== "") query.sort = sort.split(",").filter((part) => part !== "");
|
|
30
|
+
const q = params.get("q");
|
|
31
|
+
if (q !== null && q !== "") query.q = q;
|
|
32
|
+
const fields = params.get("fields");
|
|
33
|
+
if (fields !== null && fields !== "") query.fields = fields.split(",").filter((part) => part !== "");
|
|
34
|
+
params.forEach((value, key) => {
|
|
35
|
+
const match = FILTER_KEY.exec(key);
|
|
36
|
+
if (match?.[1] === void 0) return;
|
|
37
|
+
query.filter ??= {};
|
|
38
|
+
const name = match[1];
|
|
39
|
+
if (match[2] !== void 0) {
|
|
40
|
+
const current = query.filter[name];
|
|
41
|
+
query.filter[name] = [...Array.isArray(current) ? current : [], value];
|
|
42
|
+
} else query.filter[name] = value;
|
|
43
|
+
});
|
|
44
|
+
return query;
|
|
45
|
+
}
|
|
46
|
+
//#endregion
|
|
47
|
+
export { fromSearchParams, toSearchParams };
|
|
48
|
+
|
|
49
|
+
//# sourceMappingURL=query.js.map
|