@carthooks/arcubase-cli 0.1.21 → 0.1.22

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 (56) hide show
  1. package/bundle/arcubase-admin.mjs +1297 -79
  2. package/bundle/arcubase.mjs +1297 -79
  3. package/dist/generated/command_registry.generated.d.ts +285 -0
  4. package/dist/generated/command_registry.generated.d.ts.map +1 -1
  5. package/dist/generated/command_registry.generated.js +395 -0
  6. package/dist/generated/help_examples.generated.d.ts +77 -0
  7. package/dist/generated/help_examples.generated.d.ts.map +1 -1
  8. package/dist/generated/help_examples.generated.js +122 -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 +280 -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 +15 -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 +42 -0
  32. package/sdk-dist/docs/runtime-reference/widgets.md +40 -0
  33. package/sdk-dist/generated/command_registry.generated.ts +395 -0
  34. package/sdk-dist/generated/help_examples.generated.ts +122 -0
  35. package/sdk-dist/generated/type_index.generated.ts +42 -1
  36. package/sdk-dist/generated/zod_registry.generated.ts +56 -0
  37. package/sdk-dist/types/app.ts +37 -1
  38. package/sdk-dist/types/common.ts +77 -35
  39. package/sdk-dist/types/index.ts +1 -1
  40. package/sdk-dist/types/public-link.ts +10 -0
  41. package/sdk-dist/types/shared-link.ts +16 -1
  42. package/sdk-dist/types/user-action.ts +1 -43
  43. package/src/generated/command_registry.generated.ts +395 -0
  44. package/src/generated/help_examples.generated.ts +122 -0
  45. package/src/generated/type_index.generated.ts +42 -1
  46. package/src/generated/zod_registry.generated.ts +56 -0
  47. package/src/runtime/dev_sdk_gen.ts +360 -0
  48. package/src/runtime/execute.ts +325 -1
  49. package/src/runtime/zod_registry.ts +58 -0
  50. package/src/tests/command_registry.test.ts +20 -2
  51. package/src/tests/dev_sdk_gen.test.ts +226 -0
  52. package/src/tests/docs_readability.test.ts +15 -0
  53. package/src/tests/execute_validation.test.ts +129 -0
  54. package/src/tests/help.test.ts +70 -0
  55. package/sdk-dist/api/user/copilot.ts +0 -20
  56. package/sdk-dist/types/copilot.ts +0 -34
@@ -11,6 +11,7 @@ import { resolveArcubaseSDKPath } from './paths.js'
11
11
  import { compileCSVImportMapping, compileExcelImportMapping, type CompiledEntityImportConfig } from './entity_import_mapping.js'
12
12
  import { uploadLocalFileWithToken } from './local_upload.js'
13
13
  import { renderCommandHelpExamples } from './help_examples.js'
14
+ import { generateArcubaseProjectSDK } from './dev_sdk_gen.js'
14
15
 
