@carthooks/arcubase-cli 0.1.10 → 0.1.12
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 +1274 -375
- package/bundle/arcubase.mjs +1274 -375
- package/dist/generated/command_registry.generated.d.ts +36 -35
- package/dist/generated/command_registry.generated.d.ts.map +1 -1
- package/dist/generated/command_registry.generated.js +37 -36
- package/dist/generated/type_index.generated.d.ts +23 -23
- package/dist/generated/type_index.generated.js +23 -23
- package/dist/runtime/entity_save_schema.d.ts.map +1 -1
- package/dist/runtime/entity_save_schema.js +30 -0
- package/dist/runtime/env.d.ts +2 -1
- package/dist/runtime/env.d.ts.map +1 -1
- package/dist/runtime/env.js +10 -1
- package/dist/runtime/errors.d.ts +2 -0
- package/dist/runtime/errors.d.ts.map +1 -1
- package/dist/runtime/execute.d.ts +4 -2
- package/dist/runtime/execute.d.ts.map +1 -1
- package/dist/runtime/execute.js +944 -134
- package/dist/runtime/paths.d.ts +1 -1
- package/dist/runtime/paths.d.ts.map +1 -1
- package/dist/runtime/paths.js +9 -2
- package/dist/runtime/upload.d.ts +1 -11
- package/dist/runtime/upload.d.ts.map +1 -1
- package/dist/runtime/upload.js +19 -133
- package/dist/runtime/zod_registry.d.ts +5 -3
- package/dist/runtime/zod_registry.d.ts.map +1 -1
- package/dist/runtime/zod_registry.js +108 -48
- package/dist/tests/bootstrap.test.js +12 -0
- package/dist/tests/command_registry.test.js +8 -0
- package/dist/tests/docs_readability.test.d.ts +2 -0
- package/dist/tests/docs_readability.test.d.ts.map +1 -0
- package/dist/tests/docs_readability.test.js +87 -0
- package/dist/tests/entity_save_schema.test.js +22 -0
- package/dist/tests/execute_validation.test.js +381 -44
- package/dist/tests/help.test.js +45 -8
- package/dist/tests/upload.test.js +30 -77
- package/package.json +1 -1
- package/sdk-dist/docs/runtime-reference/README.md +43 -29
- package/sdk-dist/docs/runtime-reference/access-rule.md +109 -0
- package/sdk-dist/docs/runtime-reference/condition.md +182 -0
- package/sdk-dist/docs/runtime-reference/entity-schema/README.md +6 -5
- package/sdk-dist/docs/runtime-reference/entity-schema/checkbox.md +1 -1
- package/sdk-dist/docs/runtime-reference/entity-schema/file.md +2 -2
- package/sdk-dist/docs/runtime-reference/entity-schema/image.md +2 -2
- package/sdk-dist/docs/runtime-reference/entity-schema/linkto.md +3 -3
- package/sdk-dist/docs/runtime-reference/entity-schema/number.md +1 -1
- package/sdk-dist/docs/runtime-reference/entity-schema/query.md +3 -3
- package/sdk-dist/docs/runtime-reference/entity-schema/relation.md +3 -3
- package/sdk-dist/docs/runtime-reference/entity-schema/relationfield.md +3 -3
- package/sdk-dist/docs/runtime-reference/entity-schema/status.md +1 -1
- package/sdk-dist/docs/runtime-reference/entity-schema/text.md +1 -1
- package/sdk-dist/docs/runtime-reference/entity-schema.md +28 -21
- package/sdk-dist/docs/runtime-reference/examples/README.md +1 -1
- package/sdk-dist/docs/runtime-reference/examples/crm-01/README.md +30 -11
- package/sdk-dist/docs/runtime-reference/examples/crm-01/contact.create-entity.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/contact.schema.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/create-app.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/customer.create-entity.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/customer.schema.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/follow-up.create-entity.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/follow-up.schema.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.bulk-update.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.create-entity.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.query.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.row.insert.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.schema.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/crm-01/lead.selection.condition.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/oms-01/README.md +26 -26
- package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.row.insert.json +1 -1
- package/sdk-dist/docs/runtime-reference/examples/wms-01/README.md +26 -10
- package/sdk-dist/docs/runtime-reference/examples/wms-01/create-app.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/goods-receipt.create-entity.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/goods-receipt.schema.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.bulk-update.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.create-entity.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.query.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.schema.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/inventory-snapshot.selection.condition.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/item-master.create-entity.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/item-master.schema.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/stock-issue.create-entity.json +1 -0
- package/sdk-dist/docs/runtime-reference/examples/wms-01/stock-issue.schema.json +1 -0
- package/sdk-dist/docs/runtime-reference/row-crud.md +35 -8
- package/sdk-dist/docs/runtime-reference/search-and-bulk-actions.md +31 -95
- package/sdk-dist/docs/runtime-reference/selection.md +128 -0
- package/sdk-dist/docs/runtime-reference/table-lifecycle.md +113 -30
- package/sdk-dist/docs/runtime-reference/uploads.md +30 -59
- package/sdk-dist/docs/runtime-reference/workflow/README.md +129 -13
- package/sdk-dist/generated/command_registry.generated.ts +37 -36
- package/sdk-dist/generated/type_index.generated.ts +23 -23
- package/src/generated/command_registry.generated.ts +37 -36
- package/src/generated/type_index.generated.ts +23 -23
- package/src/runtime/entity_save_schema.ts +30 -0
- package/src/runtime/env.ts +14 -2
- package/src/runtime/errors.ts +2 -0
- package/src/runtime/execute.ts +1166 -134
- package/src/runtime/paths.ts +11 -2
- package/src/runtime/upload.ts +19 -178
- package/src/runtime/zod_registry.ts +113 -48
- package/src/tests/bootstrap.test.ts +16 -0
- package/src/tests/command_registry.test.ts +9 -0
- package/src/tests/docs_readability.test.ts +96 -0
- package/src/tests/entity_save_schema.test.ts +22 -0
- package/src/tests/execute_validation.test.ts +508 -47
- package/src/tests/help.test.ts +49 -8
- package/src/tests/upload.test.ts +34 -91
package/src/tests/help.test.ts
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import test from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
3
|
import { executeCLI } from '../runtime/execute.js'
|
|
4
|
+
import { listCommands, type CommandScope } from '../runtime/command_registry.js'
|
|
5
|
+
import { getTypeHints } from '../runtime/zod_registry.js'
|
|
4
6
|
|
|
5
7
|
const env = {
|
|
6
8
|
ARCUBASE_BASE_URL: 'https://arcubase.example.com',
|
|
7
9
|
ARCUBASE_ACCESS_TOKEN: 'tok',
|
|
10
|
+
ARCUBASE_SDK_ROOT: '/runtime/arcubase-sdk',
|
|
8
11
|
ARCUBASE_TRACE_ID: '',
|
|
9
12
|
ARCUBASE_SUBJECT_TYPE: '',
|
|
10
13
|
ARCUBASE_SUBJECT_ID: '',
|
|
@@ -48,36 +51,74 @@ test('admin module help lists reset app commands', async () => {
|
|
|
48
51
|
})
|
|
49
52
|
|
|
50
53
|
test('user row help replaces workflow row commands', async () => {
|
|
51
|
-
const out = await executeCLI('user', ['row', 'query', '--help'],
|
|
54
|
+
const out = await executeCLI('user', ['row', 'query', '--help'], env as any, async () => new Response('{}'))
|
|
52
55
|
assert.equal(out.kind, 'help')
|
|
53
|
-
assert.match(out.text, /body: .*--body-
|
|
56
|
+
assert.match(out.text, /body: .*--body-json <json-string> \| --body-file <file>\.json/)
|
|
54
57
|
assert.match(out.text, /query flags: --query-file/)
|
|
55
58
|
assert.doesNotMatch(out.text, /workflow query-entity/)
|
|
56
59
|
assert.doesNotMatch(out.text, /--entity-id/)
|
|
57
60
|
})
|
|
58
61
|
|
|
62
|
+
test('user row help does not expose internal policy id input', async () => {
|
|
63
|
+
const out = await executeCLI('user', ['row', 'get', '--help'], env as any, async () => new Response('{}'))
|
|
64
|
+
assert.equal(out.kind, 'help')
|
|
65
|
+
assert.doesNotMatch(out.text, /--policy-id/)
|
|
66
|
+
assert.doesNotMatch(out.text, /policy_id/)
|
|
67
|
+
})
|
|
68
|
+
|
|
59
69
|
test('admin table update-schema help uses table nouns and body-json', async () => {
|
|
60
|
-
const out = await executeCLI('admin', ['table', 'update-schema', '--help'],
|
|
70
|
+
const out = await executeCLI('admin', ['table', 'update-schema', '--help'], env as any, async () => new Response('{}'))
|
|
61
71
|
assert.equal(out.kind, 'help')
|
|
62
|
-
assert.match(out.text, /body: .*--body-
|
|
72
|
+
assert.match(out.text, /body: .*--body-json <json-string> \| --body-file <file>\.json/)
|
|
73
|
+
assert.match(out.text, /prefer --body-json/)
|
|
63
74
|
assert.match(out.text, /path flags: --app-id, --table-id/)
|
|
64
75
|
assert.doesNotMatch(out.text, /--entity-id/)
|
|
65
76
|
assert.doesNotMatch(out.text, /admin-save-entity/)
|
|
66
77
|
})
|
|
67
78
|
|
|
79
|
+
test('admin table create help gives one-call body-json example', async () => {
|
|
80
|
+
const out = await executeCLI('admin', ['table', 'create', '--help'], env as any, async () => new Response('{}'))
|
|
81
|
+
assert.equal(out.kind, 'help')
|
|
82
|
+
assert.match(out.text, /body-json example:/)
|
|
83
|
+
assert.match(out.text, /"field_id_seq":1003/)
|
|
84
|
+
assert.match(out.text, /"label":"投票描述"/)
|
|
85
|
+
assert.match(out.text, /"type":"textarea"/)
|
|
86
|
+
})
|
|
87
|
+
|
|
68
88
|
test('admin access-rule help gives update whitelist and assign-users body-json examples', async () => {
|
|
69
|
-
const
|
|
89
|
+
const create = await executeCLI('admin', ['access-rule', 'create', '--help'], env as any, async () => new Response('{}'))
|
|
90
|
+
assert.equal(create.kind, 'help')
|
|
91
|
+
assert.match(create.text, /Access rule/)
|
|
92
|
+
assert.match(create.text, /access-rule\.md/)
|
|
93
|
+
assert.match(create.text, /"user_scope":\[\{"type":"user","id":2188889901\}\]/)
|
|
94
|
+
assert.doesNotMatch(create.text, /"user_scope":\[\{"type":"member"/)
|
|
95
|
+
|
|
96
|
+
const update = await executeCLI('admin', ['access-rule', 'update', '--help'], env as any, async () => new Response('{}'))
|
|
70
97
|
assert.equal(update.kind, 'help')
|
|
71
98
|
assert.match(update.text, /--app-id/)
|
|
72
99
|
assert.match(update.text, /--table-id/)
|
|
73
100
|
assert.match(update.text, /--rule-id/)
|
|
74
101
|
assert.match(update.text, /"update":\["enabled"\]/)
|
|
102
|
+
assert.match(update.text, /"actions":\["view","add","edit"\]/)
|
|
75
103
|
|
|
76
|
-
const assign = await executeCLI('admin', ['access-rule', 'assign-users', '--help'],
|
|
104
|
+
const assign = await executeCLI('admin', ['access-rule', 'assign-users', '--help'], env as any, async () => new Response('{}'))
|
|
77
105
|
assert.equal(assign.kind, 'help')
|
|
78
106
|
assert.match(assign.text, /"update":\["user_scope"\]/)
|
|
79
|
-
assert.match(assign.text, /\{"update":\["user_scope"\],"options":\{"user_scope":\[\{"type":"
|
|
80
|
-
assert.doesNotMatch(assign.text, /"type":"
|
|
107
|
+
assert.match(assign.text, /\{"update":\["user_scope"\],"options":\{"user_scope":\[\{"type":"user","id":2188889901\}\]\}\}/)
|
|
108
|
+
assert.doesNotMatch(assign.text, /"type":"member"/)
|
|
81
109
|
assert.doesNotMatch(assign.text, /"users"/)
|
|
82
110
|
assert.doesNotMatch(assign.text, /"allowedUsers"/)
|
|
83
111
|
})
|
|
112
|
+
|
|
113
|
+
test('every body command help exposes readable TypeScript type paths', async () => {
|
|
114
|
+
for (const scope of ['admin', 'user'] as const satisfies readonly CommandScope[]) {
|
|
115
|
+
for (const command of listCommands(scope).filter((item) => item.requestType)) {
|
|
116
|
+
const out = await executeCLI(scope, [...command.commandPath, '--help'], env as any, async () => new Response('{}'))
|
|
117
|
+
assert.equal(out.kind, 'help')
|
|
118
|
+
assert.match(out.text, /^types:$/m, `${scope} ${command.commandPath.join(' ')} must expose types section`)
|
|
119
|
+
for (const hint of getTypeHints(command.requestType ?? '', env)) {
|
|
120
|
+
assert.match(out.text, new RegExp(`${hint.symbol}: ${hint.file}`.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')))
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
})
|
package/src/tests/upload.test.ts
CHANGED
|
@@ -1,132 +1,75 @@
|
|
|
1
1
|
import test from 'node:test'
|
|
2
2
|
import assert from 'node:assert/strict'
|
|
3
|
-
import fs from 'fs'
|
|
4
|
-
import os from 'os'
|
|
5
|
-
import path from 'path'
|
|
6
3
|
import { executeCLI } from '../runtime/execute.js'
|
|
4
|
+
import { CLIError } from '../runtime/errors.js'
|
|
7
5
|
|
|
8
6
|
const env = {
|
|
9
7
|
ARCUBASE_BASE_URL: 'https://arcubase.example.com',
|
|
10
8
|
ARCUBASE_ACCESS_TOKEN: 'tok',
|
|
9
|
+
ARCUBASE_SDK_ROOT: '/runtime/arcubase-sdk',
|
|
11
10
|
ARCUBASE_TRACE_ID: '',
|
|
12
11
|
ARCUBASE_SUBJECT_TYPE: '',
|
|
13
12
|
ARCUBASE_SUBJECT_ID: '',
|
|
14
13
|
ARCUBASE_TOKEN_EXPIRES_AT: '',
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
test('upload help
|
|
18
|
-
const out = await executeCLI('user', ['upload', '--help'],
|
|
16
|
+
test('upload module help exposes reset upload commands with injected SDK root', async () => {
|
|
17
|
+
const out = await executeCLI('user', ['upload', '--help'], env as any, async () => new Response('{}'))
|
|
19
18
|
assert.equal(out.kind, 'help')
|
|
20
|
-
assert.match(out.text, /
|
|
21
|
-
assert.match(out.text, /
|
|
19
|
+
assert.match(out.text, /commands:/)
|
|
20
|
+
assert.match(out.text, /token/)
|
|
21
|
+
assert.match(out.text, /preview/)
|
|
22
|
+
assert.match(out.text, /mobile/)
|
|
23
|
+
assert.match(out.text, /check-mobile/)
|
|
24
|
+
assert.doesNotMatch(out.text, /<local-file>/)
|
|
22
25
|
})
|
|
23
26
|
|
|
24
|
-
test('upload
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
const calls: Array<{ url: string; method?: string }> = []
|
|
30
|
-
|
|
31
|
-
const result = await executeCLI(
|
|
32
|
-
'user',
|
|
33
|
-
['upload', sourceFile],
|
|
34
|
-
env as any,
|
|
35
|
-
async (input, init) => {
|
|
36
|
-
const url = String(input)
|
|
37
|
-
calls.push({ url, method: init?.method })
|
|
38
|
-
if (url === 'https://arcubase.example.com/api/upload/token') {
|
|
39
|
-
return new Response(
|
|
40
|
-
JSON.stringify({
|
|
41
|
-
data: {
|
|
42
|
-
id: 123456,
|
|
43
|
-
mode: 'aliyun-oss',
|
|
44
|
-
token_data: {
|
|
45
|
-
dir: 'upload/123456/',
|
|
46
|
-
policy: 'policy',
|
|
47
|
-
accessid: 'accessid',
|
|
48
|
-
signature: 'signature',
|
|
49
|
-
host: 'https://uploads.example.com',
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
}),
|
|
53
|
-
{ status: 200, headers: { 'Content-Type': 'application/json' } },
|
|
54
|
-
)
|
|
55
|
-
}
|
|
56
|
-
if (url === 'https://uploads.example.com') {
|
|
57
|
-
return new Response('', { status: 200 })
|
|
58
|
-
}
|
|
59
|
-
throw new Error(`unexpected fetch url: ${url}`)
|
|
27
|
+
test('upload rejects local file shortcut', async () => {
|
|
28
|
+
await assert.rejects(
|
|
29
|
+
async () => {
|
|
30
|
+
await executeCLI('user', ['upload', './contract.pdf'], env as any, async () => new Response('{}'))
|
|
60
31
|
},
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
{ url: 'https://uploads.example.com', method: 'POST' },
|
|
67
|
-
])
|
|
68
|
-
assert.deepEqual(result.data, [
|
|
69
|
-
{
|
|
70
|
-
upload_id: 123456,
|
|
71
|
-
file: 'contract.pdf',
|
|
72
|
-
file_name: 'contract.pdf',
|
|
73
|
-
meta: {},
|
|
32
|
+
(error: unknown) => {
|
|
33
|
+
assert.ok(error instanceof CLIError)
|
|
34
|
+
assert.equal(error.code, 'UNKNOWN_COMMAND')
|
|
35
|
+
assert.match(error.message, /unknown command: upload \.\/contract\.pdf/)
|
|
36
|
+
return true
|
|
74
37
|
},
|
|
75
|
-
|
|
38
|
+
)
|
|
76
39
|
})
|
|
77
40
|
|
|
78
|
-
test('upload
|
|
79
|
-
const
|
|
80
|
-
const sourceFile = path.join(tempDir, 'contract.pdf')
|
|
81
|
-
fs.writeFileSync(sourceFile, 'fake pdf bytes')
|
|
82
|
-
|
|
83
|
-
const calls: Array<{ url: string; method?: string }> = []
|
|
84
|
-
|
|
41
|
+
test('upload token executes the explicit API command', async () => {
|
|
42
|
+
const calls: Array<{ url: string; method?: string; body?: string }> = []
|
|
85
43
|
const result = await executeCLI(
|
|
86
44
|
'user',
|
|
87
|
-
['upload',
|
|
45
|
+
['upload', 'token', '--body-json', '{"global":false}'],
|
|
88
46
|
env as any,
|
|
89
47
|
async (input, init) => {
|
|
90
48
|
const url = String(input)
|
|
91
|
-
calls.push({ url, method: init?.method })
|
|
49
|
+
calls.push({ url, method: init?.method, body: typeof init?.body === 'string' ? init.body : undefined })
|
|
92
50
|
if (url === 'https://arcubase.example.com/api/upload/token') {
|
|
93
51
|
return new Response(
|
|
94
52
|
JSON.stringify({
|
|
95
53
|
data: {
|
|
96
|
-
id:
|
|
97
|
-
mode: '
|
|
98
|
-
token_data: {
|
|
99
|
-
dir: 'upload/654321/',
|
|
100
|
-
policy: 'policy',
|
|
101
|
-
host: 'https://uploads.example.com/private-bucket',
|
|
102
|
-
x_amz_algorithm: 'AWS4-HMAC-SHA256',
|
|
103
|
-
x_amz_credential: 'test/20260504/us-east-1/s3/aws4_request',
|
|
104
|
-
x_amz_date: '20260504T010203Z',
|
|
105
|
-
x_amz_signature: 'abc123',
|
|
106
|
-
},
|
|
54
|
+
id: 123456,
|
|
55
|
+
mode: 'aliyun-oss',
|
|
56
|
+
token_data: { host: 'https://uploads.example.com' },
|
|
107
57
|
},
|
|
108
58
|
}),
|
|
109
59
|
{ status: 200, headers: { 'Content-Type': 'application/json' } },
|
|
110
60
|
)
|
|
111
61
|
}
|
|
112
|
-
if (url === 'https://uploads.example.com/private-bucket') {
|
|
113
|
-
return new Response('', { status: 200 })
|
|
114
|
-
}
|
|
115
62
|
throw new Error(`unexpected fetch url: ${url}`)
|
|
116
63
|
},
|
|
117
64
|
)
|
|
118
65
|
|
|
119
66
|
assert.equal(result.kind, 'result')
|
|
120
|
-
assert.deepEqual(calls, [
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
upload_id: 654321,
|
|
127
|
-
file: 'contract.pdf',
|
|
128
|
-
file_name: 'contract.pdf',
|
|
129
|
-
meta: {},
|
|
67
|
+
assert.deepEqual(calls, [{ url: 'https://arcubase.example.com/api/upload/token', method: 'POST', body: '{"global":false}' }])
|
|
68
|
+
assert.deepEqual(result.data, {
|
|
69
|
+
data: {
|
|
70
|
+
id: 123456,
|
|
71
|
+
mode: 'aliyun-oss',
|
|
72
|
+
token_data: { host: 'https://uploads.example.com' },
|
|
130
73
|
},
|
|
131
|
-
|
|
74
|
+
})
|
|
132
75
|
})
|