@kozou/api 0.0.1 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +200 -1
- package/dist/auth.d.ts +75 -0
- package/dist/auth.d.ts.map +1 -0
- package/dist/auth.js +128 -0
- package/dist/auth.js.map +1 -0
- package/dist/embed.d.ts +46 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +181 -0
- package/dist/embed.js.map +1 -0
- package/dist/errors.d.ts +19 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +33 -0
- package/dist/errors.js.map +1 -0
- package/dist/handler.d.ts +39 -0
- package/dist/handler.d.ts.map +1 -0
- package/dist/handler.js +267 -0
- package/dist/handler.js.map +1 -0
- package/dist/ident.d.ts +7 -0
- package/dist/ident.d.ts.map +1 -0
- package/dist/ident.js +12 -0
- package/dist/ident.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/openapi.d.ts +9 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +300 -0
- package/dist/openapi.js.map +1 -0
- package/dist/query-builder.d.ts +83 -0
- package/dist/query-builder.d.ts.map +1 -0
- package/dist/query-builder.js +592 -0
- package/dist/query-builder.js.map +1 -0
- package/dist/schema-lookup.d.ts +30 -0
- package/dist/schema-lookup.d.ts.map +1 -0
- package/dist/schema-lookup.js +61 -0
- package/dist/schema-lookup.js.map +1 -0
- package/dist/startApiServer.d.ts +53 -0
- package/dist/startApiServer.d.ts.map +1 -0
- package/dist/startApiServer.js +210 -0
- package/dist/startApiServer.js.map +1 -0
- package/package.json +44 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ident.d.ts","sourceRoot":"","sources":["../src/ident.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD;kBACkB;AAClB,wBAAgB,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,iDAAiD;AACjD,wBAAgB,SAAS,CAAC,QAAQ,EAAE,QAAQ,GAAG,MAAM,CAEpD"}
|
package/dist/ident.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// Identifier quoting + qualified table names. Shared by the query builder
|
|
2
|
+
// and the embed fragment renderer so both quote identifiers identically.
|
|
3
|
+
/** Quote an identifier for safe inlining (defense in depth on top of the
|
|
4
|
+
* allowlist). */
|
|
5
|
+
export function quoteIdent(id) {
|
|
6
|
+
return '"' + id.replace(/"/g, '""') + '"';
|
|
7
|
+
}
|
|
8
|
+
/** `"schema"."name"` for a resolved resource. */
|
|
9
|
+
export function qualified(resource) {
|
|
10
|
+
return `${quoteIdent(resource.schema)}.${quoteIdent(resource.name)}`;
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=ident.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ident.js","sourceRoot":"","sources":["../src/ident.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,yEAAyE;AAIzE;kBACkB;AAClB,MAAM,UAAU,UAAU,CAAC,EAAU;IACnC,OAAO,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,GAAG,CAAC;AAC5C,CAAC;AAED,iDAAiD;AACjD,MAAM,UAAU,SAAS,CAAC,QAAkB;IAC1C,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,UAAU,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;AACvE,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export { startApiServer, createApiRequestListener, isLoopbackHost, type StartApiServerOptions, type ApiServerHandle, type PoolClient, type ConnectionPool, } from './startApiServer.js';
|
|
2
|
+
export { createAuthenticator, signServiceToken, type AuthConfig, type AuthContext, type Authenticator, type JwtAlgorithm, type ServiceTokenOptions, } from './auth.js';
|
|
3
|
+
export { handleApiRequest, parseListParams, type Queryable, type ApiHandlerDeps, type ApiHttpRequest, type ApiHttpResult, } from './handler.js';
|
|
4
|
+
export { buildResourceLookup, type Resource, type ResourceKind, type ResourceLookup, type ReverseRelation, } from './schema-lookup.js';
|
|
5
|
+
export { buildListQuery, buildGetQuery, buildInsertQuery, buildUpdateQuery, buildDeleteQuery, buildRelationOptionsQuery, quoteIdent, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, DEFAULT_RELATION_LIMIT, MAX_RELATION_LIMIT, type ListQueryParams, type BuiltListQuery, type BuiltGetQuery, type BuiltMutation, type RelationOptionsParams, type BuiltRelationOptions, type SortDirection, type Filter, type FilterOperator, type ScalarFilterOperator, type IsKeyword, } from './query-builder.js';
|
|
6
|
+
export { buildOpenApiDocument, type OpenApiOptions } from './openapi.js';
|
|
7
|
+
export { parseEmbedParam, resolveEmbedSpec, buildEmbedSelectFragment, MAX_EMBED_DEPTH, MAX_EMBED_RELATIONS, MAX_EMBED_CHILDREN, type EmbedKind, type EmbedNode, type EmbedSpec, } from './embed.js';
|
|
8
|
+
export { KozouApiError, type ApiErrorBody } from './errors.js';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,cAAc,EACd,KAAK,qBAAqB,EAC1B,KAAK,eAAe,EACpB,KAAK,UAAU,EACf,KAAK,cAAc,GACpB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,KAAK,mBAAmB,GACzB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,gBAAgB,EAChB,eAAe,EACf,KAAK,SAAS,EACd,KAAK,cAAc,EACnB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,mBAAmB,EACnB,KAAK,QAAQ,EACb,KAAK,YAAY,EACjB,KAAK,cAAc,EACnB,KAAK,eAAe,GACrB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,yBAAyB,EACzB,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,kBAAkB,EAClB,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,EAClB,KAAK,aAAa,EAClB,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,EACzB,KAAK,aAAa,EAClB,KAAK,MAAM,EACX,KAAK,cAAc,EACnB,KAAK,oBAAoB,EACzB,KAAK,SAAS,GACf,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,oBAAoB,EAAE,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAEzE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,SAAS,EACd,KAAK,SAAS,EACd,KAAK,SAAS,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,aAAa,EAAE,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// @kozou/api: Kozou's own REST layer — the default `kozou dev` data backend.
|
|
2
|
+
// Serves the tables and views of a SchemaContext as a REST API. Its wire
|
|
3
|
+
// format and OpenAPI are a stable contract as of Kozou v1.0; see the README
|
|
4
|
+
// for the supported surface and the scope table.
|
|
5
|
+
export { startApiServer, createApiRequestListener, isLoopbackHost, } from './startApiServer.js';
|
|
6
|
+
export { createAuthenticator, signServiceToken, } from './auth.js';
|
|
7
|
+
export { handleApiRequest, parseListParams, } from './handler.js';
|
|
8
|
+
export { buildResourceLookup, } from './schema-lookup.js';
|
|
9
|
+
export { buildListQuery, buildGetQuery, buildInsertQuery, buildUpdateQuery, buildDeleteQuery, buildRelationOptionsQuery, quoteIdent, DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE, DEFAULT_RELATION_LIMIT, MAX_RELATION_LIMIT, } from './query-builder.js';
|
|
10
|
+
export { buildOpenApiDocument } from './openapi.js';
|
|
11
|
+
export { parseEmbedParam, resolveEmbedSpec, buildEmbedSelectFragment, MAX_EMBED_DEPTH, MAX_EMBED_RELATIONS, MAX_EMBED_CHILDREN, } from './embed.js';
|
|
12
|
+
export { KozouApiError } from './errors.js';
|
|
13
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,yEAAyE;AACzE,4EAA4E;AAC5E,iDAAiD;AAEjD,OAAO,EACL,cAAc,EACd,wBAAwB,EACxB,cAAc,GAKf,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,gBAAgB,GAMjB,MAAM,WAAW,CAAC;AAEnB,OAAO,EACL,gBAAgB,EAChB,eAAe,GAKhB,MAAM,cAAc,CAAC;AAEtB,OAAO,EACL,mBAAmB,GAKpB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EACL,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,yBAAyB,EACzB,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,sBAAsB,EACtB,kBAAkB,GAYnB,MAAM,oBAAoB,CAAC;AAE5B,OAAO,EAAE,oBAAoB,EAAuB,MAAM,cAAc,CAAC;AAEzE,OAAO,EACL,eAAe,EACf,gBAAgB,EAChB,wBAAwB,EACxB,eAAe,EACf,mBAAmB,EACnB,kBAAkB,GAInB,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,aAAa,EAAqB,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { SchemaContext } from '@kozou/core';
|
|
2
|
+
export type OpenApiOptions = {
|
|
3
|
+
title?: string;
|
|
4
|
+
version?: string;
|
|
5
|
+
};
|
|
6
|
+
type JsonObject = Record<string, unknown>;
|
|
7
|
+
export declare function buildOpenApiDocument(schema: SchemaContext, opts?: OpenApiOptions): JsonObject;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=openapi.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi.d.ts","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EACV,aAAa,EAMd,MAAM,aAAa,CAAC;AAGrB,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,CAAC;AAEF,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAK1C,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,aAAa,EACrB,IAAI,GAAE,cAAmB,GACxB,UAAU,CAuDZ"}
|
package/dist/openapi.js
ADDED
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
// OpenAPI 3.1 document generation from a SchemaContext (Kozou v0.2 spec
|
|
2
|
+
// §3.2). The differentiator over a generic auto-API is that COMMENT-derived
|
|
3
|
+
// metadata is baked into the document: table/view/column descriptions, the
|
|
4
|
+
// `@ai:` notes (as `x-kozou-ai`), `@policy:` rules (as `x-kozou-policy`),
|
|
5
|
+
// CHECK / ENUM members (as `enum`), and the resolved widget (as
|
|
6
|
+
// `x-kozou-widget`).
|
|
7
|
+
//
|
|
8
|
+
// `@policy:` rules are advisory metadata for AI agents and clients; they are
|
|
9
|
+
// not enforced here. Hard access control is the schema author's Postgres
|
|
10
|
+
// row-level security (see auth.ts).
|
|
11
|
+
//
|
|
12
|
+
// Pure function — no I/O. The server builds the document once at start and
|
|
13
|
+
// serves it at `GET /openapi.json`.
|
|
14
|
+
import { RESERVED_PARAMS } from './handler.js';
|
|
15
|
+
export function buildOpenApiDocument(schema, opts = {}) {
|
|
16
|
+
const resources = [
|
|
17
|
+
...schema.tables.map((t) => ({ resource: t, writable: true })),
|
|
18
|
+
...schema.views.map((v) => ({ resource: v, writable: false })),
|
|
19
|
+
];
|
|
20
|
+
// Use the bare name as the URL segment when it is unique across schemas,
|
|
21
|
+
// otherwise the qualified name — mirroring the request resolver.
|
|
22
|
+
const nameCounts = new Map();
|
|
23
|
+
for (const { resource } of resources) {
|
|
24
|
+
nameCounts.set(resource.name, (nameCounts.get(resource.name) ?? 0) + 1);
|
|
25
|
+
}
|
|
26
|
+
const segmentFor = (r) => nameCounts.get(r.name) === 1 ? r.name : r.qualifiedName;
|
|
27
|
+
const knownQNames = new Set(resources.map(({ resource }) => resource.qualifiedName));
|
|
28
|
+
// Reverse index for one-to-many embed hints: parent qualifiedName -> children.
|
|
29
|
+
const reverseByParent = new Map();
|
|
30
|
+
for (const t of schema.tables) {
|
|
31
|
+
for (const rel of t.relations ?? []) {
|
|
32
|
+
const parentQN = `${rel.references.schema}.${rel.references.table}`;
|
|
33
|
+
const entry = { childName: t.name, childQN: t.qualifiedName, field: rel.field };
|
|
34
|
+
const list = reverseByParent.get(parentQN);
|
|
35
|
+
if (list)
|
|
36
|
+
list.push(entry);
|
|
37
|
+
else
|
|
38
|
+
reverseByParent.set(parentQN, [entry]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
const paths = {};
|
|
42
|
+
const schemas = {};
|
|
43
|
+
for (const { resource, writable } of resources) {
|
|
44
|
+
const ref = `#/components/schemas/${resource.qualifiedName}`;
|
|
45
|
+
const relations = 'relations' in resource ? resource.relations : [];
|
|
46
|
+
const reverse = reverseByParent.get(resource.qualifiedName) ?? [];
|
|
47
|
+
const embeds = embedHints(relations, reverse, knownQNames);
|
|
48
|
+
schemas[resource.qualifiedName] = resourceSchema(resource, embeds);
|
|
49
|
+
Object.assign(paths, resourcePaths(resource, segmentFor(resource), ref, writable, embeds.length > 0));
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
openapi: '3.1.0',
|
|
53
|
+
info: {
|
|
54
|
+
title: opts.title ?? 'Kozou API',
|
|
55
|
+
version: opts.version ?? '0.0.0',
|
|
56
|
+
description: 'REST API generated by Kozou from PostgreSQL DDL + COMMENT. Descriptions, enums, and AI notes below are sourced from the database COMMENTs.',
|
|
57
|
+
},
|
|
58
|
+
paths,
|
|
59
|
+
components: { schemas },
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function resourceSchema(resource, embeds) {
|
|
63
|
+
const properties = {};
|
|
64
|
+
const required = [];
|
|
65
|
+
for (const column of resource.columns) {
|
|
66
|
+
properties[column.name] = columnSchema(column);
|
|
67
|
+
if (!column.nullable)
|
|
68
|
+
required.push(column.name);
|
|
69
|
+
}
|
|
70
|
+
const schema = { type: 'object', properties };
|
|
71
|
+
if (resource.description)
|
|
72
|
+
schema.description = resource.description;
|
|
73
|
+
if (resource.aiDescription)
|
|
74
|
+
schema['x-kozou-ai'] = resource.aiDescription;
|
|
75
|
+
if (resource.policy && resource.policy.length > 0)
|
|
76
|
+
schema['x-kozou-policy'] = resource.policy;
|
|
77
|
+
if (required.length > 0)
|
|
78
|
+
schema.required = required;
|
|
79
|
+
// Embeddable relations (forward to-one + reverse to-many, only targets that
|
|
80
|
+
// are themselves exposed resources), as a hint for clients building
|
|
81
|
+
// `?embed=`. Computed once by the caller and shared with the path builder so
|
|
82
|
+
// the `embed` query parameter is only advertised where it is usable.
|
|
83
|
+
if (embeds.length > 0)
|
|
84
|
+
schema['x-kozou-embeds'] = embeds;
|
|
85
|
+
return schema;
|
|
86
|
+
}
|
|
87
|
+
function embedHints(forward, reverse, knownQNames) {
|
|
88
|
+
const hints = [];
|
|
89
|
+
for (const r of forward) {
|
|
90
|
+
const qn = `${r.references.schema}.${r.references.table}`;
|
|
91
|
+
if (!knownQNames.has(qn))
|
|
92
|
+
continue;
|
|
93
|
+
hints.push({
|
|
94
|
+
field: r.field,
|
|
95
|
+
key: r.references.table,
|
|
96
|
+
target: `#/components/schemas/${qn}`,
|
|
97
|
+
cardinality: 'to-one',
|
|
98
|
+
});
|
|
99
|
+
}
|
|
100
|
+
for (const e of reverse) {
|
|
101
|
+
if (!knownQNames.has(e.childQN))
|
|
102
|
+
continue;
|
|
103
|
+
hints.push({
|
|
104
|
+
field: e.field,
|
|
105
|
+
key: e.childName,
|
|
106
|
+
target: `#/components/schemas/${e.childQN}`,
|
|
107
|
+
cardinality: 'to-many',
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
return hints;
|
|
111
|
+
}
|
|
112
|
+
function columnSchema(column) {
|
|
113
|
+
const base = widgetType(column.widget);
|
|
114
|
+
const schema = { ...base };
|
|
115
|
+
if (column.nullable && typeof schema.type === 'string') {
|
|
116
|
+
schema.type = [schema.type, 'null'];
|
|
117
|
+
}
|
|
118
|
+
if (column.enumValues && column.enumValues.length > 0) {
|
|
119
|
+
schema.enum = column.nullable ? [...column.enumValues, null] : [...column.enumValues];
|
|
120
|
+
}
|
|
121
|
+
if (column.description)
|
|
122
|
+
schema.description = column.description;
|
|
123
|
+
if (column.aiDescription)
|
|
124
|
+
schema['x-kozou-ai'] = column.aiDescription;
|
|
125
|
+
if (column.policy && column.policy.length > 0)
|
|
126
|
+
schema['x-kozou-policy'] = column.policy;
|
|
127
|
+
schema['x-kozou-widget'] = column.widget;
|
|
128
|
+
return schema;
|
|
129
|
+
}
|
|
130
|
+
function widgetType(widget) {
|
|
131
|
+
switch (widget) {
|
|
132
|
+
case 'number':
|
|
133
|
+
case 'currency':
|
|
134
|
+
return { type: 'number' };
|
|
135
|
+
case 'boolean':
|
|
136
|
+
return { type: 'boolean' };
|
|
137
|
+
case 'date':
|
|
138
|
+
return { type: 'string', format: 'date' };
|
|
139
|
+
case 'datetime':
|
|
140
|
+
return { type: 'string', format: 'date-time' };
|
|
141
|
+
case 'uuid':
|
|
142
|
+
return { type: 'string', format: 'uuid' };
|
|
143
|
+
case 'image-url':
|
|
144
|
+
return { type: 'string', format: 'uri' };
|
|
145
|
+
case 'json':
|
|
146
|
+
return { type: 'object' };
|
|
147
|
+
case 'text':
|
|
148
|
+
case 'textarea':
|
|
149
|
+
case 'enum-select':
|
|
150
|
+
case 'relation-select':
|
|
151
|
+
default:
|
|
152
|
+
return { type: 'string' };
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
function resourcePaths(resource, segment, ref, writable, hasEmbeds) {
|
|
156
|
+
const rowRef = { $ref: ref };
|
|
157
|
+
const label = resource.label || resource.name;
|
|
158
|
+
// The `embed` parameter is only advertised where the resource has at least
|
|
159
|
+
// one embeddable relation; a non-empty `embed` on a relation-less resource
|
|
160
|
+
// (e.g. a view) is rejected with 400, so listing it would be misleading.
|
|
161
|
+
const embedParams = hasEmbeds ? [embedParam()] : [];
|
|
162
|
+
const collection = {
|
|
163
|
+
get: {
|
|
164
|
+
summary: `List ${label}`,
|
|
165
|
+
description: resource.description ?? undefined,
|
|
166
|
+
parameters: [...listParameters(), ...filterParameters(resource), ...embedParams],
|
|
167
|
+
responses: {
|
|
168
|
+
'200': jsonResponse(`A page of ${label}.`, listResultSchema(rowRef)),
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
if (writable) {
|
|
173
|
+
collection.post = {
|
|
174
|
+
summary: `Create a ${label} row`,
|
|
175
|
+
requestBody: jsonBody(rowRef),
|
|
176
|
+
responses: {
|
|
177
|
+
'201': jsonResponse('The created row.', rowRef),
|
|
178
|
+
'400': errorResponse('Validation error.'),
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
const paths = { [`/${segment}`]: collection };
|
|
183
|
+
// Item-by-id routes only exist for resources addressable by a primary key.
|
|
184
|
+
// Views and primary-key-less tables cannot be fetched/updated/deleted by id
|
|
185
|
+
// (the request handler rejects such reads with 400), so the document must
|
|
186
|
+
// not advertise an item path for them.
|
|
187
|
+
const primaryKey = 'primaryKey' in resource ? resource.primaryKey : [];
|
|
188
|
+
if (primaryKey.length >= 1) {
|
|
189
|
+
const idParam = itemIdParam(resource);
|
|
190
|
+
const item = {
|
|
191
|
+
get: {
|
|
192
|
+
summary: `Fetch a ${label} row by id`,
|
|
193
|
+
parameters: [idParam, ...embedParams],
|
|
194
|
+
responses: {
|
|
195
|
+
'200': jsonResponse('The row.', rowRef),
|
|
196
|
+
'404': errorResponse('No such row.'),
|
|
197
|
+
},
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
if (writable) {
|
|
201
|
+
item.patch = {
|
|
202
|
+
summary: `Update a ${label} row`,
|
|
203
|
+
parameters: [idParam],
|
|
204
|
+
requestBody: jsonBody(rowRef),
|
|
205
|
+
responses: {
|
|
206
|
+
'200': jsonResponse('The updated row.', rowRef),
|
|
207
|
+
'404': errorResponse('No such row.'),
|
|
208
|
+
},
|
|
209
|
+
};
|
|
210
|
+
item.delete = {
|
|
211
|
+
summary: `Delete a ${label} row`,
|
|
212
|
+
parameters: [idParam],
|
|
213
|
+
responses: {
|
|
214
|
+
'200': jsonResponse('The deleted row.', rowRef),
|
|
215
|
+
'404': errorResponse('No such row.'),
|
|
216
|
+
},
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
paths[`/${segment}/{id}`] = item;
|
|
220
|
+
}
|
|
221
|
+
return paths;
|
|
222
|
+
}
|
|
223
|
+
/** The `{id}` path parameter. Only called for a resource with a non-empty
|
|
224
|
+
* primary key (the caller in `resourcePaths` guards on it). A composite
|
|
225
|
+
* primary key is addressed by comma-joining its components — in `primaryKey`
|
|
226
|
+
* declaration order — into the single `{id}` segment, each component
|
|
227
|
+
* percent-encoded. */
|
|
228
|
+
function itemIdParam(resource) {
|
|
229
|
+
const pk = 'primaryKey' in resource ? resource.primaryKey : [];
|
|
230
|
+
let description;
|
|
231
|
+
if (pk.length > 1) {
|
|
232
|
+
const cols = pk.map((c) => `\`${c}\``).join(', ');
|
|
233
|
+
description =
|
|
234
|
+
`Composite primary key: the components (${cols}) in this order, joined by an ` +
|
|
235
|
+
`unescaped comma in the single path segment. Percent-encode reserved characters ` +
|
|
236
|
+
`within a component; a component value cannot itself contain a comma (the segment ` +
|
|
237
|
+
`is split on commas after URL-decoding).`;
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
description = `Primary key of the row (\`${pk[0]}\`).`;
|
|
241
|
+
}
|
|
242
|
+
return { name: 'id', in: 'path', required: true, schema: { type: 'string' }, description };
|
|
243
|
+
}
|
|
244
|
+
function listParameters() {
|
|
245
|
+
return [
|
|
246
|
+
queryParam('page', { type: 'integer', minimum: 1 }, '1-based page index.'),
|
|
247
|
+
queryParam('pageSize', { type: 'integer', minimum: 1 }, 'Rows per page.'),
|
|
248
|
+
queryParam('sort', { type: 'string' }, 'Comma-separated `field.asc` / `field.desc`.'),
|
|
249
|
+
queryParam('search', { type: 'string' }, 'Free-text ILIKE across text columns.'),
|
|
250
|
+
];
|
|
251
|
+
}
|
|
252
|
+
/** Operators accepted by the `<column>=<op>.<value>` filter grammar. */
|
|
253
|
+
const FILTER_OPERATORS_DOC = 'eq, neq, gt, gte, lt, lte, like, ilike, in, is';
|
|
254
|
+
/** One query parameter per filterable column documenting the
|
|
255
|
+
* `<column>=<op>.<value>` grammar. Control keys (page/sort/…) are skipped so
|
|
256
|
+
* the doc never advertises them as filters. Multiple filters on one column
|
|
257
|
+
* combine with AND (e.g. a `gte`/`lte` range). */
|
|
258
|
+
function filterParameters(resource) {
|
|
259
|
+
return resource.columns
|
|
260
|
+
.filter((c) => !RESERVED_PARAMS.has(c.name))
|
|
261
|
+
.map((c) => queryParam(c.name, { type: 'string' }, `Filter on \`${c.name}\`: \`<op>.<value>\` where op is one of ${FILTER_OPERATORS_DOC} ` +
|
|
262
|
+
`(e.g. \`${c.name}=gte.10\`, \`${c.name}=in.(a,b)\`, \`${c.name}=is.null\`). ` +
|
|
263
|
+
`A bare value means \`eq\`. Repeat the key to AND several conditions.`));
|
|
264
|
+
}
|
|
265
|
+
function queryParam(name, schema, description) {
|
|
266
|
+
return { name, in: 'query', required: false, schema, description };
|
|
267
|
+
}
|
|
268
|
+
function embedParam() {
|
|
269
|
+
return queryParam('embed', { type: 'string' }, 'Comma-separated forward to-one relations to inline as nested objects; dot for depth (e.g. "author,editions.books.authors"). Each segment is a foreign-key column name or its referenced table name.');
|
|
270
|
+
}
|
|
271
|
+
function listResultSchema(rowRef) {
|
|
272
|
+
return {
|
|
273
|
+
type: 'object',
|
|
274
|
+
properties: {
|
|
275
|
+
rows: { type: 'array', items: rowRef },
|
|
276
|
+
total: { type: 'integer' },
|
|
277
|
+
page: { type: 'integer' },
|
|
278
|
+
pageSize: { type: 'integer' },
|
|
279
|
+
},
|
|
280
|
+
required: ['rows', 'total', 'page', 'pageSize'],
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
function jsonResponse(description, schema) {
|
|
284
|
+
return { description, content: { 'application/json': { schema } } };
|
|
285
|
+
}
|
|
286
|
+
function jsonBody(schema) {
|
|
287
|
+
return { required: true, content: { 'application/json': { schema } } };
|
|
288
|
+
}
|
|
289
|
+
function errorResponse(description) {
|
|
290
|
+
return jsonResponse(description, {
|
|
291
|
+
type: 'object',
|
|
292
|
+
properties: {
|
|
293
|
+
error: {
|
|
294
|
+
type: 'object',
|
|
295
|
+
properties: { code: { type: 'string' }, message: { type: 'string' } },
|
|
296
|
+
},
|
|
297
|
+
},
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=openapi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openapi.js","sourceRoot":"","sources":["../src/openapi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { quoteIdent } from './ident.js';
|
|
2
|
+
import { type EmbedNode } from './embed.js';
|
|
3
|
+
import type { Resource } from './schema-lookup.js';
|
|
4
|
+
export { quoteIdent };
|
|
5
|
+
export declare const DEFAULT_PAGE_SIZE = 50;
|
|
6
|
+
export declare const MAX_PAGE_SIZE = 200;
|
|
7
|
+
export type SortDirection = 'asc' | 'desc';
|
|
8
|
+
/** Horizontal filter operators for the `?<col>=<op>.<value>` query grammar. */
|
|
9
|
+
export type FilterOperator = 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'ilike' | 'in' | 'is';
|
|
10
|
+
/** Comparison operators that bind a single value. */
|
|
11
|
+
export type ScalarFilterOperator = Exclude<FilterOperator, 'in' | 'is'>;
|
|
12
|
+
/** Allowed right-hand keywords for the `is` operator (never bound — a fixed
|
|
13
|
+
* SQL `IS [NOT] NULL/TRUE/FALSE` clause). */
|
|
14
|
+
export type IsKeyword = 'null' | 'notnull' | 'true' | 'false';
|
|
15
|
+
/** A single horizontal filter. `column` must be a declared column of the
|
|
16
|
+
* resource (enforced in {@link buildListQuery}). Multiple filters on the same
|
|
17
|
+
* column are allowed and combine with AND (e.g. a `gte`/`lte` range). */
|
|
18
|
+
export type Filter = {
|
|
19
|
+
column: string;
|
|
20
|
+
op: ScalarFilterOperator;
|
|
21
|
+
value: string;
|
|
22
|
+
} | {
|
|
23
|
+
column: string;
|
|
24
|
+
op: 'in';
|
|
25
|
+
values: string[];
|
|
26
|
+
} | {
|
|
27
|
+
column: string;
|
|
28
|
+
op: 'is';
|
|
29
|
+
keyword: IsKeyword;
|
|
30
|
+
};
|
|
31
|
+
export type ListQueryParams = {
|
|
32
|
+
page?: number;
|
|
33
|
+
pageSize?: number;
|
|
34
|
+
sort?: {
|
|
35
|
+
field: string;
|
|
36
|
+
order: SortDirection;
|
|
37
|
+
}[];
|
|
38
|
+
search?: string;
|
|
39
|
+
/** Horizontal filters. Each filter's column must be a declared column of
|
|
40
|
+
* the resource; every supplied value is a bound parameter. */
|
|
41
|
+
filters?: Filter[];
|
|
42
|
+
/** Resolved forward to-one relations to inline as nested JSON objects. */
|
|
43
|
+
embed?: EmbedNode[];
|
|
44
|
+
};
|
|
45
|
+
export type BuiltListQuery = {
|
|
46
|
+
dataText: string;
|
|
47
|
+
dataValues: unknown[];
|
|
48
|
+
countText: string;
|
|
49
|
+
countValues: unknown[];
|
|
50
|
+
/** Effective (clamped) pagination echoed back in the response. */
|
|
51
|
+
page: number;
|
|
52
|
+
pageSize: number;
|
|
53
|
+
};
|
|
54
|
+
export type BuiltGetQuery = {
|
|
55
|
+
text: string;
|
|
56
|
+
values: unknown[];
|
|
57
|
+
};
|
|
58
|
+
export declare function buildListQuery(resource: Resource, params: ListQueryParams): BuiltListQuery;
|
|
59
|
+
export declare function buildGetQuery(resource: Resource, id: string, embed?: EmbedNode[]): BuiltGetQuery;
|
|
60
|
+
export type BuiltMutation = {
|
|
61
|
+
text: string;
|
|
62
|
+
values: unknown[];
|
|
63
|
+
};
|
|
64
|
+
export declare const DEFAULT_RELATION_LIMIT = 20;
|
|
65
|
+
export declare const MAX_RELATION_LIMIT = 100;
|
|
66
|
+
export type RelationOptionsParams = {
|
|
67
|
+
labelField: string;
|
|
68
|
+
searchFields: string[];
|
|
69
|
+
query?: string;
|
|
70
|
+
limit?: number;
|
|
71
|
+
};
|
|
72
|
+
export type BuiltRelationOptions = {
|
|
73
|
+
text: string;
|
|
74
|
+
values: unknown[];
|
|
75
|
+
primaryKey: string;
|
|
76
|
+
labelField: string;
|
|
77
|
+
};
|
|
78
|
+
export declare function buildInsertQuery(resource: Resource, data: Record<string, unknown>): BuiltMutation;
|
|
79
|
+
export declare function buildUpdateQuery(resource: Resource, id: string, data: Record<string, unknown>): BuiltMutation;
|
|
80
|
+
export declare function buildDeleteQuery(resource: Resource, id: string): BuiltMutation;
|
|
81
|
+
/** Lightweight `{ id, label }` lookup used by relation-select widgets. */
|
|
82
|
+
export declare function buildRelationOptionsQuery(resource: Resource, params: RelationOptionsParams): BuiltRelationOptions;
|
|
83
|
+
//# sourceMappingURL=query-builder.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query-builder.d.ts","sourceRoot":"","sources":["../src/query-builder.ts"],"names":[],"mappings":"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"}
|