@carthooks/arcubase-cli 0.1.25 → 0.1.27
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/bundle/arcubase-admin.mjs +813 -448
- package/bundle/arcubase.mjs +813 -448
- package/dist/generated/zod_registry.generated.d.ts.map +1 -1
- package/dist/generated/zod_registry.generated.js +3 -3
- package/dist/runtime/execute.d.ts.map +1 -1
- package/dist/runtime/execute.js +118 -11
- package/package.json +2 -2
- package/sdk-dist/docs/runtime-reference/dev-sdk-gen.md +14 -10
- package/sdk-dist/generated/zod_registry.generated.ts +3 -3
- package/sdk-dist/types/common.ts +1 -0
- package/sdk-dist/types/ingress.ts +15 -0
- package/sdk-dist/types/user-action.ts +1 -0
- package/src/generated/zod_registry.generated.ts +3 -3
- package/src/runtime/execute.ts +126 -11
- package/src/tests/dev_sdk_gen.test.ts +128 -39
- package/src/tests/help.test.ts +2 -1
- package/src/tests/zod_registry.test.ts +1 -0
- package/dist/runtime/dev_sdk_gen.d.ts +0 -4
- package/dist/runtime/dev_sdk_gen.d.ts.map +0 -1
- package/dist/runtime/dev_sdk_gen.js +0 -13
- package/src/runtime/dev_sdk_gen.ts +0 -20
|
@@ -4,7 +4,7 @@ import fs from 'fs'
|
|
|
4
4
|
import os from 'os'
|
|
5
5
|
import path from 'path'
|
|
6
6
|
|
|
7
|
-
import { generateArcubaseProjectSDK } from '
|
|
7
|
+
import { ArcubaseCodeGenError, generateArcubaseProjectSDK } from '@arcubase/code-gen'
|
|
8
8
|
import { executeCLI } from '../runtime/execute.js'
|
|
9
9
|
import { CLIError } from '../runtime/errors.js'
|
|
10
10
|
|
|
@@ -21,7 +21,7 @@ const env = {
|
|
|
21
21
|
function appDetail() {
|
|
22
22
|
return {
|
|
23
23
|
id: 3882856425,
|
|
24
|
-
name: '
|
|
24
|
+
name: 'Demo App',
|
|
25
25
|
entities: {},
|
|
26
26
|
}
|
|
27
27
|
}
|
|
@@ -30,7 +30,7 @@ function entityList() {
|
|
|
30
30
|
return [
|
|
31
31
|
{
|
|
32
32
|
id: 3883547182,
|
|
33
|
-
name: '
|
|
33
|
+
name: 'Records',
|
|
34
34
|
},
|
|
35
35
|
]
|
|
36
36
|
}
|
|
@@ -42,7 +42,7 @@ function appDetailWithEntityList() {
|
|
|
42
42
|
entities: [
|
|
43
43
|
{
|
|
44
44
|
id: 3883547182,
|
|
45
|
-
name: '
|
|
45
|
+
name: 'Records',
|
|
46
46
|
},
|
|
47
47
|
],
|
|
48
48
|
}
|
|
@@ -52,12 +52,11 @@ function entityDetail() {
|
|
|
52
52
|
return {
|
|
53
53
|
id: 3883547182,
|
|
54
54
|
app_id: 3882856425,
|
|
55
|
-
name: '
|
|
56
|
-
key: 'story',
|
|
55
|
+
name: 'Records',
|
|
57
56
|
fields: [
|
|
58
|
-
{ id: 1006, label: '
|
|
59
|
-
{ id: 1013, label: '
|
|
60
|
-
{ id: 1011, label: '
|
|
57
|
+
{ id: 1006, label: 'Title', key: 'title', type: 'text', required: true, options: {} },
|
|
58
|
+
{ id: 1013, label: 'Status', key: 'status', type: 'select', required: false, options: { options: { items: [{ key: 1, value: 'Open' }] } } },
|
|
59
|
+
{ id: 1011, label: 'Notes', key: 'notes', type: 'textarea', required: false, options: {} },
|
|
61
60
|
],
|
|
62
61
|
}
|
|
63
62
|
}
|
|
@@ -70,76 +69,125 @@ function entityDetailWithoutFieldKeys() {
|
|
|
70
69
|
|
|
71
70
|
function sdkSchemaPayload() {
|
|
72
71
|
return {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
id: 3882856425,
|
|
73
|
+
name: 'Demo App',
|
|
74
|
+
ingresses: [ingressDefinition()],
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function ingressDefinition(actions = ['view', 'add', 'edit', 'delete', 'bulk_update', 'export', 'batch_print', 'tag_manage', 'archive']) {
|
|
79
|
+
return {
|
|
80
|
+
id: 9001,
|
|
81
|
+
hash_id: '8WR5Kypbw9KQK20',
|
|
82
|
+
key: 'publicEntry',
|
|
83
|
+
name: 'Public entry',
|
|
84
|
+
entity: entityDetail(),
|
|
85
|
+
options: {
|
|
86
|
+
policy: {
|
|
87
|
+
actions,
|
|
88
|
+
all_fields_read: true,
|
|
89
|
+
all_fields_write: true,
|
|
90
|
+
fields: [],
|
|
78
91
|
},
|
|
79
|
-
|
|
92
|
+
list_options: {},
|
|
93
|
+
},
|
|
80
94
|
}
|
|
81
95
|
}
|
|
82
96
|
|
|
83
|
-
test('generateArcubaseProjectSDK emits typed
|
|
97
|
+
test('generateArcubaseProjectSDK emits typed ingress sdk selected from client.ingress', () => {
|
|
84
98
|
const result = generateArcubaseProjectSDK(sdkSchemaPayload())
|
|
85
99
|
|
|
86
100
|
assert.deepEqual(result.files.map((file) => file.path).sort(), [
|
|
87
101
|
'client.ts',
|
|
88
|
-
'entities/story.ts',
|
|
89
102
|
'index.ts',
|
|
103
|
+
'ingresses/publicEntry.ts',
|
|
90
104
|
'runtime.ts',
|
|
91
105
|
'schema.ts',
|
|
92
106
|
])
|
|
93
107
|
|
|
94
108
|
const client = result.files.find((file) => file.path === 'client.ts')?.contents ?? ''
|
|
95
|
-
assert.match(client, /
|
|
96
|
-
assert.match(client, /
|
|
109
|
+
assert.match(client, /createArcubaseClient/)
|
|
110
|
+
assert.match(client, /ingress<K extends ArcubaseIngressKey>\(key: K\)/)
|
|
111
|
+
assert.match(client, /case ["']publicEntry["']/)
|
|
97
112
|
|
|
98
113
|
const runtime = result.files.find((file) => file.path === 'runtime.ts')?.contents ?? ''
|
|
99
114
|
assert.match(runtime, /accessToken/)
|
|
100
115
|
assert.match(runtime, /refreshToken/)
|
|
101
116
|
|
|
102
|
-
const
|
|
103
|
-
assert.match(
|
|
104
|
-
assert.match(
|
|
105
|
-
assert.match(
|
|
106
|
-
assert.match(
|
|
107
|
-
assert.match(
|
|
108
|
-
assert.match(
|
|
109
|
-
assert.match(
|
|
110
|
-
assert.match(
|
|
111
|
-
assert.match(
|
|
117
|
+
const ingress = result.files.find((file) => file.path === 'ingresses/publicEntry.ts')?.contents ?? ''
|
|
118
|
+
assert.match(ingress, /export type PublicEntryFields =/)
|
|
119
|
+
assert.match(ingress, /title: string/)
|
|
120
|
+
assert.match(ingress, /status\?: number \| string/)
|
|
121
|
+
assert.match(ingress, /notes\?: string/)
|
|
122
|
+
assert.match(ingress, /create\(fields: PublicEntryCreateInput\)/)
|
|
123
|
+
assert.match(ingress, /update\(rowId: string \| number, fields: PublicEntryUpdateInput\)/)
|
|
124
|
+
assert.match(ingress, /query\(params: PublicEntryQueryParams = {}\)/)
|
|
125
|
+
assert.match(ingress, /delete\(selection: PublicEntrySelection\)/)
|
|
126
|
+
assert.match(ingress, /bulkUpdate\(selection: PublicEntrySelection, fields: PublicEntryBulkUpdateField\[\]\)/)
|
|
127
|
+
assert.match(ingress, /exportRows\(options: PublicEntryExportOptions = {}\)/)
|
|
128
|
+
assert.match(ingress, /batchPrint\(selection: PublicEntrySelection/)
|
|
129
|
+
assert.match(ingress, /archive\(selection: PublicEntrySelection\)/)
|
|
130
|
+
assert.match(ingress, /tags:/)
|
|
131
|
+
assert.match(ingress, /policy_id: ingressId/)
|
|
112
132
|
|
|
113
133
|
const schema = result.files.find((file) => file.path === 'schema.ts')?.contents ?? ''
|
|
114
|
-
assert.match(schema, /"
|
|
134
|
+
assert.match(schema, /"publicEntry"/)
|
|
115
135
|
assert.match(schema, /"fieldId": 1011/)
|
|
116
136
|
assert.match(schema, /"entityId": 3883547182/)
|
|
137
|
+
assert.match(schema, /"ingressId": "8WR5Kypbw9KQK20"/)
|
|
138
|
+
assert.doesNotMatch(schema, /entity3883547182/)
|
|
117
139
|
})
|
|
118
140
|
|
|
119
141
|
test('generateArcubaseProjectSDK rejects missing field keys', () => {
|
|
120
142
|
const payload = sdkSchemaPayload()
|
|
121
|
-
payload.
|
|
143
|
+
payload.ingresses[0].entity.fields[1].key = ''
|
|
122
144
|
|
|
123
145
|
assert.throws(() => generateArcubaseProjectSDK(payload), (error: unknown) => {
|
|
124
|
-
assert.ok(error instanceof
|
|
146
|
+
assert.ok(error instanceof ArcubaseCodeGenError)
|
|
125
147
|
assert.equal(error.code, 'SDK_GEN_FIELD_KEY_REQUIRED')
|
|
126
|
-
assert.match(error.message,
|
|
148
|
+
assert.match(error.message, /Status/)
|
|
127
149
|
return true
|
|
128
150
|
})
|
|
129
151
|
})
|
|
130
152
|
|
|
131
153
|
test('generateArcubaseProjectSDK rejects duplicate field keys within an entity', () => {
|
|
132
154
|
const payload = sdkSchemaPayload()
|
|
133
|
-
payload.
|
|
155
|
+
payload.ingresses[0].entity.fields[1].key = 'notes'
|
|
134
156
|
|
|
135
157
|
assert.throws(() => generateArcubaseProjectSDK(payload), (error: unknown) => {
|
|
136
|
-
assert.ok(error instanceof
|
|
158
|
+
assert.ok(error instanceof ArcubaseCodeGenError)
|
|
137
159
|
assert.equal(error.code, 'SDK_GEN_DUPLICATE_FIELD_KEY')
|
|
138
|
-
assert.match(error.message, /
|
|
160
|
+
assert.match(error.message, /notes/)
|
|
161
|
+
return true
|
|
162
|
+
})
|
|
163
|
+
})
|
|
164
|
+
|
|
165
|
+
test('generateArcubaseProjectSDK rejects missing ingress keys', () => {
|
|
166
|
+
const payload = sdkSchemaPayload()
|
|
167
|
+
payload.ingresses[0].key = ''
|
|
168
|
+
|
|
169
|
+
assert.throws(() => generateArcubaseProjectSDK(payload), (error: unknown) => {
|
|
170
|
+
assert.ok(error instanceof ArcubaseCodeGenError)
|
|
171
|
+
assert.equal(error.code, 'SDK_GEN_INGRESS_KEY_REQUIRED')
|
|
172
|
+
assert.match(error.message, /Public entry/)
|
|
139
173
|
return true
|
|
140
174
|
})
|
|
141
175
|
})
|
|
142
176
|
|
|
177
|
+
test('generateArcubaseProjectSDK only emits methods allowed by ingress actions', () => {
|
|
178
|
+
const payload = {
|
|
179
|
+
id: 3882856425,
|
|
180
|
+
ingresses: [ingressDefinition(['view'])],
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const result = generateArcubaseProjectSDK(payload)
|
|
184
|
+
const ingress = result.files.find((file) => file.path === 'ingresses/publicEntry.ts')?.contents ?? ''
|
|
185
|
+
assert.match(ingress, /query\(params: PublicEntryQueryParams = {}\)/)
|
|
186
|
+
assert.doesNotMatch(ingress, /create\(fields:/)
|
|
187
|
+
assert.doesNotMatch(ingress, /update\(rowId:/)
|
|
188
|
+
assert.doesNotMatch(ingress, /bulkUpdate\(/)
|
|
189
|
+
})
|
|
190
|
+
|
|
143
191
|
test('arcubase-admin dev sdk-gen fetches app schema and writes typed sdk files', async () => {
|
|
144
192
|
const outDir = fs.mkdtempSync(path.join(os.tmpdir(), 'arcubase-sdk-gen-'))
|
|
145
193
|
const calls: Array<{ url: string; method?: string }> = []
|
|
@@ -159,6 +207,9 @@ test('arcubase-admin dev sdk-gen fetches app schema and writes typed sdk files',
|
|
|
159
207
|
if (String(url).endsWith('/api/apps/3882856425/entity/3883547182')) {
|
|
160
208
|
return new Response(JSON.stringify({ data: entityDetail() }), { status: 200 })
|
|
161
209
|
}
|
|
210
|
+
if (String(url).endsWith('/api/apps/3882856425/entity/3883547182/ingress')) {
|
|
211
|
+
return new Response(JSON.stringify({ data: { list: [ingressDefinition()] } }), { status: 200 })
|
|
212
|
+
}
|
|
162
213
|
return new Response(JSON.stringify({ error: { message: `unexpected url ${url}` } }), { status: 404 })
|
|
163
214
|
},
|
|
164
215
|
)
|
|
@@ -171,10 +222,13 @@ test('arcubase-admin dev sdk-gen fetches app schema and writes typed sdk files',
|
|
|
171
222
|
assert.match(calls[1].url, /\/api\/apps\/3882856425\/entities$/)
|
|
172
223
|
assert.equal(calls[2].method, 'GET')
|
|
173
224
|
assert.match(calls[2].url, /\/api\/apps\/3882856425\/entity\/3883547182$/)
|
|
225
|
+
assert.equal(calls[3].method, 'GET')
|
|
226
|
+
assert.match(calls[3].url, /\/api\/apps\/3882856425\/entity\/3883547182\/ingress$/)
|
|
174
227
|
assert.ok(fs.existsSync(path.join(outDir, 'index.ts')))
|
|
175
228
|
assert.ok(fs.existsSync(path.join(outDir, 'client.ts')))
|
|
176
|
-
assert.ok(fs.existsSync(path.join(outDir, '
|
|
177
|
-
assert.match(fs.readFileSync(path.join(outDir, 'client.ts'), 'utf8'), /
|
|
229
|
+
assert.ok(fs.existsSync(path.join(outDir, 'ingresses/publicEntry.ts')))
|
|
230
|
+
assert.match(fs.readFileSync(path.join(outDir, 'client.ts'), 'utf8'), /createArcubaseClient/)
|
|
231
|
+
assert.notEqual(fs.existsSync(path.join(outDir, 'entities')), true)
|
|
178
232
|
})
|
|
179
233
|
|
|
180
234
|
test('arcubase-admin dev sdk-gen fails before generation when field keys are missing', async () => {
|
|
@@ -196,15 +250,50 @@ test('arcubase-admin dev sdk-gen fails before generation when field keys are mis
|
|
|
196
250
|
if (String(url).endsWith('/api/apps/3882856425/entity/3883547182')) {
|
|
197
251
|
return new Response(JSON.stringify({ data: entityDetailWithoutFieldKeys() }), { status: 200 })
|
|
198
252
|
}
|
|
253
|
+
if (String(url).endsWith('/api/apps/3882856425/entity/3883547182/ingress')) {
|
|
254
|
+
return new Response(JSON.stringify({ data: { list: [ingressDefinition()] } }), { status: 200 })
|
|
255
|
+
}
|
|
199
256
|
return new Response(JSON.stringify({ error: { message: `unexpected url ${url}` } }), { status: 404 })
|
|
200
257
|
},
|
|
201
258
|
), (error: unknown) => {
|
|
202
259
|
assert.ok(error instanceof CLIError)
|
|
203
260
|
assert.equal(error.code, 'SDK_GEN_FIELD_KEY_REQUIRED')
|
|
204
|
-
assert.match(error.message,
|
|
261
|
+
assert.match(error.message, /Records\.Title \(1006\)/)
|
|
205
262
|
return true
|
|
206
263
|
})
|
|
207
264
|
|
|
208
265
|
assert.equal(calls.some((call) => call.url.endsWith('/custom-keys')), false)
|
|
209
266
|
assert.equal(fs.existsSync(path.join(outDir, 'index.ts')), false)
|
|
210
267
|
})
|
|
268
|
+
|
|
269
|
+
test('arcubase-admin dev sdk-gen fails before generation when ingress keys are missing', async () => {
|
|
270
|
+
const outDir = fs.mkdtempSync(path.join(os.tmpdir(), 'arcubase-sdk-gen-'))
|
|
271
|
+
|
|
272
|
+
await assert.rejects(() => executeCLI(
|
|
273
|
+
'admin',
|
|
274
|
+
['dev', 'sdk-gen', '--app-id', '3882856425', '--out', outDir],
|
|
275
|
+
env as any,
|
|
276
|
+
async (url) => {
|
|
277
|
+
if (String(url).endsWith('/api/apps/3882856425')) {
|
|
278
|
+
return new Response(JSON.stringify({ data: appDetail() }), { status: 200 })
|
|
279
|
+
}
|
|
280
|
+
if (String(url).endsWith('/api/apps/3882856425/entities')) {
|
|
281
|
+
return new Response(JSON.stringify({ data: entityList() }), { status: 200 })
|
|
282
|
+
}
|
|
283
|
+
if (String(url).endsWith('/api/apps/3882856425/entity/3883547182')) {
|
|
284
|
+
return new Response(JSON.stringify({ data: entityDetail() }), { status: 200 })
|
|
285
|
+
}
|
|
286
|
+
if (String(url).endsWith('/api/apps/3882856425/entity/3883547182/ingress')) {
|
|
287
|
+
return new Response(JSON.stringify({ data: { list: [{ ...ingressDefinition(), key: '' }] } }), { status: 200 })
|
|
288
|
+
}
|
|
289
|
+
return new Response(JSON.stringify({ error: { message: `unexpected url ${url}` } }), { status: 404 })
|
|
290
|
+
},
|
|
291
|
+
), (error: unknown) => {
|
|
292
|
+
assert.ok(error instanceof CLIError)
|
|
293
|
+
assert.equal(error.code, 'SDK_GEN_INGRESS_KEY_REQUIRED')
|
|
294
|
+
assert.match(error.message, /Public entry/)
|
|
295
|
+
return true
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
assert.equal(fs.existsSync(path.join(outDir, 'index.ts')), false)
|
|
299
|
+
})
|
package/src/tests/help.test.ts
CHANGED
|
@@ -36,8 +36,9 @@ test('admin dev sdk-gen help documents typed project sdk generation', async () =
|
|
|
36
36
|
assert.match(commandHelp.text, /arcubase-admin dev sdk-gen/)
|
|
37
37
|
assert.match(commandHelp.text, /--app-id/)
|
|
38
38
|
assert.match(commandHelp.text, /--out/)
|
|
39
|
+
assert.match(commandHelp.text, /ingress\.key/)
|
|
39
40
|
assert.match(commandHelp.text, /field\.key/)
|
|
40
|
-
assert.match(commandHelp.text, /
|
|
41
|
+
assert.match(commandHelp.text, /createArcubaseClient\(\{ baseURL, accessToken, refreshToken \}\)\.ingress\("<IngressKey>"\)/)
|
|
41
42
|
})
|
|
42
43
|
|
|
43
44
|
test('user root help exposes reset surface nouns only', async () => {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"dev_sdk_gen.d.ts","sourceRoot":"","sources":["../../src/runtime/dev_sdk_gen.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,YAAY,EAClB,MAAM,oBAAoB,CAAA;AAI3B,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAA;AAExE,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,OAAO,GAAG,YAAY,CASzE"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import { ArcubaseCodeGenError, generateArcubaseProjectSDK as generateArcubaseProjectSDKCore, } from '@arcubase/code-gen';
|
|
2
|
-
import { CLIError } from './errors.js';
|
|
3
|
-
export function generateArcubaseProjectSDK(payload) {
|
|
4
|
-
try {
|
|
5
|
-
return generateArcubaseProjectSDKCore(payload);
|
|
6
|
-
}
|
|
7
|
-
catch (error) {
|
|
8
|
-
if (error instanceof ArcubaseCodeGenError) {
|
|
9
|
-
throw new CLIError(error.code, error.message, 2);
|
|
10
|
-
}
|
|
11
|
-
throw error;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
ArcubaseCodeGenError,
|
|
3
|
-
generateArcubaseProjectSDK as generateArcubaseProjectSDKCore,
|
|
4
|
-
type GeneratedSDK,
|
|
5
|
-
} from '@arcubase/code-gen'
|
|
6
|
-
|
|
7
|
-
import { CLIError } from './errors.js'
|
|
8
|
-
|
|
9
|
-
export type { GeneratedSDK, GeneratedSDKFile } from '@arcubase/code-gen'
|
|
10
|
-
|
|
11
|
-
export function generateArcubaseProjectSDK(payload: unknown): GeneratedSDK {
|
|
12
|
-
try {
|
|
13
|
-
return generateArcubaseProjectSDKCore(payload)
|
|
14
|
-
} catch (error) {
|
|
15
|
-
if (error instanceof ArcubaseCodeGenError) {
|
|
16
|
-
throw new CLIError(error.code, error.message, 2)
|
|
17
|
-
}
|
|
18
|
-
throw error
|
|
19
|
-
}
|
|
20
|
-
}
|