@carthooks/arcubase-cli 0.1.6 → 0.1.8
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 +1041 -5061
- package/bundle/arcubase.mjs +1041 -5061
- package/dist/generated/command_registry.generated.d.ts +693 -2812
- package/dist/generated/command_registry.generated.d.ts.map +1 -1
- package/dist/generated/command_registry.generated.js +812 -4100
- package/dist/generated/type_index.generated.d.ts +8 -397
- package/dist/generated/type_index.generated.d.ts.map +1 -1
- package/dist/generated/type_index.generated.js +8 -403
- package/dist/generated/zod_registry.generated.d.ts +33 -261
- package/dist/generated/zod_registry.generated.d.ts.map +1 -1
- package/dist/generated/zod_registry.generated.js +36 -365
- package/dist/runtime/body_loader.d.ts.map +1 -1
- package/dist/runtime/body_loader.js +12 -0
- package/dist/runtime/command_registry.d.ts +3 -21
- package/dist/runtime/command_registry.d.ts.map +1 -1
- package/dist/runtime/command_registry.js +17 -23
- package/dist/runtime/env.d.ts +0 -2
- package/dist/runtime/env.d.ts.map +1 -1
- package/dist/runtime/env.js +0 -2
- package/dist/runtime/execute.d.ts +3 -3
- package/dist/runtime/execute.d.ts.map +1 -1
- package/dist/runtime/execute.js +53 -157
- package/dist/runtime/http.d.ts +1 -1
- package/dist/runtime/http.d.ts.map +1 -1
- package/dist/runtime/http.js +1 -5
- package/dist/runtime/upload.js +3 -3
- package/dist/runtime/zod_registry.d.ts.map +1 -1
- package/dist/runtime/zod_registry.js +7 -24
- package/dist/tests/bootstrap.test.js +1 -12
- package/dist/tests/command_registry.test.js +81 -28
- package/dist/tests/execute_validation.test.js +175 -510
- package/dist/tests/help.test.js +39 -63
- package/dist/tests/upload.test.js +4 -5
- package/dist/tests/zod_registry.test.js +0 -10
- package/package.json +1 -1
- package/sdk-dist/docs/runtime-reference/README.md +20 -36
- package/sdk-dist/docs/runtime-reference/entity-schema/README.md +4 -5
- package/sdk-dist/docs/runtime-reference/entity-schema/linkto.md +3 -3
- 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/subform.md +2 -2
- package/sdk-dist/docs/runtime-reference/entity-schema.md +4 -4
- package/sdk-dist/docs/runtime-reference/examples/README.md +2 -2
- package/sdk-dist/docs/runtime-reference/examples/oms-01/README.md +12 -12
- package/sdk-dist/docs/runtime-reference/examples/oms-01/app-overview.md +1 -1
- package/sdk-dist/docs/runtime-reference/row-crud.md +11 -32
- package/sdk-dist/docs/runtime-reference/search-and-bulk-actions.md +16 -47
- package/sdk-dist/docs/runtime-reference/table-lifecycle.md +11 -27
- package/sdk-dist/docs/runtime-reference/uploads.md +1 -1
- package/sdk-dist/docs/runtime-reference/workflow/README.md +19 -0
- package/sdk-dist/generated/command_registry.generated.ts +814 -4102
- package/sdk-dist/generated/type_index.generated.ts +8 -403
- package/sdk-dist/generated/zod_registry.generated.ts +44 -500
- package/sdk-dist/types/app.ts +3 -10
- package/sdk-dist/types/common.ts +7 -0
- package/sdk-dist/types/global-action.ts +2 -1
- package/src/generated/command_registry.generated.ts +814 -4102
- package/src/generated/type_index.generated.ts +8 -403
- package/src/generated/zod_registry.generated.ts +44 -500
- package/src/runtime/body_loader.ts +11 -0
- package/src/runtime/command_registry.ts +22 -44
- package/src/runtime/env.ts +0 -4
- package/src/runtime/execute.ts +58 -168
- package/src/runtime/http.ts +2 -6
- package/src/runtime/upload.ts +3 -3
- package/src/runtime/zod_registry.ts +7 -25
- package/src/tests/bootstrap.test.ts +1 -12
- package/src/tests/command_registry.test.ts +81 -31
- package/src/tests/execute_validation.test.ts +199 -640
- package/src/tests/help.test.ts +36 -70
- package/src/tests/upload.test.ts +4 -5
- package/src/tests/zod_registry.test.ts +0 -13
- package/sdk-dist/docs/runtime-reference/app-discovery.md +0 -84
package/src/tests/help.test.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { executeCLI } from '../runtime/execute.js'
|
|
|
4
4
|
|
|
5
5
|
const env = {
|
|
6
6
|
ARCUBASE_BASE_URL: 'https://arcubase.example.com',
|
|
7
|
-
ARCUBASE_TENANT_ID: 'tenant_1',
|
|
8
7
|
ARCUBASE_ACCESS_TOKEN: 'tok',
|
|
9
8
|
ARCUBASE_TRACE_ID: '',
|
|
10
9
|
ARCUBASE_SUBJECT_TYPE: '',
|
|
@@ -12,89 +11,56 @@ const env = {
|
|
|
12
11
|
ARCUBASE_TOKEN_EXPIRES_AT: '',
|
|
13
12
|
}
|
|
14
13
|
|
|
15
|
-
test('root help
|
|
14
|
+
test('admin root help exposes reset surface nouns only', async () => {
|
|
16
15
|
const out = await executeCLI('admin', ['--help'], env as any, async () => new Response('{}'))
|
|
17
16
|
assert.equal(out.kind, 'help')
|
|
18
|
-
assert.match(out.text, /
|
|
17
|
+
assert.match(out.text, /\bapp\b/)
|
|
18
|
+
assert.match(out.text, /\btable\b/)
|
|
19
|
+
assert.match(out.text, /\baccess-rule\b/)
|
|
20
|
+
assert.match(out.text, /\bworkflow\b/)
|
|
21
|
+
assert.doesNotMatch(out.text, /\bentity\b/)
|
|
22
|
+
assert.doesNotMatch(out.text, /\bingress\b/)
|
|
19
23
|
})
|
|
20
24
|
|
|
21
|
-
test('root help
|
|
22
|
-
const out = await executeCLI('
|
|
25
|
+
test('user root help exposes reset surface nouns only', async () => {
|
|
26
|
+
const out = await executeCLI('user', ['--help'], env as any, async () => new Response('{}'))
|
|
23
27
|
assert.equal(out.kind, 'help')
|
|
24
|
-
assert.match(out.text, /
|
|
28
|
+
assert.match(out.text, /\bentry\b/)
|
|
29
|
+
assert.match(out.text, /\btable\b/)
|
|
30
|
+
assert.match(out.text, /\brow\b/)
|
|
31
|
+
assert.match(out.text, /\bupload\b/)
|
|
32
|
+
assert.match(out.text, /\bprofile\b/)
|
|
33
|
+
assert.match(out.text, /\bworkflow\b/)
|
|
34
|
+
assert.doesNotMatch(out.text, /\bglobal-action\b/)
|
|
35
|
+
assert.doesNotMatch(out.text, /\bentity\b/)
|
|
25
36
|
})
|
|
26
37
|
|
|
27
|
-
test('module help
|
|
38
|
+
test('admin module help lists reset app commands', async () => {
|
|
28
39
|
const out = await executeCLI('admin', ['app', '--help'], env as any, async () => new Response('{}'))
|
|
29
40
|
assert.equal(out.kind, 'help')
|
|
30
|
-
assert.match(out.text, /
|
|
31
|
-
assert.match(out.text, /
|
|
41
|
+
assert.match(out.text, /list/)
|
|
42
|
+
assert.match(out.text, /get/)
|
|
43
|
+
assert.match(out.text, /create/)
|
|
44
|
+
assert.match(out.text, /update/)
|
|
45
|
+
assert.match(out.text, /delete/)
|
|
46
|
+
assert.doesNotMatch(out.text, /list-apps/)
|
|
47
|
+
assert.doesNotMatch(out.text, /create-entity/)
|
|
32
48
|
})
|
|
33
49
|
|
|
34
|
-
test('
|
|
35
|
-
const out = await executeCLI('user', ['
|
|
36
|
-
assert.equal(out.kind, 'help')
|
|
37
|
-
assert.match(out.text, /delete-entity-row/)
|
|
38
|
-
})
|
|
39
|
-
|
|
40
|
-
test('command help prints normalized external endpoint', async () => {
|
|
41
|
-
const out = await executeCLI('admin', ['app-entity-share', 'get-app-entity-shares', '--help'], env as any, async () => new Response('{}'))
|
|
42
|
-
assert.equal(out.kind, 'help')
|
|
43
|
-
assert.match(out.text, /endpoint: \/apps\/:app_id\/entity-shares/)
|
|
44
|
-
assert.doesNotMatch(out.text, /endpoint: \/api\//)
|
|
45
|
-
})
|
|
46
|
-
|
|
47
|
-
test('command help does not require runtime env', async () => {
|
|
48
|
-
const out = await executeCLI('user', ['rows', 'delete-entity-row', '--help'], undefined, async () => new Response('{}'))
|
|
49
|
-
assert.equal(out.kind, 'help')
|
|
50
|
-
assert.match(out.text, /endpoint: \/entity\/:app_id\/:entity_id\/delete-item/)
|
|
51
|
-
})
|
|
52
|
-
|
|
53
|
-
test('command help prints runtime doc hints for request body commands', async () => {
|
|
54
|
-
const out = await executeCLI('user', ['rows', 'update-entity-row', '--help'], undefined, async () => new Response('{}'))
|
|
55
|
-
assert.equal(out.kind, 'help')
|
|
56
|
-
assert.match(out.text, /docs:/)
|
|
57
|
-
assert.match(out.text, /\/opt\/arcubase-sdk\/docs\/runtime-reference\/row-crud\.md/)
|
|
58
|
-
assert.match(out.text, /\/opt\/arcubase-sdk\/docs\/runtime-reference\/examples\/README\.md/)
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
test('command help prints query-file support', async () => {
|
|
62
|
-
const out = await executeCLI('user', ['rows', 'query-entity', '--help'], undefined, async () => new Response('{}'))
|
|
50
|
+
test('user row help replaces workflow row commands', async () => {
|
|
51
|
+
const out = await executeCLI('user', ['row', 'query', '--help'], undefined, async () => new Response('{}'))
|
|
63
52
|
assert.equal(out.kind, 'help')
|
|
53
|
+
assert.match(out.text, /body: .*--body-file or --body-json/)
|
|
64
54
|
assert.match(out.text, /query flags: --query-file/)
|
|
55
|
+
assert.doesNotMatch(out.text, /workflow query-entity/)
|
|
56
|
+
assert.doesNotMatch(out.text, /--entity-id/)
|
|
65
57
|
})
|
|
66
58
|
|
|
67
|
-
test('
|
|
68
|
-
const out = await executeCLI('admin', ['
|
|
69
|
-
assert.equal(out.kind, 'help')
|
|
70
|
-
assert.match(out.text, /\/opt\/arcubase-sdk\/docs\/runtime-reference\/table-lifecycle\.md/)
|
|
71
|
-
assert.match(out.text, /\/opt\/arcubase-sdk\/docs\/runtime-reference\/examples\/README\.md/)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
test('app inventory help explains app discovery purpose', async () => {
|
|
75
|
-
const out = await executeCLI('admin', ['app', 'inventory', '--help'], undefined, async () => new Response('{}'))
|
|
59
|
+
test('admin table update-schema help uses table nouns and body-json', async () => {
|
|
60
|
+
const out = await executeCLI('admin', ['table', 'update-schema', '--help'], undefined, async () => new Response('{}'))
|
|
76
61
|
assert.equal(out.kind, 'help')
|
|
77
|
-
assert.match(out.text, /
|
|
78
|
-
assert.match(out.text, /
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
test('admin-save-entity help prints row-operation next step', async () => {
|
|
82
|
-
const out = await executeCLI('admin', ['entity', 'admin-save-entity', '--help'], undefined, async () => new Response('{}'))
|
|
83
|
-
assert.equal(out.kind, 'help')
|
|
84
|
-
assert.match(out.text, /switch to arcubase for row operations/)
|
|
85
|
-
assert.match(out.text, /arcubase rows insert-entity/)
|
|
86
|
-
assert.match(out.text, /arcubase rows query-entity/)
|
|
87
|
-
})
|
|
88
|
-
|
|
89
|
-
test('wrong admin workflow command points to arcubase row command', async () => {
|
|
90
|
-
await assert.rejects(
|
|
91
|
-
() => executeCLI('admin', ['workflow', 'insert-entity'], env as any, async () => new Response('{}')),
|
|
92
|
-
(error: any) => {
|
|
93
|
-
assert.equal(error.code, 'UNKNOWN_COMMAND')
|
|
94
|
-
assert.equal(error.details?.reason, 'workflow insert-entity is not a valid command group; use arcubase rows for row operations')
|
|
95
|
-
assert.equal(error.details?.hint, 'use: arcubase rows insert-entity')
|
|
96
|
-
assert.ok((error.details?.suggestions ?? []).includes('arcubase rows insert-entity'))
|
|
97
|
-
return true
|
|
98
|
-
}
|
|
99
|
-
)
|
|
62
|
+
assert.match(out.text, /body: .*--body-file or --body-json/)
|
|
63
|
+
assert.match(out.text, /path flags: --app-id, --table-id/)
|
|
64
|
+
assert.doesNotMatch(out.text, /--entity-id/)
|
|
65
|
+
assert.doesNotMatch(out.text, /admin-save-entity/)
|
|
100
66
|
})
|
package/src/tests/upload.test.ts
CHANGED
|
@@ -7,7 +7,6 @@ import { executeCLI } from '../runtime/execute.js'
|
|
|
7
7
|
|
|
8
8
|
const env = {
|
|
9
9
|
ARCUBASE_BASE_URL: 'https://arcubase.example.com',
|
|
10
|
-
ARCUBASE_TENANT_ID: 'tenant_1',
|
|
11
10
|
ARCUBASE_ACCESS_TOKEN: 'tok',
|
|
12
11
|
ARCUBASE_TRACE_ID: '',
|
|
13
12
|
ARCUBASE_SUBJECT_TYPE: '',
|
|
@@ -36,7 +35,7 @@ test('upload returns file field value array for row payloads', async () => {
|
|
|
36
35
|
async (input, init) => {
|
|
37
36
|
const url = String(input)
|
|
38
37
|
calls.push({ url, method: init?.method })
|
|
39
|
-
if (url === 'https://arcubase.example.com/upload/token') {
|
|
38
|
+
if (url === 'https://arcubase.example.com/api/upload/token') {
|
|
40
39
|
return new Response(
|
|
41
40
|
JSON.stringify({
|
|
42
41
|
data: {
|
|
@@ -63,7 +62,7 @@ test('upload returns file field value array for row payloads', async () => {
|
|
|
63
62
|
|
|
64
63
|
assert.equal(result.kind, 'result')
|
|
65
64
|
assert.deepEqual(calls, [
|
|
66
|
-
{ url: 'https://arcubase.example.com/upload/token', method: 'POST' },
|
|
65
|
+
{ url: 'https://arcubase.example.com/api/upload/token', method: 'POST' },
|
|
67
66
|
{ url: 'https://uploads.example.com', method: 'POST' },
|
|
68
67
|
])
|
|
69
68
|
assert.deepEqual(result.data, [
|
|
@@ -90,7 +89,7 @@ test('upload supports s3-post-policy tokens', async () => {
|
|
|
90
89
|
async (input, init) => {
|
|
91
90
|
const url = String(input)
|
|
92
91
|
calls.push({ url, method: init?.method })
|
|
93
|
-
if (url === 'https://arcubase.example.com/upload/token') {
|
|
92
|
+
if (url === 'https://arcubase.example.com/api/upload/token') {
|
|
94
93
|
return new Response(
|
|
95
94
|
JSON.stringify({
|
|
96
95
|
data: {
|
|
@@ -119,7 +118,7 @@ test('upload supports s3-post-policy tokens', async () => {
|
|
|
119
118
|
|
|
120
119
|
assert.equal(result.kind, 'result')
|
|
121
120
|
assert.deepEqual(calls, [
|
|
122
|
-
{ url: 'https://arcubase.example.com/upload/token', method: 'POST' },
|
|
121
|
+
{ url: 'https://arcubase.example.com/api/upload/token', method: 'POST' },
|
|
123
122
|
{ url: 'https://uploads.example.com/private-bucket', method: 'POST' },
|
|
124
123
|
])
|
|
125
124
|
assert.deepEqual(result.data, [
|
|
@@ -14,19 +14,6 @@ test('EntityQueryReqVO schema exists', () => {
|
|
|
14
14
|
assert.ok(getBodySchema('EntityQueryReqVO'))
|
|
15
15
|
})
|
|
16
16
|
|
|
17
|
-
test('inline request type schema exists', () => {
|
|
18
|
-
assert.ok(getBodySchema('{ name: string }'))
|
|
19
|
-
})
|
|
20
|
-
|
|
21
|
-
test('intersection request type schema exists', () => {
|
|
22
|
-
assert.ok(getBodySchema('EntityImportReqVO & EntityImportActionVO'))
|
|
23
|
-
})
|
|
24
|
-
|
|
25
|
-
test('manual notification override schema exists', () => {
|
|
26
|
-
assert.ok(getBodySchema('EntityUpdateNotificationsReqVO'))
|
|
27
|
-
assert.equal(getUnsupportedBodySchemaReason('EntityUpdateNotificationsReqVO'), null)
|
|
28
|
-
})
|
|
29
|
-
|
|
30
17
|
test('unknown request type has no schema and no unsupported reason', () => {
|
|
31
18
|
assert.equal(getBodySchema('DefinitelyMissingReqVO'), null)
|
|
32
19
|
assert.equal(getUnsupportedBodySchemaReason('DefinitelyMissingReqVO'), null)
|
|
@@ -1,84 +0,0 @@
|
|
|
1
|
-
# App Discovery
|
|
2
|
-
|
|
3
|
-
Arcubase is a headless app platform.
|
|
4
|
-
|
|
5
|
-
Core concepts:
|
|
6
|
-
|
|
7
|
-
- app: the top-level business container
|
|
8
|
-
- entity: a table inside an app
|
|
9
|
-
- row: a record inside an entity
|
|
10
|
-
|
|
11
|
-
Hard rule:
|
|
12
|
-
|
|
13
|
-
- every operation happens inside one app
|
|
14
|
-
- find the target app before schema work or row work
|
|
15
|
-
|
|
16
|
-
Do not ask the user for `app_id` unless:
|
|
17
|
-
|
|
18
|
-
- there is no matching app
|
|
19
|
-
- there are multiple matching apps and you cannot resolve the ambiguity
|
|
20
|
-
|
|
21
|
-
Do not ask the user for `entity_id` when creating a new table.
|
|
22
|
-
Entity ids are assigned by Arcubase.
|
|
23
|
-
|
|
24
|
-
## First command
|
|
25
|
-
|
|
26
|
-
Use the inventory command first:
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
arcubase-admin app inventory
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
It returns:
|
|
33
|
-
|
|
34
|
-
- every accessible app
|
|
35
|
-
- each app id and app name
|
|
36
|
-
- each app entity list
|
|
37
|
-
|
|
38
|
-
Use it to resolve:
|
|
39
|
-
|
|
40
|
-
- which existing app matches the user's business name
|
|
41
|
-
- whether the table already exists
|
|
42
|
-
- whether the user is talking about the wrong app
|
|
43
|
-
|
|
44
|
-
## Resolution order
|
|
45
|
-
|
|
46
|
-
1. if the user gives an app name, run `arcubase-admin app inventory`
|
|
47
|
-
2. match the app by name yourself
|
|
48
|
-
3. if the match is unique, continue without asking for `app_id`
|
|
49
|
-
4. if there is no match, ask which app to use
|
|
50
|
-
5. if there are multiple matches, ask the user to choose by app name
|
|
51
|
-
|
|
52
|
-
## Existing app flow
|
|
53
|
-
|
|
54
|
-
If the app already exists:
|
|
55
|
-
|
|
56
|
-
1. run `arcubase-admin app inventory`
|
|
57
|
-
2. find the app id
|
|
58
|
-
3. inspect the existing entity list
|
|
59
|
-
4. decide whether to create a new entity or write rows into an existing entity
|
|
60
|
-
|
|
61
|
-
## Per-employee permission scenarios
|
|
62
|
-
|
|
63
|
-
If the business requirement says different digiEmployees should have different Arcubase abilities:
|
|
64
|
-
|
|
65
|
-
- first resolve the app with `arcubase-admin app inventory`
|
|
66
|
-
- then resolve digiEmployee mappings with `arcubase.get_digiemployee_users`
|
|
67
|
-
- then continue with ingress-oriented admin configuration
|
|
68
|
-
|
|
69
|
-
Do not treat this as a reason to ask the user for:
|
|
70
|
-
|
|
71
|
-
- `app_id`
|
|
72
|
-
- `entity_id`
|
|
73
|
-
- `tenantUserId`
|
|
74
|
-
|
|
75
|
-
Do not treat this as a primary app-sharing or permission-template discovery task.
|
|
76
|
-
|
|
77
|
-
## New app flow
|
|
78
|
-
|
|
79
|
-
If the user clearly wants a brand-new app:
|
|
80
|
-
|
|
81
|
-
1. create the app with `arcubase-admin app create-app-by-tenants`
|
|
82
|
-
2. create the entity shell
|
|
83
|
-
3. save the schema
|
|
84
|
-
4. switch to `arcubase rows ...` for row operations
|