@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.
Files changed (74) hide show
  1. package/bundle/arcubase-admin.mjs +1041 -5061
  2. package/bundle/arcubase.mjs +1041 -5061
  3. package/dist/generated/command_registry.generated.d.ts +693 -2812
  4. package/dist/generated/command_registry.generated.d.ts.map +1 -1
  5. package/dist/generated/command_registry.generated.js +812 -4100
  6. package/dist/generated/type_index.generated.d.ts +8 -397
  7. package/dist/generated/type_index.generated.d.ts.map +1 -1
  8. package/dist/generated/type_index.generated.js +8 -403
  9. package/dist/generated/zod_registry.generated.d.ts +33 -261
  10. package/dist/generated/zod_registry.generated.d.ts.map +1 -1
  11. package/dist/generated/zod_registry.generated.js +36 -365
  12. package/dist/runtime/body_loader.d.ts.map +1 -1
  13. package/dist/runtime/body_loader.js +12 -0
  14. package/dist/runtime/command_registry.d.ts +3 -21
  15. package/dist/runtime/command_registry.d.ts.map +1 -1
  16. package/dist/runtime/command_registry.js +17 -23
  17. package/dist/runtime/env.d.ts +0 -2
  18. package/dist/runtime/env.d.ts.map +1 -1
  19. package/dist/runtime/env.js +0 -2
  20. package/dist/runtime/execute.d.ts +3 -3
  21. package/dist/runtime/execute.d.ts.map +1 -1
  22. package/dist/runtime/execute.js +53 -157
  23. package/dist/runtime/http.d.ts +1 -1
  24. package/dist/runtime/http.d.ts.map +1 -1
  25. package/dist/runtime/http.js +1 -5
  26. package/dist/runtime/upload.js +3 -3
  27. package/dist/runtime/zod_registry.d.ts.map +1 -1
  28. package/dist/runtime/zod_registry.js +7 -24
  29. package/dist/tests/bootstrap.test.js +1 -12
  30. package/dist/tests/command_registry.test.js +81 -28
  31. package/dist/tests/execute_validation.test.js +175 -510
  32. package/dist/tests/help.test.js +39 -63
  33. package/dist/tests/upload.test.js +4 -5
  34. package/dist/tests/zod_registry.test.js +0 -10
  35. package/package.json +1 -1
  36. package/sdk-dist/docs/runtime-reference/README.md +20 -36
  37. package/sdk-dist/docs/runtime-reference/entity-schema/README.md +4 -5
  38. package/sdk-dist/docs/runtime-reference/entity-schema/linkto.md +3 -3
  39. package/sdk-dist/docs/runtime-reference/entity-schema/query.md +3 -3
  40. package/sdk-dist/docs/runtime-reference/entity-schema/relation.md +3 -3
  41. package/sdk-dist/docs/runtime-reference/entity-schema/relationfield.md +3 -3
  42. package/sdk-dist/docs/runtime-reference/entity-schema/subform.md +2 -2
  43. package/sdk-dist/docs/runtime-reference/entity-schema.md +4 -4
  44. package/sdk-dist/docs/runtime-reference/examples/README.md +2 -2
  45. package/sdk-dist/docs/runtime-reference/examples/oms-01/README.md +12 -12
  46. package/sdk-dist/docs/runtime-reference/examples/oms-01/app-overview.md +1 -1
  47. package/sdk-dist/docs/runtime-reference/row-crud.md +11 -32
  48. package/sdk-dist/docs/runtime-reference/search-and-bulk-actions.md +16 -47
  49. package/sdk-dist/docs/runtime-reference/table-lifecycle.md +11 -27
  50. package/sdk-dist/docs/runtime-reference/uploads.md +1 -1
  51. package/sdk-dist/docs/runtime-reference/workflow/README.md +19 -0
  52. package/sdk-dist/generated/command_registry.generated.ts +814 -4102
  53. package/sdk-dist/generated/type_index.generated.ts +8 -403
  54. package/sdk-dist/generated/zod_registry.generated.ts +44 -500
  55. package/sdk-dist/types/app.ts +3 -10
  56. package/sdk-dist/types/common.ts +7 -0
  57. package/sdk-dist/types/global-action.ts +2 -1
  58. package/src/generated/command_registry.generated.ts +814 -4102
  59. package/src/generated/type_index.generated.ts +8 -403
  60. package/src/generated/zod_registry.generated.ts +44 -500
  61. package/src/runtime/body_loader.ts +11 -0
  62. package/src/runtime/command_registry.ts +22 -44
  63. package/src/runtime/env.ts +0 -4
  64. package/src/runtime/execute.ts +58 -168
  65. package/src/runtime/http.ts +2 -6
  66. package/src/runtime/upload.ts +3 -3
  67. package/src/runtime/zod_registry.ts +7 -25
  68. package/src/tests/bootstrap.test.ts +1 -12
  69. package/src/tests/command_registry.test.ts +81 -31
  70. package/src/tests/execute_validation.test.ts +199 -640
  71. package/src/tests/help.test.ts +36 -70
  72. package/src/tests/upload.test.ts +4 -5
  73. package/src/tests/zod_registry.test.ts +0 -13
  74. package/sdk-dist/docs/runtime-reference/app-discovery.md +0 -84
@@ -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 prints modules', async () => {
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, /app/)
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 does not require runtime env', async () => {
22
- const out = await executeCLI('admin', ['--help'], undefined, async () => new Response('{}'))
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, /modules:/)
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 prints create-entity command', async () => {
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, /create-entity/)
31
- assert.match(out.text, /inventory/)
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('module help does not require runtime env', async () => {
35
- const out = await executeCLI('user', ['rows', '--help'], undefined, async () => new Response('{}'))
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('command help without request body can still point to lifecycle and examples docs', async () => {
68
- const out = await executeCLI('admin', ['entity', 'admin-get-entity-info', '--help'], undefined, async () => new Response('{}'))
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, /list all accessible apps and each app entity list/)
78
- assert.match(out.text, /use this before asking the user for app_id/)
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
  })
@@ -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