@carthooks/arcubase-cli 0.1.21 → 0.1.23

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.
Files changed (57) hide show
  1. package/bundle/arcubase-admin.mjs +1430 -83
  2. package/bundle/arcubase.mjs +1430 -83
  3. package/dist/generated/command_registry.generated.d.ts +300 -0
  4. package/dist/generated/command_registry.generated.d.ts.map +1 -1
  5. package/dist/generated/command_registry.generated.js +416 -0
  6. package/dist/generated/help_examples.generated.d.ts +116 -0
  7. package/dist/generated/help_examples.generated.d.ts.map +1 -1
  8. package/dist/generated/help_examples.generated.js +184 -0
  9. package/dist/generated/type_index.generated.d.ts +40 -1
  10. package/dist/generated/type_index.generated.d.ts.map +1 -1
  11. package/dist/generated/type_index.generated.js +42 -1
  12. package/dist/generated/zod_registry.generated.d.ts +29 -1
  13. package/dist/generated/zod_registry.generated.d.ts.map +1 -1
  14. package/dist/generated/zod_registry.generated.js +37 -0
  15. package/dist/runtime/dev_sdk_gen.d.ts +9 -0
  16. package/dist/runtime/dev_sdk_gen.d.ts.map +1 -0
  17. package/dist/runtime/dev_sdk_gen.js +319 -0
  18. package/dist/runtime/execute.d.ts.map +1 -1
  19. package/dist/runtime/execute.js +326 -1
  20. package/dist/runtime/zod_registry.d.ts.map +1 -1
  21. package/dist/runtime/zod_registry.js +58 -0
  22. package/package.json +1 -1
  23. package/sdk-dist/api/admin/index.ts +1 -0
  24. package/sdk-dist/api/admin/public-link.ts +48 -0
  25. package/sdk-dist/api/shared-link/form.ts +42 -1
  26. package/sdk-dist/api/user/index.ts +0 -1
  27. package/sdk-dist/docs/runtime-reference/dashboard.md +22 -0
  28. package/sdk-dist/docs/runtime-reference/dev-sdk-gen.md +41 -0
  29. package/sdk-dist/docs/runtime-reference/help-examples/admin/dashboard/update-layout.json +16 -0
  30. package/sdk-dist/docs/runtime-reference/help-examples/admin/widget/create.json +33 -0
  31. package/sdk-dist/docs/runtime-reference/help-examples/admin/widget/preview-data.json +66 -0
  32. package/sdk-dist/docs/runtime-reference/help-examples/admin/widget/update-ingress-options.json +21 -0
  33. package/sdk-dist/docs/runtime-reference/widgets.md +64 -0
  34. package/sdk-dist/generated/command_registry.generated.ts +416 -0
  35. package/sdk-dist/generated/help_examples.generated.ts +184 -0
  36. package/sdk-dist/generated/type_index.generated.ts +42 -1
  37. package/sdk-dist/generated/zod_registry.generated.ts +56 -0
  38. package/sdk-dist/types/app.ts +37 -1
  39. package/sdk-dist/types/common.ts +77 -35
  40. package/sdk-dist/types/index.ts +1 -1
  41. package/sdk-dist/types/public-link.ts +10 -0
  42. package/sdk-dist/types/shared-link.ts +16 -1
  43. package/sdk-dist/types/user-action.ts +1 -43
  44. package/src/generated/command_registry.generated.ts +416 -0
  45. package/src/generated/help_examples.generated.ts +184 -0
  46. package/src/generated/type_index.generated.ts +42 -1
  47. package/src/generated/zod_registry.generated.ts +56 -0
  48. package/src/runtime/dev_sdk_gen.ts +360 -0
  49. package/src/runtime/execute.ts +371 -1
  50. package/src/runtime/zod_registry.ts +58 -0
  51. package/src/tests/command_registry.test.ts +21 -2
  52. package/src/tests/dev_sdk_gen.test.ts +226 -0
  53. package/src/tests/docs_readability.test.ts +15 -0
  54. package/src/tests/execute_validation.test.ts +226 -0
  55. package/src/tests/help.test.ts +86 -0
  56. package/sdk-dist/api/user/copilot.ts +0 -20
  57. package/sdk-dist/types/copilot.ts +0 -34
