@zeyos/client 0.1.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 +31 -0
- package/LICENSE +21 -0
- package/README.md +458 -0
- package/agents/README.md +66 -0
- package/agents/shared/business-app-benchmarks.md +111 -0
- package/agents/shared/zeyos-entity-map.md +142 -0
- package/agents/shared/zeyos-entity-reference.md +570 -0
- package/agents/shared/zeyos-query-patterns.md +89 -0
- package/agents/zeyos-account-intelligence/SKILL.md +34 -0
- package/agents/zeyos-account-intelligence/agents/openai.yaml +4 -0
- package/agents/zeyos-account-intelligence/references/workflows.md +84 -0
- package/agents/zeyos-billing-insights/SKILL.md +41 -0
- package/agents/zeyos-billing-insights/agents/openai.yaml +4 -0
- package/agents/zeyos-billing-insights/references/workflows.md +106 -0
- package/agents/zeyos-campaign-and-outreach/SKILL.md +44 -0
- package/agents/zeyos-campaign-and-outreach/agents/openai.yaml +4 -0
- package/agents/zeyos-campaign-and-outreach/references/workflows.md +100 -0
- package/agents/zeyos-collaboration-and-activity/SKILL.md +37 -0
- package/agents/zeyos-collaboration-and-activity/agents/openai.yaml +4 -0
- package/agents/zeyos-collaboration-and-activity/references/workflows.md +104 -0
- package/agents/zeyos-collections-and-dunning/SKILL.md +46 -0
- package/agents/zeyos-collections-and-dunning/agents/openai.yaml +4 -0
- package/agents/zeyos-collections-and-dunning/references/workflows.md +132 -0
- package/agents/zeyos-commerce-and-inventory/SKILL.md +38 -0
- package/agents/zeyos-commerce-and-inventory/agents/openai.yaml +4 -0
- package/agents/zeyos-commerce-and-inventory/references/workflows.md +101 -0
- package/agents/zeyos-mail-operations/SKILL.md +35 -0
- package/agents/zeyos-mail-operations/agents/openai.yaml +4 -0
- package/agents/zeyos-mail-operations/references/workflows.md +110 -0
- package/agents/zeyos-notes-and-sops/SKILL.md +31 -0
- package/agents/zeyos-notes-and-sops/agents/openai.yaml +4 -0
- package/agents/zeyos-notes-and-sops/references/workflows.md +85 -0
- package/agents/zeyos-platform-and-schema/SKILL.md +37 -0
- package/agents/zeyos-platform-and-schema/agents/openai.yaml +4 -0
- package/agents/zeyos-platform-and-schema/references/workflows.md +97 -0
- package/agents/zeyos-work-management/SKILL.md +45 -0
- package/agents/zeyos-work-management/agents/openai.yaml +4 -0
- package/agents/zeyos-work-management/references/workflows.md +148 -0
- package/docs/01-api-reference/01-data-retrieval.md +601 -0
- package/docs/01-api-reference/02-authentication.md +288 -0
- package/docs/01-api-reference/03-resources.md +270 -0
- package/docs/01-api-reference/04-schema.md +539 -0
- package/docs/01-api-reference/_category_.json +9 -0
- package/docs/02-javascript-client/01-getting-started.md +146 -0
- package/docs/02-javascript-client/02-authentication.md +287 -0
- package/docs/02-javascript-client/03-making-requests.md +572 -0
- package/docs/02-javascript-client/04-practical-guide.md +348 -0
- package/docs/02-javascript-client/_category_.json +9 -0
- package/docs/03-cli/01-getting-started.md +219 -0
- package/docs/03-cli/02-commands.md +407 -0
- package/docs/03-cli/03-configuration.md +220 -0
- package/docs/03-cli/_category_.json +9 -0
- package/docs/04-agent-workflows/00-coding-agents.md +35 -0
- package/docs/04-agent-workflows/01-agent-quickstart.md +147 -0
- package/docs/04-agent-workflows/02-agent-recipes.md +109 -0
- package/docs/04-agent-workflows/03-cli-coverage-and-escalation.md +65 -0
- package/docs/04-agent-workflows/_category_.json +9 -0
- package/docs/04-sample-apps/01-kanban.md +89 -0
- package/docs/04-sample-apps/02-crm.md +81 -0
- package/docs/04-sample-apps/03-dashboard.md +80 -0
- package/docs/04-sample-apps/_category_.json +9 -0
- package/docs/05-tutorials/00-application-developers.md +43 -0
- package/docs/05-tutorials/01-integration-architecture.md +60 -0
- package/docs/05-tutorials/02-build-your-own-zeyos-frontend.md +517 -0
- package/docs/05-tutorials/03-server-side-integrations.md +185 -0
- package/docs/05-tutorials/_category_.json +9 -0
- package/docs/intro.md +197 -0
- package/openapi/api.json +24308 -0
- package/openapi/auth.json +415 -0
- package/openapi/dbref.json +56223 -0
- package/openapi/oauth2.json +781 -0
- package/openapi/sdk.json +949 -0
- package/openapi/views.txt +642 -0
- package/package.json +49 -0
- package/samples/crm/README.md +28 -0
- package/samples/crm/index.html +327 -0
- package/samples/crm/js/api.js +208 -0
- package/samples/crm/js/auth.js +61 -0
- package/samples/crm/js/main.js +545 -0
- package/samples/crm/js/state.js +90 -0
- package/samples/crm/js/ui.js +51 -0
- package/samples/dashboard/README.md +28 -0
- package/samples/dashboard/index.html +280 -0
- package/samples/dashboard/js/api.js +197 -0
- package/samples/dashboard/js/auth.js +59 -0
- package/samples/dashboard/js/main.js +382 -0
- package/samples/dashboard/js/state.js +81 -0
- package/samples/dashboard/js/ui.js +48 -0
- package/samples/kanban/README.md +28 -0
- package/samples/kanban/index.html +263 -0
- package/samples/kanban/js/api.js +152 -0
- package/samples/kanban/js/auth.js +59 -0
- package/samples/kanban/js/constants.js +40 -0
- package/samples/kanban/js/kanban.js +246 -0
- package/samples/kanban/js/main.js +362 -0
- package/samples/kanban/js/modals.js +474 -0
- package/samples/kanban/js/settings.js +82 -0
- package/samples/kanban/js/state.js +118 -0
- package/samples/kanban/js/ui.js +49 -0
- package/scripts/generate-client.mjs +344 -0
- package/src/generated/operations.js +9772 -0
- package/src/generated/schema.js +8982 -0
- package/src/index.js +85 -0
- package/src/runtime/client.js +1208 -0
- package/src/runtime/error.js +29 -0
- package/src/runtime/http.js +174 -0
- package/src/runtime/request-shape.js +35 -0
- package/src/runtime/schema.js +206 -0
- package/src/runtime/suggest.js +74 -0
- package/src/runtime/token-store.js +105 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared UI utilities: toast notifications + loading overlay.
|
|
3
|
+
* Extracted into its own module to avoid circular imports between main.js and modals.js.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// ── Toast Notifications ─────────────────────────────────────────────────────
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @param {string} message
|
|
10
|
+
* @param {'success'|'error'|'info'} type
|
|
11
|
+
*/
|
|
12
|
+
export function showToast(message, type = 'info') {
|
|
13
|
+
const container = document.getElementById('toast-container');
|
|
14
|
+
if (!container) return;
|
|
15
|
+
|
|
16
|
+
const colors = {
|
|
17
|
+
success: 'bg-emerald-600',
|
|
18
|
+
error: 'bg-red-600',
|
|
19
|
+
info: 'bg-slate-700',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const toast = document.createElement('div');
|
|
23
|
+
toast.className =
|
|
24
|
+
`pointer-events-auto flex items-center gap-3 px-4 py-3 rounded-xl text-white text-sm shadow-xl ` +
|
|
25
|
+
`${colors[type] ?? colors.info} translate-y-2 opacity-0 transition-all duration-200`;
|
|
26
|
+
toast.textContent = message;
|
|
27
|
+
|
|
28
|
+
container.appendChild(toast);
|
|
29
|
+
// Trigger transition on next frame
|
|
30
|
+
requestAnimationFrame(() => {
|
|
31
|
+
requestAnimationFrame(() => toast.classList.remove('translate-y-2', 'opacity-0'));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const duration = type === 'error' ? 5000 : 3000;
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
toast.classList.add('opacity-0', 'translate-y-2');
|
|
37
|
+
toast.addEventListener('transitionend', () => toast.remove(), { once: true });
|
|
38
|
+
}, duration);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// ── Loading Overlay ─────────────────────────────────────────────────────────
|
|
42
|
+
|
|
43
|
+
export function showLoading() {
|
|
44
|
+
document.getElementById('loading-overlay')?.classList.remove('hidden');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function hideLoading() {
|
|
48
|
+
document.getElementById('loading-overlay')?.classList.add('hidden');
|
|
49
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
const HTTP_METHODS = new Set(['get', 'put', 'post', 'delete', 'patch', 'head', 'options', 'trace']);
|
|
6
|
+
|
|
7
|
+
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
8
|
+
const SOURCES = [
|
|
9
|
+
{ service: 'api', file: 'openapi/api.json' },
|
|
10
|
+
{ service: 'oauth2', file: 'openapi/oauth2.json' },
|
|
11
|
+
{ service: 'legacyAuth', file: 'openapi/auth.json' }
|
|
12
|
+
];
|
|
13
|
+
|
|
14
|
+
function unescapePointerToken(token) {
|
|
15
|
+
return token.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function pointerGet(doc, ref) {
|
|
19
|
+
if (!ref.startsWith('#/')) {
|
|
20
|
+
throw new Error(`External $ref is not supported: ${ref}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const parts = ref.slice(2).split('/').map(unescapePointerToken);
|
|
24
|
+
let current = doc;
|
|
25
|
+
for (const part of parts) {
|
|
26
|
+
if (current == null || !Object.prototype.hasOwnProperty.call(current, part)) {
|
|
27
|
+
throw new Error(`Unresolvable $ref: ${ref}`);
|
|
28
|
+
}
|
|
29
|
+
current = current[part];
|
|
30
|
+
}
|
|
31
|
+
return current;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function resolveRef(doc, value) {
|
|
35
|
+
if (!value || typeof value !== 'object') {
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
if (typeof value.$ref === 'string') {
|
|
39
|
+
return pointerGet(doc, value.$ref);
|
|
40
|
+
}
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function normalizeServer(server) {
|
|
45
|
+
const urlTemplate = server?.url || '';
|
|
46
|
+
const defaultVariables = {};
|
|
47
|
+
|
|
48
|
+
if (server?.variables && typeof server.variables === 'object') {
|
|
49
|
+
for (const [name, variable] of Object.entries(server.variables)) {
|
|
50
|
+
if (variable && typeof variable.default === 'string') {
|
|
51
|
+
defaultVariables[name] = variable.default;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const basePathTemplate = (() => {
|
|
57
|
+
const match = urlTemplate.match(/^https?:\/\/[^/]+(\/.*)$/i);
|
|
58
|
+
return match ? match[1] : '';
|
|
59
|
+
})();
|
|
60
|
+
|
|
61
|
+
return {
|
|
62
|
+
urlTemplate,
|
|
63
|
+
basePathTemplate,
|
|
64
|
+
defaultVariables
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function normalizeParameters(doc, pathItem, operation) {
|
|
69
|
+
const combined = [
|
|
70
|
+
...(Array.isArray(pathItem?.parameters) ? pathItem.parameters : []),
|
|
71
|
+
...(Array.isArray(operation?.parameters) ? operation.parameters : [])
|
|
72
|
+
];
|
|
73
|
+
|
|
74
|
+
const indexed = new Map();
|
|
75
|
+
for (const rawParameter of combined) {
|
|
76
|
+
const parameter = resolveRef(doc, rawParameter);
|
|
77
|
+
if (!parameter || typeof parameter !== 'object') {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const key = `${parameter.in || 'unknown'}:${parameter.name || 'unknown'}`;
|
|
82
|
+
indexed.set(key, {
|
|
83
|
+
name: parameter.name,
|
|
84
|
+
in: parameter.in,
|
|
85
|
+
required: Boolean(parameter.required)
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const normalized = Array.from(indexed.values()).filter((parameter) => parameter.name && parameter.in);
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
all: normalized,
|
|
93
|
+
path: normalized.filter((parameter) => parameter.in === 'path').map((parameter) => parameter.name),
|
|
94
|
+
query: normalized.filter((parameter) => parameter.in === 'query').map((parameter) => parameter.name),
|
|
95
|
+
header: normalized.filter((parameter) => parameter.in === 'header').map((parameter) => parameter.name)
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function normalizeRequestBody(doc, operation) {
|
|
100
|
+
const requestBody = resolveRef(doc, operation?.requestBody);
|
|
101
|
+
if (!requestBody || typeof requestBody !== 'object') {
|
|
102
|
+
return {
|
|
103
|
+
required: false,
|
|
104
|
+
contentTypes: []
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const content = requestBody.content && typeof requestBody.content === 'object' ? requestBody.content : {};
|
|
109
|
+
const contentTypes = Object.keys(content);
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
required: Boolean(requestBody.required),
|
|
113
|
+
contentTypes
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function normalizeOperationSecurity(doc, operation) {
|
|
118
|
+
if (Array.isArray(operation?.security)) {
|
|
119
|
+
return operation.security;
|
|
120
|
+
}
|
|
121
|
+
if (Array.isArray(doc.security)) {
|
|
122
|
+
return doc.security;
|
|
123
|
+
}
|
|
124
|
+
return [];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function buildOperationId({ operationId, method, route, existingIds }) {
|
|
128
|
+
if (operationId && !existingIds.has(operationId)) {
|
|
129
|
+
return operationId;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const routeToken = route
|
|
133
|
+
.replace(/[^a-zA-Z0-9{}]/g, '_')
|
|
134
|
+
.replace(/[{}]/g, '')
|
|
135
|
+
.replace(/_+/g, '_')
|
|
136
|
+
.replace(/^_|_$/g, '');
|
|
137
|
+
|
|
138
|
+
let candidate = operationId || `${method.toLowerCase()}_${routeToken || 'operation'}`;
|
|
139
|
+
let suffix = 1;
|
|
140
|
+
|
|
141
|
+
while (existingIds.has(candidate)) {
|
|
142
|
+
suffix += 1;
|
|
143
|
+
candidate = `${operationId || `${method.toLowerCase()}_${routeToken || 'operation'}`}_${suffix}`;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return candidate;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function collectOperations(doc) {
|
|
150
|
+
const operations = [];
|
|
151
|
+
const existingIds = new Set();
|
|
152
|
+
|
|
153
|
+
for (const [route, pathItem] of Object.entries(doc.paths || {})) {
|
|
154
|
+
for (const [method, operation] of Object.entries(pathItem || {})) {
|
|
155
|
+
if (!HTTP_METHODS.has(method)) {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const normalizedMethod = method.toUpperCase();
|
|
160
|
+
const parameters = normalizeParameters(doc, pathItem, operation);
|
|
161
|
+
const requestBody = normalizeRequestBody(doc, operation);
|
|
162
|
+
const security = normalizeOperationSecurity(doc, operation);
|
|
163
|
+
const finalOperationId = buildOperationId({
|
|
164
|
+
operationId: operation.operationId,
|
|
165
|
+
method: normalizedMethod,
|
|
166
|
+
route,
|
|
167
|
+
existingIds
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
existingIds.add(finalOperationId);
|
|
171
|
+
|
|
172
|
+
operations.push({
|
|
173
|
+
operationId: finalOperationId,
|
|
174
|
+
summary: operation.summary || '',
|
|
175
|
+
deprecated: Boolean(operation.deprecated),
|
|
176
|
+
method: normalizedMethod,
|
|
177
|
+
path: route,
|
|
178
|
+
security,
|
|
179
|
+
requestBodyRequired: requestBody.required,
|
|
180
|
+
requestContentTypes: requestBody.contentTypes,
|
|
181
|
+
parameterNames: {
|
|
182
|
+
path: parameters.path,
|
|
183
|
+
query: parameters.query,
|
|
184
|
+
header: parameters.header
|
|
185
|
+
}
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
operations.sort((a, b) => {
|
|
191
|
+
if (a.path !== b.path) {
|
|
192
|
+
return a.path.localeCompare(b.path);
|
|
193
|
+
}
|
|
194
|
+
return a.method.localeCompare(b.method);
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
return operations;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function readSpecFile(relativePath) {
|
|
201
|
+
const absolutePath = path.join(ROOT, relativePath);
|
|
202
|
+
const raw = await readFile(absolutePath, 'utf8');
|
|
203
|
+
return JSON.parse(raw);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
async function readOptionalSpecFile(relativePath) {
|
|
207
|
+
try {
|
|
208
|
+
return await readSpecFile(relativePath);
|
|
209
|
+
} catch (error) {
|
|
210
|
+
if (error?.code === 'ENOENT') {
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function buildServices(specEntries) {
|
|
218
|
+
const services = {};
|
|
219
|
+
|
|
220
|
+
for (const specEntry of specEntries) {
|
|
221
|
+
const { service, file, doc } = specEntry;
|
|
222
|
+
const firstServer = Array.isArray(doc.servers) ? doc.servers[0] : undefined;
|
|
223
|
+
|
|
224
|
+
services[service] = {
|
|
225
|
+
key: service,
|
|
226
|
+
source: file,
|
|
227
|
+
title: doc.info?.title || '',
|
|
228
|
+
version: doc.info?.version || '',
|
|
229
|
+
server: normalizeServer(firstServer),
|
|
230
|
+
globalSecurity: Array.isArray(doc.security) ? doc.security : [],
|
|
231
|
+
operations: collectOperations(doc)
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return services;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function renderModule(generated) {
|
|
239
|
+
const payload = JSON.stringify(generated, null, 2);
|
|
240
|
+
|
|
241
|
+
return [
|
|
242
|
+
'// This file is auto-generated by scripts/generate-client.mjs',
|
|
243
|
+
'// Do not edit manually.',
|
|
244
|
+
'',
|
|
245
|
+
`export const GENERATED = ${payload};`,
|
|
246
|
+
'export const SERVICES = GENERATED.services;',
|
|
247
|
+
"export const SERVICE_KEYS = Object.freeze(Object.keys(SERVICES));",
|
|
248
|
+
''
|
|
249
|
+
].join('\n');
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Enum values are documented inline in dbref descriptions as `N`=LABEL pairs,
|
|
253
|
+
// e.g. "Status (`0`=NOTSTARTED, `1`=AWAITINGACCEPTANCE, ...)". Extract them so
|
|
254
|
+
// the client can validate enum inputs and suggest valid values.
|
|
255
|
+
function parseEnum(description) {
|
|
256
|
+
if (typeof description !== 'string') return null;
|
|
257
|
+
const out = {};
|
|
258
|
+
let count = 0;
|
|
259
|
+
const re = /`(-?\d+)`\s*=\s*([A-Za-z0-9_]+)/g;
|
|
260
|
+
let match;
|
|
261
|
+
while ((match = re.exec(description)) !== null) {
|
|
262
|
+
out[match[1]] = match[2];
|
|
263
|
+
count += 1;
|
|
264
|
+
}
|
|
265
|
+
return count >= 2 ? out : null;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// Compact field/enum/foreign-key map derived from openapi/dbref.json. Much
|
|
269
|
+
// smaller than the raw dbref (drops storage, collation, constraints, triggers,
|
|
270
|
+
// stats, etc.) so it ships cheaply and powers runtime introspection.
|
|
271
|
+
function buildSchema(dbref) {
|
|
272
|
+
const schema = {};
|
|
273
|
+
if (!Array.isArray(dbref)) return schema;
|
|
274
|
+
|
|
275
|
+
for (const entity of dbref) {
|
|
276
|
+
if (!entity || typeof entity !== 'object' || !entity.name || !Array.isArray(entity.fields)) {
|
|
277
|
+
continue;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const fields = {};
|
|
281
|
+
for (const field of entity.fields) {
|
|
282
|
+
if (!field || typeof field !== 'object' || !field.name) continue;
|
|
283
|
+
const def = { type: field.type || 'unknown' };
|
|
284
|
+
if (field.indexed) def.indexed = true;
|
|
285
|
+
if (Array.isArray(field.fkeys) && field.fkeys.length > 0 && field.fkeys[0].table) {
|
|
286
|
+
def.fk = field.fkeys[0].table;
|
|
287
|
+
}
|
|
288
|
+
const enumValues = parseEnum(field.description);
|
|
289
|
+
if (enumValues) def.enum = enumValues;
|
|
290
|
+
fields[field.name] = def;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
schema[entity.name] = { type: entity.type || 'table', fields };
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return schema;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function renderSchemaModule(schema) {
|
|
300
|
+
return [
|
|
301
|
+
'// This file is auto-generated by scripts/generate-client.mjs',
|
|
302
|
+
'// Do not edit manually. Source: openapi/dbref.json',
|
|
303
|
+
'',
|
|
304
|
+
`export const SCHEMA = ${JSON.stringify(schema, null, 2)};`,
|
|
305
|
+
'export const SCHEMA_RESOURCES = Object.freeze(Object.keys(SCHEMA));',
|
|
306
|
+
''
|
|
307
|
+
].join('\n');
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
async function main() {
|
|
311
|
+
const specEntries = [];
|
|
312
|
+
|
|
313
|
+
for (const source of SOURCES) {
|
|
314
|
+
const doc = await readSpecFile(source.file);
|
|
315
|
+
specEntries.push({ ...source, doc });
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const generated = {
|
|
319
|
+
// Deterministic by default (avoids per-build/test git churn). Publish
|
|
320
|
+
// pipelines can stamp a real timestamp by setting ZEYOS_GENERATED_AT.
|
|
321
|
+
generatedAt: process.env.ZEYOS_GENERATED_AT ?? null,
|
|
322
|
+
services: buildServices(specEntries)
|
|
323
|
+
};
|
|
324
|
+
|
|
325
|
+
const outputFile = path.join(ROOT, 'src/generated/operations.js');
|
|
326
|
+
await mkdir(path.dirname(outputFile), { recursive: true });
|
|
327
|
+
await writeFile(outputFile, renderModule(generated), 'utf8');
|
|
328
|
+
|
|
329
|
+
const dbref = await readOptionalSpecFile('openapi/dbref.json');
|
|
330
|
+
const schema = buildSchema(dbref);
|
|
331
|
+
await writeFile(path.join(ROOT, 'src/generated/schema.js'), renderSchemaModule(schema), 'utf8');
|
|
332
|
+
|
|
333
|
+
const operationCount = Object.values(generated.services)
|
|
334
|
+
.map((service) => service.operations.length)
|
|
335
|
+
.reduce((sum, value) => sum + value, 0);
|
|
336
|
+
|
|
337
|
+
process.stdout.write(`Generated operations for ${Object.keys(generated.services).length} services (${operationCount} operations).\n`);
|
|
338
|
+
process.stdout.write(`Generated schema for ${Object.keys(schema).length} resources.\n`);
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
main().catch((error) => {
|
|
342
|
+
console.error(error);
|
|
343
|
+
process.exit(1);
|
|
344
|
+
});
|