15
16
  export type CommandExecutionSummary = {
16
17
  scope: CommandScope
@@ -24,7 +25,8 @@ export type CommandExecutionSummary = {
24
25
 
25
26
  export function renderRootHelp(scope: CommandScope): string {
26
27
  const binary = scope === 'admin' ? 'arcubase-admin' : 'arcubase'
27
- const items = listModules(scope).map((item) => ` - ${item}`)
28
+ const modules = scope === 'admin' ? [...listModules(scope), 'dev'].sort() : listModules(scope)
29
+ const items = modules.map((item) => ` - ${item}`)
28
30
  return [
29
31
  `${binary} <command> [subcommand] [flags]`,
30
32
  '',
@@ -38,6 +40,17 @@ export function renderRootHelp(scope: CommandScope): string {
38
40
  ].join('\n')
39
41
  }
40
42
 
43
+ function renderDevModuleHelp(): string {
44
+ return [
45
+ 'arcubase-admin dev <command> [flags]',
46
+ '',
47
+ 'developer commands for local project tooling',
48
+ '',
49
+ 'commands:',
50
+ ' - sdk-gen',
51
+ ].join('\n')
52
+ }
53
+
41
54
  export function renderModuleHelp(scope: CommandScope, moduleName: string, env: EnvInput = process.env): string {
42
55
  const items = listModuleCommands(scope, moduleName)
43
56
  if (items.length === 0) {
@@ -251,6 +264,44 @@ function getFixedValue(value: object): string | undefined {
251
264
  return typeof fixedValue === 'string' ? fixedValue : undefined
252
265
  }
253
266
 
267
+ function isDevSDKGenCommand(scope: CommandScope, moduleName: string | undefined, commandName: string | undefined): boolean {
268
+ return scope === 'admin' && moduleName === 'dev' && commandName === 'sdk-gen'
269
+ }
270
+
271
+ function validateDevSDKGenFlags(flags: Record<string, string | boolean>) {
272
+ const allowed = new Set(['help', 'app-id', 'out'])
273
+ for (const flag of Object.keys(flags)) {
274
+ if (!allowed.has(flag)) {
275
+ throw new CLIError('UNKNOWN_FLAG', `unknown flag --${flag} for dev sdk-gen`, 2, {
276
+ operation: 'dev sdk-gen',
277
+ hint: 'run dev sdk-gen --help for the supported flags',
278
+ })
279
+ }
280
+ }
281
+ }
282
+
283
+ function renderDevSDKGenHelp(): string {
284
+ return [
285
+ 'arcubase-admin dev sdk-gen',
286
+ 'method: local generator',
287
+ 'endpoint: /apps/:app_id',
288
+ 'body: none',
289
+ 'path flags: --app-id',
290
+ 'flags: --out',
291
+ '',
292
+ 'generates typed TypeScript source for the current Web project',
293
+ '',
294
+ 'example:',
295
+ ' - arcubase-admin dev sdk-gen --app-id <app_id> --out src/arcubase',
296
+ '',
297
+ 'requirements:',
298
+ ' - missing field.key values are initialized through admin auth as field<id>',
299
+ ' - existing field.key values must be stable unique TypeScript identifiers',
300
+ ' - entity property names use entity.key when available, otherwise a stable entity<id> fallback',
301
+ ' - generated code accepts createArcubaseSdk({ baseURL, accessToken, refreshToken })',
302
+ ].join('\n')
303
+ }
304
+
254
305
  function resolveEndpoint(command: NonNullable<ReturnType<typeof findCommand>>, flags: Record<string, string | boolean>): string {
255
306
  let resolved: string = command.endpoint
256
307
  for (const mapping of command.pathParams) {
@@ -657,6 +708,14 @@ function isAccessRuleCommand(scope: CommandScope, command: NonNullable<ReturnTyp
657
708
  return scope === 'admin' && command.commandPath[0] === 'access-rule'
658
709
  }
659
710
 
711
+ function isWidgetPreviewDataCommand(scope: CommandScope, command: NonNullable<ReturnType<typeof findCommand>>): boolean {
712
+ return scope === 'admin' && command.commandPath[0] === 'widget' && command.commandPath[1] === 'preview-data'
713
+ }
714
+
715
+ function isWidgetUpdateCommand(scope: CommandScope, command: NonNullable<ReturnType<typeof findCommand>>): boolean {
716
+ return scope === 'admin' && command.commandPath[0] === 'widget' && command.commandPath[1] === 'update'
717
+ }
718
+
660
719
  function isAccessRuleUpdateCommand(scope: CommandScope, command: NonNullable<ReturnType<typeof findCommand>>): boolean {
661
720
  return scope === 'admin' && command.commandPath[0] === 'access-rule' && command.commandPath[1] === 'update'
662
721
  }
@@ -966,6 +1025,45 @@ function buildBodyGuidance(requestType: string): Pick<CLIErrorDetails, 'retryToo
966
1025
  ],
967
1026
  }
968
1027
  }
1028
+ if (requestType === 'AppPreviewWidgetsDataReqVO') {
1029
+ return {
1030
+ shapeHint: {
1031
+ dataSource: 2188891001,
1032
+ dataSourceType: 'entity',
1033
+ dimensions: [{ IsField: true, FieldID: 1004, Name: 'Source' }],
1034
+ measures: [{ FieldID: 1005, Aggregate: 'sum', Name: 'Total Amount' }],
1035
+ },
1036
+ commonMistakes: [
1037
+ 'widget preview-data body is the chart query itself',
1038
+ 'do not send a saved widget object with id, entity_id, or options',
1039
+ 'field dimensions with FieldID must set IsField:true',
1040
+ 'unsupported aggregates are invalid; use sum, avg, max, min, count, or distinct_count',
1041
+ ],
1042
+ suggestions: [
1043
+ 'retry with: arcubase-admin widget preview-data --app-id <app_id> --body-json \'{"dataSource":<table_id>,"dataSourceType":"entity","dimensions":[{"IsField":true,"FieldID":<field_id>,"Name":"Source"}],"measures":[{"FieldID":<number_field_id>,"Aggregate":"sum","Name":"Total Amount"}]}\'',
1044
+ ],
1045
+ }
1046
+ }
1047
+ if (requestType === 'AppUpdateWidgetsReqVO') {
1048
+ return {
1049
+ shapeHint: {
1050
+ entity_id: 2188891001,
1051
+ options: {
1052
+ charts: {
1053
+ entity_id: 2188891001,
1054
+ dimensions: [{ IsField: true, FieldID: 1004, Name: 'Source' }],
1055
+ measures: [{ FieldID: 1005, Aggregate: 'sum', Name: 'Total Amount' }],
1056
+ type: 'bar',
1057
+ },
1058
+ },
1059
+ },
1060
+ commonMistakes: [
1061
+ 'dashboard chart config belongs in body.options.charts',
1062
+ 'field dimensions with FieldID must set IsField:true',
1063
+ 'verify persistence with widget get or widget list after update',
1064
+ ],
1065
+ }
1066
+ }
969
1067
  return {}
970
1068
  }
971
1069
 
@@ -1228,6 +1326,32 @@ function requireUpdateContains(
1228
1326
  }
1229
1327
  }
1230
1328
 
1329
+ function validateChartFieldDimensions(
1330
+ scope: CommandScope,
1331
+ command: NonNullable<ReturnType<typeof findCommand>>,
1332
+ requestType: string,
1333
+ dimensions: unknown,
1334
+ pathPrefix: string,
1335
+ env: EnvInput,
1336
+ ) {
1337
+ if (!Array.isArray(dimensions)) return
1338
+ for (const [index, dimension] of dimensions.entries()) {
1339
+ if (!isRecord(dimension) || typeof dimension.FieldID !== 'number') {
1340
+ continue
1341
+ }
1342
+ if (dimension.IsField !== true) {
1343
+ throwBodyValidationFailure(
1344
+ 'widget chart field dimensions require IsField:true when FieldID is present; otherwise grouped statistics collapse into total rows',
1345
+ requestType,
1346
+ `${pathPrefix}.${index}.IsField`,
1347
+ scope,
1348
+ command,
1349
+ env,
1350
+ )
1351
+ }
1352
+ }
1353
+ }
1354
+
1231
1355
  function validateActionSpecificRawBody(
1232
1356
  scope: CommandScope,
1233
1357
  command: NonNullable<ReturnType<typeof findCommand>>,
@@ -1361,6 +1485,16 @@ function validateActionSpecificRawBody(
1361
1485
  validateAccessRuleUserScope(scope, command, command.requestType, options?.user_scope, env)
1362
1486
  validateAccessRulePolicyShape(scope, command, command.requestType, policy, env, 'body.options.policy')
1363
1487
  }
1488
+
1489
+ if (isWidgetPreviewDataCommand(scope, command)) {
1490
+ validateChartFieldDimensions(scope, command, command.requestType, body.dimensions, 'body.dimensions', env)
1491
+ }
1492
+
1493
+ if (isWidgetUpdateCommand(scope, command)) {
1494
+ const options = isRecord(body.options) ? body.options : undefined
1495
+ const charts = options && isRecord(options.charts) ? options.charts : undefined
1496
+ validateChartFieldDimensions(scope, command, command.requestType, charts?.dimensions, 'body.options.charts.dimensions', env)
1497
+ }
1364
1498
  }
1365
1499
 
1366
1500
  function validateActionSpecificBody(
@@ -1818,6 +1952,186 @@ async function requestJSON(
1818
1952
  return { status: response.status, data: parsedResponse }
1819
1953
  }
1820
1954
 
1955
+ function unwrapResponseData(payload: unknown): unknown {
1956
+ return isRecord(payload) && 'data' in payload ? payload.data : payload
1957
+ }
1958
+
1959
+ type SDKGenEntityRef = {
1960
+ id: string
1961
+ }
1962
+
1963
+ type SDKGenInitializedFieldKeys = {
1964
+ entityId: number
1965
+ fields: string[]
1966
+ }
1967
+
1968
+ function extractSDKGenAppPayload(payload: unknown): Record<string, any> {
1969
+ const appPayload = unwrapResponseData(payload)
1970
+ if (!isRecord(appPayload)) {
1971
+ throw new CLIError('SDK_GEN_INVALID_SCHEMA', 'sdk-gen expected app detail response data', 2)
1972
+ }
1973
+ return appPayload
1974
+ }
1975
+
1976
+ function extractSDKGenEntityRefs(appPayload: Record<string, any>): SDKGenEntityRef[] {
1977
+ const sourceEntities = Array.isArray(appPayload.entities)
1978
+ ? appPayload.entities
1979
+ : Array.isArray(appPayload.tables)
1980
+ ? appPayload.tables
1981
+ : Array.isArray(appPayload.entitys)
1982
+ ? appPayload.entitys
1983
+ : []
1984
+ const refs = sourceEntities
1985
+ .filter((item): item is Record<string, unknown> => isRecord(item))
1986
+ .map((item) => item.id)
1987
+ .filter((id): id is string | number => typeof id === 'string' || typeof id === 'number')
1988
+ .map((id) => ({ id: String(id) }))
1989
+ return refs
1990
+ }
1991
+
1992
+ function resolveSDKGenAppId(flagAppId: string, appPayload: Record<string, any>): number {
1993
+ const fromPayload = typeof appPayload.id === 'number' ? appPayload.id : typeof appPayload.app_id === 'number' ? appPayload.app_id : undefined
1994
+ const fromFlag = Number(flagAppId)
1995
+ if (Number.isFinite(fromFlag)) return fromFlag
1996
+ if (fromPayload) return fromPayload
1997
+ throw new CLIError('SDK_GEN_APP_ID_REQUIRED', 'sdk-gen expected numeric app id', 2)
1998
+ }
1999
+
2000
+ function walkSDKGenFields(fields: unknown, visit: (field: Record<string, any>) => void) {
2001
+ if (!Array.isArray(fields)) {
2002
+ return
2003
+ }
2004
+ for (const field of fields) {
2005
+ if (!isRecord(field)) {
2006
+ continue
2007
+ }
2008
+ visit(field)
2009
+ walkSDKGenFields(field.children, visit)
2010
+ }
2011
+ }
2012
+
2013
+ function collectExistingSDKGenFieldKeys(entity: Record<string, any>): Set<string> {
2014
+ const keys = new Set<string>()
2015
+ walkSDKGenFields(entity.fields, (field) => {
2016
+ if (typeof field.key === 'string' && field.key.trim() !== '') {
2017
+ keys.add(field.key.trim())
2018
+ }
2019
+ })
2020
+ return keys
2021
+ }
2022
+
2023
+ function nextSDKGenFallbackFieldKey(fieldId: number, usedKeys: Set<string>): string {
2024
+ const base = `field${fieldId}`
2025
+ if (!usedKeys.has(base)) {
2026
+ usedKeys.add(base)
2027
+ return base
2028
+ }
2029
+ let index = 2
2030
+ while (usedKeys.has(`${base}_${index}`)) {
2031
+ index++
2032
+ }
2033
+ const key = `${base}_${index}`
2034
+ usedKeys.add(key)
2035
+ return key
2036
+ }
2037
+
2038
+ function initializeMissingSDKGenFieldKeys(entity: Record<string, any>): Array<{ id: number; key: string }> {
2039
+ const usedKeys = collectExistingSDKGenFieldKeys(entity)
2040
+ const updates: Array<{ id: number; key: string }> = []
2041
+ walkSDKGenFields(entity.fields, (field) => {
2042
+ if (typeof field.id !== 'number') {
2043
+ return
2044
+ }
2045
+ if (typeof field.key === 'string' && field.key.trim() !== '') {
2046
+ field.key = field.key.trim()
2047
+ return
2048
+ }
2049
+ const key = nextSDKGenFallbackFieldKey(field.id, usedKeys)
2050
+ field.key = key
2051
+ updates.push({ id: field.id, key })
2052
+ })
2053
+ return updates
2054
+ }
2055
+
2056
+ async function executeDevSDKGen(
2057
+ runtimeEnv: RuntimeEnv,
2058
+ flags: Record<string, string | boolean>,
2059
+ fetchImpl: typeof fetch,
2060
+ ): Promise<any> {
2061
+ validateDevSDKGenFlags(flags)
2062
+ const appId = flagValue(flags, 'app-id')
2063
+ const outDir = flagValue(flags, 'out')
2064
+ if (!appId) {
2065
+ throw new CLIError('MISSING_PATH_FLAG', 'missing required flag --app-id', 2)
2066
+ }
2067
+ if (!outDir) {
2068
+ throw new CLIError('MISSING_FLAG', 'missing required flag --out', 2, {
2069
+ operation: 'dev sdk-gen',
2070
+ hint: 'retry with --out src/arcubase',
2071
+ })
2072
+ }
2073
+
2074
+ const endpoint = `/api/apps/${encodeURIComponent(appId)}`
2075
+ const appDetail = await requestJSON(runtimeEnv, 'GET', endpoint, undefined, fetchImpl)
2076
+ const appPayload = extractSDKGenAppPayload(appDetail.data)
2077
+ let entityRefs = extractSDKGenEntityRefs(appPayload)
2078
+ if (entityRefs.length === 0) {
2079
+ const entitiesEndpoint = `/api/apps/${encodeURIComponent(appId)}/entities`
2080
+ const entityList = await requestJSON(runtimeEnv, 'GET', entitiesEndpoint, undefined, fetchImpl)
2081
+ const entityListPayload = unwrapResponseData(entityList.data)
2082
+ entityRefs = extractSDKGenEntityRefs({ entities: entityListPayload })
2083
+ }
2084
+ if (entityRefs.length === 0) {
2085
+ throw new CLIError('SDK_GEN_NO_ENTITIES', 'sdk-gen could not find entities in app detail response', 2)
2086
+ }
2087
+ const entities = []
2088
+ const initializedFieldKeys: SDKGenInitializedFieldKeys[] = []
2089
+ for (const entityRef of entityRefs) {
2090
+ const entityEndpoint = `/api/apps/${encodeURIComponent(appId)}/entity/${encodeURIComponent(entityRef.id)}`
2091
+ const entityDetail = await requestJSON(runtimeEnv, 'GET', entityEndpoint, undefined, fetchImpl)
2092
+ const entityPayload = unwrapResponseData(entityDetail.data)
2093
+ if (!isRecord(entityPayload)) {
2094
+ throw new CLIError('SDK_GEN_INVALID_SCHEMA', 'sdk-gen expected entity detail response data', 2)
2095
+ }
2096
+ const customKeyUpdates = initializeMissingSDKGenFieldKeys(entityPayload)
2097
+ if (customKeyUpdates.length > 0) {
2098
+ const customKeysEndpoint = `/api/apps/${encodeURIComponent(appId)}/entity/${encodeURIComponent(entityRef.id)}/custom-keys`
2099
+ await requestJSON(runtimeEnv, 'PUT', customKeysEndpoint, customKeyUpdates, fetchImpl)
2100
+ initializedFieldKeys.push({
2101
+ entityId: typeof entityPayload.id === 'number' ? entityPayload.id : Number(entityRef.id),
2102
+ fields: customKeyUpdates.map((item) => item.key),
2103
+ })
2104
+ }
2105
+ entities.push(entityPayload)
2106
+ }
2107
+ const generated = generateArcubaseProjectSDK({
2108
+ id: resolveSDKGenAppId(appId, appPayload),
2109
+ name: typeof appPayload.name === 'string' ? appPayload.name : undefined,
2110
+ entities,
2111
+ })
2112
+ const absoluteOutDir = path.resolve(process.cwd(), outDir)
2113
+
2114
+ for (const file of generated.files) {
2115
+ const targetPath = path.resolve(absoluteOutDir, file.path)
2116
+ if (!targetPath.startsWith(`${absoluteOutDir}${path.sep}`)) {
2117
+ throw new CLIError('SDK_GEN_INVALID_OUTPUT_PATH', `invalid generated file path: ${file.path}`, 2)
2118
+ }
2119
+ fs.mkdirSync(path.dirname(targetPath), { recursive: true })
2120
+ fs.writeFileSync(targetPath, file.contents)
2121
+ }
2122
+
2123
+ return {
2124
+ kind: 'result',
2125
+ commandPath: ['dev', 'sdk-gen'],
2126
+ status: appDetail.status,
2127
+ data: {
2128
+ out: absoluteOutDir,
2129
+ files: generated.files.map((file) => file.path),
2130
+ initializedFieldKeys,
2131
+ },
2132
+ }
2133
+ }
2134
+
1821
2135
  function requiredStringFlag(flags: Record<string, string | boolean>, flag: string): string {
1822
2136
  const value = flagValue(flags, flag)
1823
2137
  if (!value) {
@@ -2186,6 +2500,16 @@ export async function executeCLI(scope: CommandScope, argv: string[], env?: Runt
2186
2500
  }
2187
2501
  return { kind: 'help', text: renderRootHelp(scope) }
2188
2502
  }
2503
+ if (isDevSDKGenCommand(scope, moduleName, commandName)) {
2504
+ if (hasFlag(parsed.flags, 'help')) {
2505
+ return { kind: 'help', text: renderDevSDKGenHelp() }
2506
+ }
2507
+ const runtimeEnv = env ?? loadRuntimeEnv(scope)
2508
+ return executeDevSDKGen(runtimeEnv, parsed.flags, fetchImpl)
2509
+ }
2510
+ if (scope === 'admin' && moduleName === 'dev' && !commandName) {
2511
+ return { kind: 'help', text: renderDevModuleHelp() }
2512
+ }
2189
2513
  const singleTokenCommand = !commandName ? findCommand(scope, moduleName) : null
2190
2514
  if (!commandName && !singleTokenCommand) {
2191
2515
  if (hasFlag(parsed.flags, 'help-examples')) {
@@ -89,12 +89,32 @@ const conditionDocHint: RuntimeDocHint = {
89
89
  title: 'Condition',
90
90
  file: 'docs/runtime-reference/condition.md',
91
91
  }
92
+ const dashboardDocHint: RuntimeDocHint = {
93
+ title: 'Dashboard',
94
+ file: 'docs/runtime-reference/dashboard.md',
95
+ }
96
+ const widgetsDocHint: RuntimeDocHint = {
97
+ title: 'Widgets',
98
+ file: 'docs/runtime-reference/widgets.md',
99
+ }
92
100
 
93
101
  const docHintIndex: Record<string, RuntimeDocHint[]> = {
94
102
  AppCreateByTenantsReqVO: [
95
103
  { title: 'Table lifecycle', file: 'docs/runtime-reference/table-lifecycle.md' },
96
104
  examplesIndexDocHint,
97
105
  ],
106
+ AppNewWidgetsReqVO: [
107
+ widgetsDocHint,
108
+ ],
109
+ AppPreviewWidgetsDataReqVO: [
110
+ widgetsDocHint,
111
+ ],
112
+ AppUpdateWidgetsIngressOptionsReqVO: [
113
+ widgetsDocHint,
114
+ ],
115
+ AppUpdateWidgetsReqVO: [
116
+ widgetsDocHint,
117
+ ],
98
118
  AppCreateEntityReqVO: [
99
119
  { title: 'Table lifecycle', file: 'docs/runtime-reference/table-lifecycle.md' },
100
120
  { title: 'Table schema cheatsheet', file: 'docs/runtime-reference/entity-schema.md' },
@@ -113,6 +133,19 @@ const docHintIndex: Record<string, RuntimeDocHint[]> = {
113
133
  { title: 'Access rule', file: 'docs/runtime-reference/access-rule.md' },
114
134
  examplesIndexDocHint,
115
135
  ],
136
+ DashboardCreateReqVO: [
137
+ dashboardDocHint,
138
+ ],
139
+ DashboardUpdateLayoutReqVO: [
140
+ dashboardDocHint,
141
+ widgetsDocHint,
142
+ ],
143
+ DashboardUpdateOptionsReqVO: [
144
+ dashboardDocHint,
145
+ ],
146
+ DashboardUpdateTitleReqVO: [
147
+ dashboardDocHint,
148
+ ],
116
149
  EntitySaveReqVO: [
117
150
  { title: 'Table schema cheatsheet', file: 'docs/runtime-reference/entity-schema.md' },
118
151
  { title: 'Field type index', file: 'docs/runtime-reference/entity-schema/README.md' },
@@ -152,6 +185,9 @@ const docHintIndex: Record<string, RuntimeDocHint[]> = {
152
185
  { title: 'Search and bulk actions', file: 'docs/runtime-reference/search-and-bulk-actions.md' },
153
186
  examplesIndexDocHint,
154
187
  ],
188
+ FetchWidgetsDataReqVO: [
189
+ widgetsDocHint,
190
+ ],
155
191
  'EntityInvokeBatchOperatorReqVO & InvokeRequestVO': [
156
192
  selectionDocHint,
157
193
  conditionDocHint,
@@ -206,6 +242,21 @@ const commandDocHintIndex: Record<string, RuntimeDocHint[]> = {
206
242
  { title: 'Table lifecycle', file: 'docs/runtime-reference/table-lifecycle.md' },
207
243
  examplesIndexDocHint,
208
244
  ],
245
+ 'admin.dashboard.getDashboardList': [
246
+ dashboardDocHint,
247
+ ],
248
+ 'admin.dashboard.getDashboardOptions': [
249
+ dashboardDocHint,
250
+ ],
251
+ 'admin.widget.widgetsList': [
252
+ widgetsDocHint,
253
+ ],
254
+ 'admin.widget.getWidgets': [
255
+ widgetsDocHint,
256
+ ],
257
+ 'admin.widget.deleteWidgets': [
258
+ widgetsDocHint,
259
+ ],
209
260
  'admin.app.createEntity': [
210
261
  { title: 'Table lifecycle', file: 'docs/runtime-reference/table-lifecycle.md' },
211
262
  examplesIndexDocHint,
@@ -273,6 +324,13 @@ const commandDocHintIndex: Record<string, RuntimeDocHint[]> = {
273
324
  { title: 'Search and bulk actions', file: 'docs/runtime-reference/search-and-bulk-actions.md' },
274
325
  examplesIndexDocHint,
275
326
  ],
327
+ 'user.dashboard.getDashboard': [
328
+ dashboardDocHint,
329
+ ],
330
+ 'user.dashboard.getDashboardWidgets': [
331
+ dashboardDocHint,
332
+ widgetsDocHint,
333
+ ],
276
334
  }
277
335
 
278
336
  export function getCommandDocHints(operation: string, env: EnvInput = process.env): RuntimeDocHint[] {
@@ -17,6 +17,12 @@ test('admin surface is restricted to reset white list', () => {
17
17
  'app list',
18
18
  'app update',
19
19
  'app watch-import-task',
20
+ 'dashboard create',
21
+ 'dashboard get-options',
22
+ 'dashboard list',
23
+ 'dashboard rename',
24
+ 'dashboard update-layout',
25
+ 'dashboard update-options',
20
26
  'table create',
21
27
  'table delete',
22
28
  'table get',
@@ -30,6 +36,13 @@ test('admin surface is restricted to reset white list', () => {
30
36
  'access-rule get',
31
37
  'access-rule list',
32
38
  'access-rule update',
39
+ 'widget create',
40
+ 'widget delete',
41
+ 'widget get',
42
+ 'widget list',
43
+ 'widget preview-data',
44
+ 'widget update',
45
+ 'widget update-ingress-options',
33
46
  'workflow create-version',
34
47
  'workflow deploy',
35
48
  'workflow get',
@@ -41,6 +54,8 @@ test('admin surface is restricted to reset white list', () => {
41
54
  test('user surface is restricted to reset white list', () => {
42
55
  const actual = commandPaths(userCommands)
43
56
  const expected = [
57
+ 'dashboard get',
58
+ 'dashboard widgets',
44
59
  'entry',
45
60
  'profile get',
46
61
  'profile update',
@@ -60,6 +75,7 @@ test('user surface is restricted to reset white list', () => {
60
75
  'upload mobile',
61
76
  'upload preview',
62
77
  'upload token',
78
+ 'widget data',
63
79
  'workflow approve',
64
80
  'workflow countersign',
65
81
  'workflow forward',
@@ -76,8 +92,8 @@ test('user surface is restricted to reset white list', () => {
76
92
  test('generated modules use CLI namespaces only', () => {
77
93
  const adminModules = [...new Set(adminCommands.map((item) => item.module))].sort()
78
94
  const userModules = [...new Set(userCommands.map((item) => item.module))].sort()
79
- assert.deepEqual(adminModules, ['access-rule', 'app', 'table', 'workflow'])
80
- assert.deepEqual(userModules, ['entry', 'profile', 'row', 'table', 'upload', 'workflow'])
95
+ assert.deepEqual(adminModules, ['access-rule', 'app', 'dashboard', 'table', 'widget', 'workflow'])
96
+ assert.deepEqual(userModules, ['dashboard', 'entry', 'profile', 'row', 'table', 'upload', 'widget', 'workflow'])
81
97
  })
82
98
 
83
99
  test('black list commands are removed from registry', () => {
@@ -99,6 +115,8 @@ test('black list commands are removed from registry', () => {
99
115
  'workflow query-entity-selection',
100
116
  'profile change-password',
101
117
  'profile auth-channels',
118
+ 'dashboard delete',
119
+ 'dashboard clone',
102
120
  ]
103
121
  for (const item of forbidden) {
104
122
  assert.equal(all.has(item), false, `${item} should not exist`)