@@ -0,0 +1,319 @@
1
+ import { CLIError } from './errors.js';
2
+ function isRecord(value) {
3
+ return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
4
+ }
5
+ function unwrapData(payload) {
6
+ return isRecord(payload) && 'data' in payload ? payload.data : payload;
7
+ }
8
+ function toPascalCase(value) {
9
+ const words = value
10
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
11
+ .split(/[^A-Za-z0-9]+/)
12
+ .filter(Boolean);
13
+ if (words.length === 0)
14
+ return 'Entity';
15
+ return words.map((word) => `${word[0].toUpperCase()}${word.slice(1)}`).join('');
16
+ }
17
+ function toCamelCase(value) {
18
+ const pascal = toPascalCase(value);
19
+ return `${pascal[0].toLowerCase()}${pascal.slice(1)}`;
20
+ }
21
+ function sanitizeEntityKey(entity) {
22
+ const fallback = entity.sdkName || `entity_${entity.id}`;
23
+ return toCamelCase(fallback) === 'entity' ? `entity${entity.id}` : toCamelCase(fallback);
24
+ }
25
+ function normalizeFieldType(type) {
26
+ switch (type) {
27
+ case 'number':
28
+ return 'number';
29
+ case 'boolean':
30
+ return 'boolean';
31
+ case 'select':
32
+ case 'radio':
33
+ case 'checkbox':
34
+ case 'status':
35
+ return 'number | string';
36
+ case 'file':
37
+ case 'image':
38
+ case 'member':
39
+ case 'members':
40
+ case 'department':
41
+ case 'departments':
42
+ case 'linkto':
43
+ case 'relation':
44
+ case 'relationfield':
45
+ case 'subform':
46
+ return 'unknown';
47
+ default:
48
+ return 'string';
49
+ }
50
+ }
51
+ function normalizeEntities(payload) {
52
+ const root = unwrapData(payload);
53
+ if (!isRecord(root)) {
54
+ throw new CLIError('SDK_GEN_INVALID_SCHEMA', 'sdk-gen expected an app detail object', 2);
55
+ }
56
+ const appId = typeof root.id === 'number' ? root.id : typeof root.app_id === 'number' ? root.app_id : undefined;
57
+ if (!appId) {
58
+ throw new CLIError('SDK_GEN_APP_ID_REQUIRED', 'sdk-gen expected app id in app detail response', 2);
59
+ }
60
+ const sourceEntities = Array.isArray(root.entities)
61
+ ? root.entities
62
+ : Array.isArray(root.tables)
63
+ ? root.tables
64
+ : Array.isArray(root.entitys)
65
+ ? root.entitys
66
+ : [];
67
+ const entities = sourceEntities
68
+ .filter((item) => isRecord(item))
69
+ .filter((item) => typeof item.id === 'number' && typeof item.name === 'string')
70
+ .map((item) => {
71
+ const fields = Array.isArray(item.fields)
72
+ ? item.fields
73
+ .filter((field) => isRecord(field))
74
+ .filter((field) => typeof field.id === 'number' && typeof field.label === 'string' && typeof field.type === 'string')
75
+ .map((field) => ({
76
+ id: field.id,
77
+ label: field.label,
78
+ key: typeof field.key === 'string' ? field.key.trim() : '',
79
+ type: field.type,
80
+ required: field.required === true,
81
+ options: isRecord(field.options) ? field.options : undefined,
82
+ }))
83
+ : [];
84
+ const entity = {
85
+ appId,
86
+ id: item.id,
87
+ name: item.name,
88
+ sdkName: typeof item.key === 'string' && item.key.trim() ? item.key.trim() : '',
89
+ fields,
90
+ };
91
+ entity.sdkName = sanitizeEntityKey(entity);
92
+ return entity;
93
+ });
94
+ if (entities.length === 0) {
95
+ throw new CLIError('SDK_GEN_NO_ENTITIES', 'sdk-gen could not find entities in app detail response', 2);
96
+ }
97
+ validateEntities(entities);
98
+ return entities;
99
+ }
100
+ function validateEntities(entities) {
101
+ const entityKeys = new Set();
102
+ for (const entity of entities) {
103
+ if (entityKeys.has(entity.sdkName)) {
104
+ throw new CLIError('SDK_GEN_DUPLICATE_ENTITY_KEY', `duplicate generated entity key: ${entity.sdkName}`, 2);
105
+ }
106
+ entityKeys.add(entity.sdkName);
107
+ const fieldKeys = new Set();
108
+ for (const field of entity.fields) {
109
+ if (!field.key) {
110
+ throw new CLIError('SDK_GEN_FIELD_KEY_REQUIRED', `field.key is required for ${entity.name}.${field.label} (${field.id})`, 2);
111
+ }
112
+ if (!/^[A-Za-z][A-Za-z0-9_]*$/.test(field.key)) {
113
+ throw new CLIError('SDK_GEN_INVALID_FIELD_KEY', `field.key must be a TypeScript identifier: ${entity.name}.${field.key}`, 2);
114
+ }
115
+ if (fieldKeys.has(field.key)) {
116
+ throw new CLIError('SDK_GEN_DUPLICATE_FIELD_KEY', `duplicate field.key in ${entity.name}: ${field.key}`, 2);
117
+ }
118
+ fieldKeys.add(field.key);
119
+ }
120
+ }
121
+ }
122
+ function emitRuntime() {
123
+ return `export type ArcubaseSdkConfig = {
124
+ baseURL: string
125
+ accessToken?: string
126
+ refreshToken?: string
127
+ }
128
+
129
+ export type ArcubaseRow<TFields> = {
130
+ id: number
131
+ title?: string
132
+ fields: TFields
133
+ raw: Record<string, any>
134
+ }
135
+
136
+ export type ArcubaseRuntime = {
137
+ config: ArcubaseSdkConfig
138
+ request<T>(method: string, endpoint: string, body?: unknown): Promise<T>
139
+ }
140
+
141
+ type ArcubaseEnvelope<T> = {
142
+ data?: T
143
+ error?: { message?: string; key?: string; type?: string }
144
+ }
145
+
146
+ export function createRuntime(config: ArcubaseSdkConfig): ArcubaseRuntime {
147
+ let accessToken = config.accessToken ?? ''
148
+ let refreshToken = config.refreshToken ?? ''
149
+
150
+ async function request<T>(method: string, endpoint: string, body?: unknown): Promise<T> {
151
+ const baseURL = config.baseURL.endsWith('/') ? config.baseURL : \`\${config.baseURL}/\`
152
+ const response = await fetch(new URL(endpoint.replace(/^\\/+/, ''), baseURL).toString(), {
153
+ method,
154
+ headers: {
155
+ 'Content-Type': 'application/json',
156
+ ...(accessToken ? { Authorization: \`Bearer \${accessToken}\` } : {}),
157
+ },
158
+ body: body === undefined ? undefined : JSON.stringify(body),
159
+ })
160
+ const payload = await response.json().catch(() => null) as ArcubaseEnvelope<T> | null
161
+ if (!response.ok || payload?.error) {
162
+ throw new Error(payload?.error?.message ?? \`Arcubase request failed: \${response.status}\`)
163
+ }
164
+ return payload?.data as T
165
+ }
166
+
167
+ return {
168
+ config: { ...config, accessToken, refreshToken },
169
+ request,
170
+ }
171
+ }
172
+ `;
173
+ }
174
+ function emitSchema(entities) {
175
+ const schema = {
176
+ entities: Object.fromEntries(entities.map((entity) => [
177
+ entity.sdkName,
178
+ {
179
+ entityId: entity.id,
180
+ name: entity.name,
181
+ fields: Object.fromEntries(entity.fields.map((field) => [
182
+ field.key,
183
+ {
184
+ fieldId: field.id,
185
+ label: field.label,
186
+ type: field.type,
187
+ required: field.required,
188
+ },
189
+ ])),
190
+ },
191
+ ])),
192
+ };
193
+ return `export const arcubaseSchema = ${JSON.stringify(schema, null, 2)} as const
194
+
195
+ export type ArcubaseEntityKey = keyof typeof arcubaseSchema.entities
196
+ `;
197
+ }
198
+ function emitEntity(entity) {
199
+ const typeName = toPascalCase(entity.sdkName);
200
+ const fieldLines = entity.fields.map((field) => {
201
+ const optional = field.required ? '' : '?';
202
+ return ` ${field.key}${optional}: ${normalizeFieldType(field.type)}`;
203
+ });
204
+ const fieldMapLines = entity.fields.map((field) => ` ${field.key}: ${field.id},`);
205
+ return `import type { ArcubaseRuntime, ArcubaseRow } from '../runtime.js'
206
+
207
+ export type ${typeName}Fields = {
208
+ ${fieldLines.join('\n')}
209
+ }
210
+
211
+ export type ${typeName}CreateInput = ${typeName}Fields
212
+ export type ${typeName}UpdateInput = Partial<${typeName}Fields>
213
+
214
+ const entityId = ${entity.id}
215
+ const fieldIds = {
216
+ ${fieldMapLines.join('\n')}
217
+ } as const
218
+
219
+ function toArcubaseFields(fields: Partial<${typeName}Fields>): Record<string, any> {
220
+ const out: Record<string, any> = {}
221
+ for (const [key, value] of Object.entries(fields)) {
222
+ if (value === undefined) continue
223
+ const fieldId = fieldIds[key as keyof typeof fieldIds]
224
+ if (!fieldId) {
225
+ throw new Error(\`Unknown ${entity.sdkName} field: \${key}\`)
226
+ }
227
+ out[String(fieldId)] = value
228
+ }
229
+ return out
230
+ }
231
+
232
+ function fromArcubaseFields(raw: Record<string, any> | undefined): ${typeName}Fields {
233
+ const out: Record<string, any> = {}
234
+ const source = raw ?? {}
235
+ for (const [key, fieldId] of Object.entries(fieldIds)) {
236
+ if (String(fieldId) in source) {
237
+ out[key] = source[String(fieldId)]
238
+ }
239
+ }
240
+ return out as ${typeName}Fields
241
+ }
242
+
243
+ function mapRow(raw: Record<string, any>): ArcubaseRow<${typeName}Fields> {
244
+ return {
245
+ id: Number(raw.id),
246
+ title: typeof raw.title === 'string' ? raw.title : undefined,
247
+ fields: fromArcubaseFields(raw.fields),
248
+ raw,
249
+ }
250
+ }
251
+
252
+ export function create${typeName}SDK(runtime: ArcubaseRuntime) {
253
+ return {
254
+ async create(fields: ${typeName}CreateInput): Promise<number> {
255
+ return runtime.request<number>('POST', \`/entity/${entity.appId}/\${entityId}/insert\`, {
256
+ fields: toArcubaseFields(fields),
257
+ })
258
+ },
259
+
260
+ async update(rowId: string | number, fields: ${typeName}UpdateInput): Promise<boolean> {
261
+ const data = toArcubaseFields(fields)
262
+ return runtime.request<boolean>('PUT', \`/entity/${entity.appId}/\${entityId}/row/\${rowId}\`, {
263
+ data,
264
+ changed_fields: Object.keys(data).map((fieldId) => Number(fieldId)),
265
+ })
266
+ },
267
+
268
+ async get(rowId: string | number): Promise<ArcubaseRow<${typeName}Fields>> {
269
+ const data = await runtime.request<{ record?: Record<string, any> }>('GET', \`/entity/${entity.appId}/\${entityId}/row/\${rowId}\`)
270
+ return mapRow(data.record ?? {})
271
+ },
272
+
273
+ async query(params: { limit?: number; offset?: number; search?: Record<string, any> } = {}): Promise<{ items: ArcubaseRow<${typeName}Fields>[]; total?: number }> {
274
+ const data = await runtime.request<{ items?: Record<string, any>[]; total?: number }>('POST', \`/entity/${entity.appId}/\${entityId}/data-query\`, params)
275
+ return {
276
+ items: (data.items ?? []).map(mapRow),
277
+ total: data.total,
278
+ }
279
+ },
280
+ }
281
+ }
282
+ `;
283
+ }
284
+ function emitClient(entities) {
285
+ const imports = entities.map((entity) => {
286
+ const typeName = toPascalCase(entity.sdkName);
287
+ return `import { create${typeName}SDK } from './entities/${entity.sdkName}.js'`;
288
+ });
289
+ const properties = entities.map((entity) => ` ${entity.sdkName}: create${toPascalCase(entity.sdkName)}SDK(runtime),`);
290
+ return `import { createRuntime, type ArcubaseSdkConfig } from './runtime.js'
291
+ ${imports.join('\n')}
292
+
293
+ export function createArcubaseSdk(config: ArcubaseSdkConfig) {
294
+ const runtime = createRuntime(config)
295
+ return {
296
+ ${properties.join('\n')}
297
+ }
298
+ }
299
+ `;
300
+ }
301
+ function emitIndex(entities) {
302
+ return `export { createArcubaseSdk } from './client.js'
303
+ export type { ArcubaseSdkConfig, ArcubaseRow } from './runtime.js'
304
+ export { arcubaseSchema } from './schema.js'
305
+ ${entities.map((entity) => `export type { ${toPascalCase(entity.sdkName)}Fields, ${toPascalCase(entity.sdkName)}CreateInput, ${toPascalCase(entity.sdkName)}UpdateInput } from './entities/${entity.sdkName}.js'`).join('\n')}
306
+ `;
307
+ }
308
+ export function generateArcubaseProjectSDK(payload) {
309
+ const entities = normalizeEntities(payload);
310
+ return {
311
+ files: [
312
+ { path: 'runtime.ts', contents: emitRuntime() },
313
+ { path: 'schema.ts', contents: emitSchema(entities) },
314
+ ...entities.map((entity) => ({ path: `entities/${entity.sdkName}.ts`, contents: emitEntity(entity) })),
315
+ { path: 'client.ts', contents: emitClient(entities) },
316
+ { path: 'index.ts', contents: emitIndex(entities) },
317
+ ],
318
+ };
319
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/runtime/execute.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AAG1D,OAAO,EAAyF,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAQhJ,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,YAAY,CAAA;IACnB,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;CAC9B,CAAA;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAc1D;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAE,QAAsB,GAAG,MAAM,CAoB7G;AA8BD,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAUlD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,GAAE,QAAsB,GAAG,MAAM,CA8HpI;AA04DD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,uBAAuB,CActH;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CA+NrI"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../src/runtime/execute.ts"],"names":[],"mappings":"AAGA,OAAO,EAAkB,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AAG1D,OAAO,EAAyF,KAAK,YAAY,EAAE,MAAM,uBAAuB,CAAA;AAShJ,MAAM,MAAM,uBAAuB,GAAG;IACpC,KAAK,EAAE,YAAY,CAAA;IACnB,WAAW,EAAE,SAAS,MAAM,EAAE,CAAA;IAC9B,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,SAAS,EAAE,SAAS,MAAM,EAAE,CAAA;IAC5B,UAAU,EAAE,SAAS,MAAM,EAAE,CAAA;CAC9B,CAAA;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,YAAY,GAAG,MAAM,CAe1D;AAaD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAE,QAAsB,GAAG,MAAM,CAoB7G;AA8BD,KAAK,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAA;AAUlD,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,GAAE,QAAsB,GAAG,MAAM,CA8HpI;AAquED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,uBAAuB,CActH;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,CAAC,EAAE,UAAU,EAAE,SAAS,GAAE,OAAO,KAAa,GAAG,OAAO,CAAC,GAAG,CAAC,CAyOrI"}