@carthooks/arcubase-cli 0.1.2 → 0.1.4

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 (106) hide show
  1. package/bundle/arcubase-admin.mjs +1238 -16
  2. package/bundle/arcubase.mjs +1238 -16
  3. package/dist/bin/arcubase-admin.js +0 -0
  4. package/dist/bin/arcubase.js +0 -0
  5. package/dist/runtime/command_registry.d.ts +8 -0
  6. package/dist/runtime/command_registry.d.ts.map +1 -1
  7. package/dist/runtime/command_registry.js +35 -0
  8. package/dist/runtime/entity_save_schema.d.ts +15 -0
  9. package/dist/runtime/entity_save_schema.d.ts.map +1 -0
  10. package/dist/runtime/entity_save_schema.js +641 -0
  11. package/dist/runtime/errors.d.ts +11 -0
  12. package/dist/runtime/errors.d.ts.map +1 -1
  13. package/dist/runtime/execute.d.ts.map +1 -1
  14. package/dist/runtime/execute.js +294 -12
  15. package/dist/runtime/upload.d.ts +13 -0
  16. package/dist/runtime/upload.d.ts.map +1 -0
  17. package/dist/runtime/upload.js +148 -0
  18. package/dist/runtime/zod_registry.d.ts +6 -0
  19. package/dist/runtime/zod_registry.d.ts.map +1 -1
  20. package/dist/runtime/zod_registry.js +136 -0
  21. package/dist/tests/command_registry.test.js +5 -0
  22. package/dist/tests/entity_save_schema.test.d.ts +2 -0
  23. package/dist/tests/entity_save_schema.test.d.ts.map +1 -0
  24. package/dist/tests/entity_save_schema.test.js +221 -0
  25. package/dist/tests/execute_validation.test.js +603 -0
  26. package/dist/tests/help.test.js +49 -0
  27. package/dist/tests/upload.test.d.ts +2 -0
  28. package/dist/tests/upload.test.d.ts.map +1 -0
  29. package/dist/tests/upload.test.js +107 -0
  30. package/package.json +1 -1
  31. package/sdk-dist/docs/runtime-reference/README.md +187 -0
  32. package/sdk-dist/docs/runtime-reference/entity-schema/README.md +120 -0
  33. package/sdk-dist/docs/runtime-reference/entity-schema/address.md +34 -0
  34. package/sdk-dist/docs/runtime-reference/entity-schema/boolean.md +37 -0
  35. package/sdk-dist/docs/runtime-reference/entity-schema/button.md +47 -0
  36. package/sdk-dist/docs/runtime-reference/entity-schema/checkbox.md +61 -0
  37. package/sdk-dist/docs/runtime-reference/entity-schema/datasets.md +35 -0
  38. package/sdk-dist/docs/runtime-reference/entity-schema/datetime.md +40 -0
  39. package/sdk-dist/docs/runtime-reference/entity-schema/department.md +33 -0
  40. package/sdk-dist/docs/runtime-reference/entity-schema/departments.md +34 -0
  41. package/sdk-dist/docs/runtime-reference/entity-schema/divider.md +39 -0
  42. package/sdk-dist/docs/runtime-reference/entity-schema/file.md +53 -0
  43. package/sdk-dist/docs/runtime-reference/entity-schema/formula.md +44 -0
  44. package/sdk-dist/docs/runtime-reference/entity-schema/image.md +52 -0
  45. package/sdk-dist/docs/runtime-reference/entity-schema/linkto.md +67 -0
  46. package/sdk-dist/docs/runtime-reference/entity-schema/location.md +32 -0
  47. package/sdk-dist/docs/runtime-reference/entity-schema/lookup.md +41 -0
  48. package/sdk-dist/docs/runtime-reference/entity-schema/member.md +37 -0
  49. package/sdk-dist/docs/runtime-reference/entity-schema/members.md +31 -0
  50. package/sdk-dist/docs/runtime-reference/entity-schema/number.md +70 -0
  51. package/sdk-dist/docs/runtime-reference/entity-schema/query.md +50 -0
  52. package/sdk-dist/docs/runtime-reference/entity-schema/radio.md +53 -0
  53. package/sdk-dist/docs/runtime-reference/entity-schema/relation.md +55 -0
  54. package/sdk-dist/docs/runtime-reference/entity-schema/relationfield.md +38 -0
  55. package/sdk-dist/docs/runtime-reference/entity-schema/rollup.md +54 -0
  56. package/sdk-dist/docs/runtime-reference/entity-schema/select.md +63 -0
  57. package/sdk-dist/docs/runtime-reference/entity-schema/selects.md +51 -0
  58. package/sdk-dist/docs/runtime-reference/entity-schema/serialnumber.md +48 -0
  59. package/sdk-dist/docs/runtime-reference/entity-schema/signature.md +31 -0
  60. package/sdk-dist/docs/runtime-reference/entity-schema/status.md +70 -0
  61. package/sdk-dist/docs/runtime-reference/entity-schema/subform.md +74 -0
  62. package/sdk-dist/docs/runtime-reference/entity-schema/tags.md +19 -0
  63. package/sdk-dist/docs/runtime-reference/entity-schema/text.md +76 -0
  64. package/sdk-dist/docs/runtime-reference/entity-schema/textarea.md +47 -0
  65. package/sdk-dist/docs/runtime-reference/entity-schema/workflow_status.md +17 -0
  66. package/sdk-dist/docs/runtime-reference/entity-schema.md +197 -0
  67. package/sdk-dist/docs/runtime-reference/examples/README.md +51 -0
  68. package/sdk-dist/docs/runtime-reference/examples/crm-01/README.md +20 -0
  69. package/sdk-dist/docs/runtime-reference/examples/mes-01/README.md +21 -0
  70. package/sdk-dist/docs/runtime-reference/examples/oms-01/README.md +183 -0
  71. package/sdk-dist/docs/runtime-reference/examples/oms-01/app-overview.md +79 -0
  72. package/sdk-dist/docs/runtime-reference/examples/oms-01/create-app.json +3 -0
  73. package/sdk-dist/docs/runtime-reference/examples/oms-01/goods-receipt.create-entity.json +3 -0
  74. package/sdk-dist/docs/runtime-reference/examples/oms-01/goods-receipt.schema.json +159 -0
  75. package/sdk-dist/docs/runtime-reference/examples/oms-01/item-master.create-entity.json +3 -0
  76. package/sdk-dist/docs/runtime-reference/examples/oms-01/item-master.schema.json +179 -0
  77. package/sdk-dist/docs/runtime-reference/examples/oms-01/picking-list.create-entity.json +3 -0
  78. package/sdk-dist/docs/runtime-reference/examples/oms-01/picking-list.schema.json +168 -0
  79. package/sdk-dist/docs/runtime-reference/examples/oms-01/purchase-order.create-entity.json +3 -0
  80. package/sdk-dist/docs/runtime-reference/examples/oms-01/purchase-order.schema.json +417 -0
  81. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.bulk-update.json +22 -0
  82. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.create-entity.json +3 -0
  83. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.query.json +10 -0
  84. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.row.insert.json +28 -0
  85. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.schema.json +398 -0
  86. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.selection.condition.json +13 -0
  87. package/sdk-dist/docs/runtime-reference/examples/oms-01/sales-order.selection.ids.json +7 -0
  88. package/sdk-dist/docs/runtime-reference/examples/oms-01/stock-issue.create-entity.json +3 -0
  89. package/sdk-dist/docs/runtime-reference/examples/oms-01/stock-issue.schema.json +137 -0
  90. package/sdk-dist/docs/runtime-reference/examples/wms-01/README.md +21 -0
  91. package/sdk-dist/docs/runtime-reference/row-crud.md +188 -0
  92. package/sdk-dist/docs/runtime-reference/search-and-bulk-actions.md +494 -0
  93. package/sdk-dist/docs/runtime-reference/table-lifecycle.md +135 -0
  94. package/sdk-dist/docs/runtime-reference/uploads.md +106 -0
  95. package/sdk-dist/docs/runtime-reference/workflow/README.md +19 -0
  96. package/src/runtime/command_registry.ts +47 -0
  97. package/src/runtime/entity_save_schema.ts +708 -0
  98. package/src/runtime/errors.ts +12 -0
  99. package/src/runtime/execute.ts +367 -12
  100. package/src/runtime/upload.ts +196 -0
  101. package/src/runtime/zod_registry.ts +156 -0
  102. package/src/tests/command_registry.test.ts +6 -0
  103. package/src/tests/entity_save_schema.test.ts +236 -0
  104. package/src/tests/execute_validation.test.ts +781 -0
  105. package/src/tests/help.test.ts +60 -0
  106. package/src/tests/upload.test.ts +133 -0
@@ -4738,6 +4738,41 @@ function listModules(scope) {
4738
4738
  function listModuleCommands(scope, moduleName) {
4739
4739
  return listCommands(scope).filter((item) => item.commandPath[0] === moduleName);
4740
4740
  }
4741
+ function levenshtein(a, b) {
4742
+ const dp = Array.from({ length: a.length + 1 }, () => new Array(b.length + 1).fill(0));
4743
+ for (let i = 0; i <= a.length; i += 1) dp[i][0] = i;
4744
+ for (let j = 0; j <= b.length; j += 1) dp[0][j] = j;
4745
+ for (let i = 1; i <= a.length; i += 1) {
4746
+ for (let j = 1; j <= b.length; j += 1) {
4747
+ const cost = a[i - 1] === b[j - 1] ? 0 : 1;
4748
+ dp[i][j] = Math.min(
4749
+ dp[i - 1][j] + 1,
4750
+ dp[i][j - 1] + 1,
4751
+ dp[i - 1][j - 1] + cost
4752
+ );
4753
+ }
4754
+ }
4755
+ return dp[a.length][b.length];
4756
+ }
4757
+ function findCommandSuggestions(moduleName, commandName, limit = 3) {
4758
+ const suggestions = [];
4759
+ for (const scope of ["admin", "user"]) {
4760
+ for (const command of listCommands(scope)) {
4761
+ const [candidateModule, candidateCommand] = command.commandPath;
4762
+ const moduleScore = candidateModule === moduleName ? 0 : levenshtein(candidateModule, moduleName || "");
4763
+ const commandScore = candidateCommand === commandName ? 0 : levenshtein(candidateCommand, commandName || "");
4764
+ const score = commandScore * 3 + moduleScore;
4765
+ suggestions.push({
4766
+ scope,
4767
+ moduleName: candidateModule,
4768
+ commandName: candidateCommand,
4769
+ binary: scope === "admin" ? "arcubase-admin" : "arcubase",
4770
+ score
4771
+ });
4772
+ }
4773
+ }
4774
+ return suggestions.sort((a, b) => a.score - b.score || a.binary.localeCompare(b.binary) || a.moduleName.localeCompare(b.moduleName) || a.commandName.localeCompare(b.commandName)).slice(0, limit);
4775
+ }
4741
4776
 
4742
4777
  // src/runtime/body_loader.ts
4743
4778
  import fs from "fs";
@@ -5241,8 +5276,8 @@ function getErrorMap() {
5241
5276
 
5242
5277
  // node_modules/zod/v3/helpers/parseUtil.js
5243
5278
  var makeIssue = (params) => {
5244
- const { data, path, errorMaps, issueData } = params;
5245
- const fullPath = [...path, ...issueData.path || []];
5279
+ const { data, path: path2, errorMaps, issueData } = params;
5280
+ const fullPath = [...path2, ...issueData.path || []];
5246
5281
  const fullIssue = {
5247
5282
  ...issueData,
5248
5283
  path: fullPath
@@ -5358,11 +5393,11 @@ var errorUtil;
5358
5393
 
5359
5394
  // node_modules/zod/v3/types.js
5360
5395
  var ParseInputLazyPath = class {
5361
- constructor(parent, value, path, key) {
5396
+ constructor(parent, value, path2, key) {
5362
5397
  this._cachedPath = [];
5363
5398
  this.parent = parent;
5364
5399
  this.data = value;
5365
- this._path = path;
5400
+ this._path = path2;
5366
5401
  this._key = key;
5367
5402
  }
5368
5403
  get path() {
@@ -9661,10 +9696,671 @@ var typeIndex = {
9661
9696
  }
9662
9697
  };
9663
9698
 
9699
+ // src/runtime/entity_save_schema.ts
9700
+ var simpleFieldTypes = [
9701
+ "text",
9702
+ "textarea",
9703
+ "number",
9704
+ "datetime",
9705
+ "boolean",
9706
+ "button",
9707
+ "address",
9708
+ "datasets",
9709
+ "department",
9710
+ "departments",
9711
+ "divider",
9712
+ "file",
9713
+ "image",
9714
+ "location",
9715
+ "member",
9716
+ "members",
9717
+ "signature",
9718
+ "tags",
9719
+ "workflow_status",
9720
+ "mobile"
9721
+ ];
9722
+ var enumFieldTypes = ["select", "selects", "radio", "checkbox", "status"];
9723
+ var relationFieldTypes = ["linkto", "relation", "query"];
9724
+ var strictFieldTypes = /* @__PURE__ */ new Set([
9725
+ ...simpleFieldTypes,
9726
+ ...enumFieldTypes,
9727
+ ...relationFieldTypes,
9728
+ "formula",
9729
+ "lookup",
9730
+ "relationfield",
9731
+ "rollup",
9732
+ "serialnumber",
9733
+ "subform"
9734
+ ]);
9735
+ var relationTypeSchema = external_exports.union([external_exports.literal("entity"), external_exports.literal("aggregation")]);
9736
+ var linkModeSchema = external_exports.union([external_exports.literal("manual"), external_exports.literal("auto")]);
9737
+ var linkStructureSchema = external_exports.union([external_exports.literal("single"), external_exports.literal("multiple")]);
9738
+ var enumItemSchema = external_exports.object({
9739
+ key: external_exports.union([external_exports.number(), external_exports.string()]).optional(),
9740
+ value: external_exports.string().min(1)
9741
+ }).passthrough();
9742
+ var statusItemSchema = external_exports.object({
9743
+ key: external_exports.union([external_exports.number(), external_exports.string()]).optional(),
9744
+ code: external_exports.union([external_exports.number(), external_exports.string()]),
9745
+ value: external_exports.string().min(1)
9746
+ }).passthrough();
9747
+ var sortConfigSchema = external_exports.object({
9748
+ column: external_exports.string().min(1),
9749
+ order: external_exports.union([external_exports.literal("asc"), external_exports.literal("desc")])
9750
+ }).strict();
9751
+ var relationOptionsSchema = external_exports.object({
9752
+ entity_id: external_exports.number().int().positive(),
9753
+ app_id: external_exports.number().int().positive(),
9754
+ type: relationTypeSchema,
9755
+ show_fields: external_exports.array(external_exports.number().int().positive()).optional(),
9756
+ condition: external_exports.record(external_exports.string(), external_exports.any()).optional(),
9757
+ sorts: external_exports.array(sortConfigSchema).optional(),
9758
+ queryFields: external_exports.array(external_exports.number().int().positive()).optional(),
9759
+ subform_id: external_exports.number().int().positive().optional()
9760
+ }).strict();
9761
+ var lookupRelationSchema = external_exports.object({
9762
+ local_linkto_field_id: external_exports.number().int().min(1001),
9763
+ target_field_id: external_exports.number().int().min(1001),
9764
+ target_field_type: external_exports.string().min(1)
9765
+ }).strict();
9766
+ var relationFieldRelationSchema = external_exports.object({
9767
+ app_id: external_exports.number().int().positive(),
9768
+ entity_id: external_exports.number().int().positive(),
9769
+ type: relationTypeSchema.optional(),
9770
+ show_fields: external_exports.array(external_exports.number().int().positive()).optional(),
9771
+ condition: external_exports.record(external_exports.string(), external_exports.any()).optional(),
9772
+ sorts: external_exports.array(sortConfigSchema).optional(),
9773
+ queryFields: external_exports.array(external_exports.number().int().positive()).optional(),
9774
+ subform_id: external_exports.number().int().positive().optional()
9775
+ }).strict();
9776
+ var formulaSchema = external_exports.object({
9777
+ code: external_exports.string().min(1),
9778
+ jsonlogic: external_exports.record(external_exports.string(), external_exports.any())
9779
+ }).strict();
9780
+ var serialPartBuildinStringSchema = external_exports.object({
9781
+ mode: external_exports.literal("buildin"),
9782
+ type: external_exports.literal("string"),
9783
+ options: external_exports.object({
9784
+ value: external_exports.string()
9785
+ }).strict()
9786
+ }).strict();
9787
+ var serialPartBuildinDateSchema = external_exports.object({
9788
+ mode: external_exports.literal("buildin"),
9789
+ type: external_exports.literal("date"),
9790
+ options: external_exports.object({
9791
+ format: external_exports.string().optional(),
9792
+ perdefined: external_exports.boolean().optional(),
9793
+ perdefinedFormat: external_exports.string().optional()
9794
+ }).strict().refine((value) => Boolean(value.format || value.perdefinedFormat), {
9795
+ message: "date serial part requires format or perdefinedFormat"
9796
+ })
9797
+ }).strict();
9798
+ var serialPartBuildinSerialSchema = external_exports.object({
9799
+ mode: external_exports.literal("buildin"),
9800
+ type: external_exports.literal("serial"),
9801
+ options: external_exports.object({
9802
+ length: external_exports.number().int().min(1),
9803
+ lenfixed: external_exports.boolean().optional(),
9804
+ random: external_exports.boolean().optional(),
9805
+ reset: external_exports.string().optional()
9806
+ }).strict()
9807
+ }).strict();
9808
+ var serialPartFieldSchema = external_exports.object({
9809
+ mode: external_exports.literal("field"),
9810
+ field_id: external_exports.number().int().min(1001)
9811
+ }).passthrough();
9812
+ var serialPartSchema = external_exports.union([
9813
+ serialPartBuildinStringSchema,
9814
+ serialPartBuildinDateSchema,
9815
+ serialPartBuildinSerialSchema,
9816
+ serialPartFieldSchema
9817
+ ]);
9818
+ var fieldCommonShape = {
9819
+ id: external_exports.number().int().min(1001),
9820
+ label: external_exports.string().min(1),
9821
+ required: external_exports.boolean(),
9822
+ unique: external_exports.boolean(),
9823
+ editable: external_exports.boolean(),
9824
+ visible: external_exports.boolean(),
9825
+ show_label: external_exports.boolean(),
9826
+ scannable: external_exports.boolean(),
9827
+ default_value_mode: external_exports.number(),
9828
+ description: external_exports.string(),
9829
+ value: external_exports.any(),
9830
+ number_decimal: external_exports.number().int().min(0),
9831
+ depends: external_exports.array(external_exports.number().int().min(1001)),
9832
+ key: external_exports.string(),
9833
+ code_index: external_exports.boolean(),
9834
+ transfers: external_exports.array(external_exports.any()).nullable()
9835
+ };
9836
+ function isRecord(value) {
9837
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
9838
+ }
9839
+ function cloneJSON(value) {
9840
+ return JSON.parse(JSON.stringify(value));
9841
+ }
9842
+ function makeSimpleFieldSchema(type) {
9843
+ return external_exports.object({
9844
+ ...fieldCommonShape,
9845
+ type: external_exports.literal(type),
9846
+ options: external_exports.record(external_exports.string(), external_exports.any()).nullable(),
9847
+ children: external_exports.null()
9848
+ }).strict();
9849
+ }
9850
+ function makeEnumFieldSchema(type) {
9851
+ const itemSchema = type === "status" ? statusItemSchema : enumItemSchema;
9852
+ return external_exports.object({
9853
+ ...fieldCommonShape,
9854
+ type: external_exports.literal(type),
9855
+ options: external_exports.object({
9856
+ options: external_exports.object({
9857
+ items: external_exports.array(itemSchema).min(1)
9858
+ }).passthrough()
9859
+ }).passthrough(),
9860
+ children: external_exports.null()
9861
+ }).strict();
9862
+ }
9863
+ var simpleFieldSchemas = simpleFieldTypes.map((type) => makeSimpleFieldSchema(type));
9864
+ var enumFieldSchemas = enumFieldTypes.map((type) => makeEnumFieldSchema(type));
9865
+ var formulaFieldSchema = external_exports.object({
9866
+ ...fieldCommonShape,
9867
+ type: external_exports.literal("formula"),
9868
+ options: external_exports.object({
9869
+ formula: formulaSchema
9870
+ }).passthrough(),
9871
+ children: external_exports.null()
9872
+ }).strict();
9873
+ var relationFieldSchemas = relationFieldTypes.map(
9874
+ (type) => external_exports.object({
9875
+ ...fieldCommonShape,
9876
+ type: external_exports.literal(type),
9877
+ options: external_exports.object({
9878
+ relation: relationOptionsSchema,
9879
+ mode: linkModeSchema.optional(),
9880
+ structure: linkStructureSchema.optional(),
9881
+ multiple: external_exports.boolean().optional(),
9882
+ backfill: external_exports.array(external_exports.object({
9883
+ id: external_exports.number().int().min(1001),
9884
+ local_id: external_exports.number().int().min(1001)
9885
+ }).strict()).optional(),
9886
+ addValue: external_exports.boolean().optional(),
9887
+ placeholder: external_exports.string().optional()
9888
+ }).passthrough(),
9889
+ children: external_exports.null()
9890
+ }).strict()
9891
+ );
9892
+ var lookupFieldSchema = external_exports.object({
9893
+ ...fieldCommonShape,
9894
+ type: external_exports.literal("lookup"),
9895
+ options: external_exports.object({
9896
+ relation: lookupRelationSchema
9897
+ }).passthrough(),
9898
+ children: external_exports.null()
9899
+ }).strict();
9900
+ var relationFieldFieldSchema = external_exports.object({
9901
+ ...fieldCommonShape,
9902
+ type: external_exports.literal("relationfield"),
9903
+ options: external_exports.object({
9904
+ relation: relationFieldRelationSchema
9905
+ }).passthrough(),
9906
+ children: external_exports.null()
9907
+ }).strict();
9908
+ var rollupFieldSchema = external_exports.object({
9909
+ ...fieldCommonShape,
9910
+ type: external_exports.literal("rollup"),
9911
+ options: external_exports.object({
9912
+ relation: lookupRelationSchema,
9913
+ aggregation_formula: formulaSchema
9914
+ }).passthrough(),
9915
+ children: external_exports.null()
9916
+ }).strict();
9917
+ var serialNumberFieldSchema = external_exports.object({
9918
+ ...fieldCommonShape,
9919
+ type: external_exports.literal("serialnumber"),
9920
+ options: external_exports.object({
9921
+ parts: external_exports.array(serialPartSchema).min(1)
9922
+ }).passthrough(),
9923
+ children: external_exports.null()
9924
+ }).strict();
9925
+ var genericFieldSchema = external_exports.object({
9926
+ ...fieldCommonShape,
9927
+ type: external_exports.string().min(1).refine((value) => !strictFieldTypes.has(value), {
9928
+ message: "known field type must use dedicated schema"
9929
+ }),
9930
+ options: external_exports.record(external_exports.string(), external_exports.any()).nullable(),
9931
+ children: external_exports.null()
9932
+ }).strict();
9933
+ var baseFieldSchemaVariants = [
9934
+ ...simpleFieldSchemas,
9935
+ ...enumFieldSchemas,
9936
+ ...relationFieldSchemas,
9937
+ formulaFieldSchema,
9938
+ lookupFieldSchema,
9939
+ relationFieldFieldSchema,
9940
+ rollupFieldSchema,
9941
+ serialNumberFieldSchema,
9942
+ genericFieldSchema
9943
+ ];
9944
+ var BaseFieldSchema = external_exports.union(baseFieldSchemaVariants);
9945
+ var SubformFieldSchema = external_exports.lazy(
9946
+ () => external_exports.object({
9947
+ ...fieldCommonShape,
9948
+ type: external_exports.literal("subform"),
9949
+ options: external_exports.record(external_exports.string(), external_exports.any()).nullable(),
9950
+ children: external_exports.array(BaseFieldSchema),
9951
+ layout: external_exports.array(external_exports.array(external_exports.number().int().min(1001))).optional()
9952
+ }).strict()
9953
+ );
9954
+ var FieldVOSchema = external_exports.lazy(
9955
+ () => external_exports.union([
9956
+ SubformFieldSchema,
9957
+ BaseFieldSchema
9958
+ ])
9959
+ );
9960
+ function normalizeEntitySaveInput(value) {
9961
+ if (!isRecord(value)) {
9962
+ return value;
9963
+ }
9964
+ const entity = cloneJSON(value);
9965
+ if (entity.layout === null) {
9966
+ entity.layout = [];
9967
+ }
9968
+ if (entity.current_serial === null) {
9969
+ delete entity.current_serial;
9970
+ }
9971
+ if (isRecord(entity.options)) {
9972
+ if (entity.options.Buttons === null) {
9973
+ entity.options.Buttons = [];
9974
+ }
9975
+ if (entity.options.CustomActions === null) {
9976
+ entity.options.CustomActions = [];
9977
+ }
9978
+ if (entity.options.FrontendEvents === null) {
9979
+ entity.options.FrontendEvents = [];
9980
+ }
9981
+ if (entity.options.misc === null) {
9982
+ entity.options.misc = {};
9983
+ }
9984
+ if (isRecord(entity.options.TitleFormat)) {
9985
+ if (entity.options.TitleFormat.Fields === null) {
9986
+ entity.options.TitleFormat.Fields = [];
9987
+ }
9988
+ if (entity.options.TitleFormat.Parts === null) {
9989
+ entity.options.TitleFormat.Parts = [];
9990
+ }
9991
+ }
9992
+ }
9993
+ return entity;
9994
+ }
9995
+ function collectAllFieldIds(fields) {
9996
+ const out = [];
9997
+ for (const field of fields) {
9998
+ out.push(field.id);
9999
+ if (field.type === "subform" && Array.isArray(field.children)) {
10000
+ for (const child of field.children) {
10001
+ out.push(child.id);
10002
+ }
10003
+ }
10004
+ }
10005
+ return out;
10006
+ }
10007
+ function collectTopLevelFieldIds(fields) {
10008
+ return fields.map((field) => field.id);
10009
+ }
10010
+ function extractFormulaFieldIds(value) {
10011
+ if (!value || typeof value !== "string") return [];
10012
+ const ids = [...value.matchAll(/:(\d+)\]\]/g)].map((match) => Number(match[1]));
10013
+ return ids.filter((id) => Number.isInteger(id) && id > 0);
10014
+ }
10015
+ var EntitySaveReqVOBaseSchema = external_exports.object({
10016
+ app_id: external_exports.number().optional(),
10017
+ current_serial: EntitySerialUpdaterVOSchema.optional(),
10018
+ field_id_seq: external_exports.number().int(),
10019
+ fields: external_exports.array(FieldVOSchema),
10020
+ id: external_exports.number().optional(),
10021
+ layout: external_exports.array(external_exports.array(external_exports.number().int().min(1001))).optional(),
10022
+ name: external_exports.string().optional(),
10023
+ options: EntityOptionsSchema.optional(),
10024
+ prototypes: external_exports.array(external_exports.number()).optional(),
10025
+ schema_version: external_exports.number().optional(),
10026
+ workflow_enabled: external_exports.boolean().optional()
10027
+ }).strict().superRefine((entity, ctx) => {
10028
+ const fields = entity.fields;
10029
+ const topLevelIds = new Set(collectTopLevelFieldIds(fields));
10030
+ const allIds = collectAllFieldIds(fields);
10031
+ const allIdSet = /* @__PURE__ */ new Set();
10032
+ for (const id of allIds) {
10033
+ if (allIdSet.has(id)) {
10034
+ ctx.addIssue({
10035
+ code: external_exports.ZodIssueCode.custom,
10036
+ path: ["fields"],
10037
+ message: `duplicate field id: ${id}`
10038
+ });
10039
+ }
10040
+ allIdSet.add(id);
10041
+ }
10042
+ const maxFieldId = allIds.length > 0 ? Math.max(...allIds) : 1e3;
10043
+ if (entity.field_id_seq <= maxFieldId) {
10044
+ ctx.addIssue({
10045
+ code: external_exports.ZodIssueCode.custom,
10046
+ path: ["field_id_seq"],
10047
+ message: `field_id_seq must be greater than max field id ${maxFieldId}`
10048
+ });
10049
+ }
10050
+ for (const [rowIndex, row] of (entity.layout ?? []).entries()) {
10051
+ for (const [colIndex, fieldId] of row.entries()) {
10052
+ if (!topLevelIds.has(fieldId)) {
10053
+ ctx.addIssue({
10054
+ code: external_exports.ZodIssueCode.custom,
10055
+ path: ["layout", rowIndex, colIndex],
10056
+ message: `layout references unknown top-level field id ${fieldId}`
10057
+ });
10058
+ }
10059
+ }
10060
+ }
10061
+ for (const [fieldIndex, field] of fields.entries()) {
10062
+ if (field.type !== "subform" && field.children !== null) {
10063
+ ctx.addIssue({
10064
+ code: external_exports.ZodIssueCode.custom,
10065
+ path: ["fields", fieldIndex, "children"],
10066
+ message: "only subform field can define children"
10067
+ });
10068
+ }
10069
+ if (field.type === "subform") {
10070
+ const childIds = new Set((field.children ?? []).map((child) => child.id));
10071
+ for (const [rowIndex, row] of (field.layout ?? []).entries()) {
10072
+ for (const [colIndex, fieldId] of row.entries()) {
10073
+ if (!childIds.has(fieldId)) {
10074
+ ctx.addIssue({
10075
+ code: external_exports.ZodIssueCode.custom,
10076
+ path: ["fields", fieldIndex, "layout", rowIndex, colIndex],
10077
+ message: `subform layout references non-child field id ${fieldId}`
10078
+ });
10079
+ }
10080
+ }
10081
+ }
10082
+ }
10083
+ if (field.type === "lookup" || field.type === "rollup") {
10084
+ const localLinktoFieldId = field.options?.relation?.local_linkto_field_id;
10085
+ if (typeof localLinktoFieldId === "number") {
10086
+ const target = fields.find((item) => item.id === localLinktoFieldId);
10087
+ if (!target || target.type !== "linkto") {
10088
+ ctx.addIssue({
10089
+ code: external_exports.ZodIssueCode.custom,
10090
+ path: ["fields", fieldIndex, "options", "relation", "local_linkto_field_id"],
10091
+ message: "local_linkto_field_id must reference a linkto field"
10092
+ });
10093
+ }
10094
+ }
10095
+ }
10096
+ if (field.type === "formula" || field.type === "rollup") {
10097
+ const formulaCode = field.type === "formula" ? field.options?.formula?.code : field.options?.aggregation_formula?.code;
10098
+ for (const referencedFieldId of extractFormulaFieldIds(formulaCode)) {
10099
+ if (!allIdSet.has(referencedFieldId)) {
10100
+ ctx.addIssue({
10101
+ code: external_exports.ZodIssueCode.custom,
10102
+ path: ["fields", fieldIndex, "options"],
10103
+ message: `formula references unknown field id ${referencedFieldId}`
10104
+ });
10105
+ }
10106
+ }
10107
+ }
10108
+ }
10109
+ });
10110
+ var EntitySaveReqVOSchemaOverride = external_exports.preprocess(normalizeEntitySaveInput, EntitySaveReqVOBaseSchema);
10111
+ var queryOrderSchema = external_exports.object({
10112
+ column: external_exports.string().min(1),
10113
+ order: external_exports.string().min(1)
10114
+ }).strict();
10115
+ var AppCreateByTenantsReqVOSchemaOverride = external_exports.object({
10116
+ icon_color: external_exports.string().optional(),
10117
+ icon_name: external_exports.string().optional(),
10118
+ name: external_exports.string().min(1).max(20)
10119
+ }).strict();
10120
+ var EntityQueryReqVOSchemaOverride = external_exports.object({
10121
+ limit: external_exports.number().int().positive().optional(),
10122
+ offset: external_exports.number().int().min(0).optional(),
10123
+ policy_id: external_exports.string().optional(),
10124
+ search: external_exports.record(external_exports.string(), external_exports.any()).optional(),
10125
+ sorts: external_exports.array(queryOrderSchema).optional(),
10126
+ view_mode: external_exports.string().optional(),
10127
+ viewMode: external_exports.never().optional(),
10128
+ withSubForm: external_exports.boolean().optional()
10129
+ }).strict();
10130
+ var InsertReqVOSchemaOverride = external_exports.object({
10131
+ policy_id: external_exports.string().optional(),
10132
+ fields: external_exports.record(external_exports.string(), external_exports.any())
10133
+ }).strict().superRefine((body, ctx) => {
10134
+ for (const key of Object.keys(body.fields ?? {})) {
10135
+ if (!/^\d+$/.test(key)) {
10136
+ ctx.addIssue({
10137
+ code: external_exports.ZodIssueCode.custom,
10138
+ path: ["fields", key],
10139
+ message: "row fields must use numeric field ids as keys"
10140
+ });
10141
+ }
10142
+ }
10143
+ });
10144
+ var EntityUpdateReqVOSchemaOverride = external_exports.object({
10145
+ changed_fields: external_exports.array(external_exports.number().int().positive()).min(1),
10146
+ data: external_exports.record(external_exports.string(), external_exports.any()),
10147
+ policy_id: external_exports.string().optional(),
10148
+ tags: external_exports.array(external_exports.string()).optional(),
10149
+ fields: external_exports.never().optional()
10150
+ }).strict().superRefine((body, ctx) => {
10151
+ const dataFieldIds = /* @__PURE__ */ new Set();
10152
+ for (const key of Object.keys(body.data ?? {})) {
10153
+ if (!/^\d+$/.test(key)) {
10154
+ ctx.addIssue({
10155
+ code: external_exports.ZodIssueCode.custom,
10156
+ path: ["data", key],
10157
+ message: "row data must use numeric field ids as keys"
10158
+ });
10159
+ continue;
10160
+ }
10161
+ dataFieldIds.add(Number(key));
10162
+ }
10163
+ for (const [index, fieldId] of body.changed_fields.entries()) {
10164
+ if (!dataFieldIds.has(fieldId)) {
10165
+ ctx.addIssue({
10166
+ code: external_exports.ZodIssueCode.custom,
10167
+ path: ["changed_fields", index],
10168
+ message: `changed_fields references ${fieldId}, but data does not contain that field id`
10169
+ });
10170
+ }
10171
+ }
10172
+ });
10173
+ var entitySelectionSortSchema = external_exports.object({
10174
+ column: external_exports.string().min(1),
10175
+ order: external_exports.string().min(1)
10176
+ }).strict();
10177
+ var entitySelectionIdsSchema = external_exports.object({
10178
+ type: external_exports.literal("ids"),
10179
+ ids: external_exports.array(external_exports.number().int().positive()).min(1),
10180
+ length: external_exports.number().int().positive().optional(),
10181
+ sorts: external_exports.array(entitySelectionSortSchema).optional(),
10182
+ condition: external_exports.record(external_exports.string(), external_exports.any()).optional()
10183
+ }).strict().superRefine((selection, ctx) => {
10184
+ if (selection.length !== void 0 && selection.length !== selection.ids.length) {
10185
+ ctx.addIssue({
10186
+ code: external_exports.ZodIssueCode.custom,
10187
+ path: ["length"],
10188
+ message: "length must match ids.length when provided"
10189
+ });
10190
+ }
10191
+ });
10192
+ var entitySelectionConditionSchema = external_exports.object({
10193
+ type: external_exports.literal("condition"),
10194
+ length: external_exports.number().int().positive(),
10195
+ condition: external_exports.record(external_exports.string(), external_exports.any()).superRefine((condition, ctx) => {
10196
+ if (!Object.keys(condition).length) {
10197
+ ctx.addIssue({
10198
+ code: external_exports.ZodIssueCode.custom,
10199
+ message: "condition selection requires a non-empty condition object"
10200
+ });
10201
+ return;
10202
+ }
10203
+ if (condition.selectAll !== true) {
10204
+ ctx.addIssue({
10205
+ code: external_exports.ZodIssueCode.custom,
10206
+ path: ["selectAll"],
10207
+ message: "condition selection must set selectAll to true"
10208
+ });
10209
+ }
10210
+ const filterKeys = Object.keys(condition).filter((key) => key !== "selectAll");
10211
+ if (filterKeys.length === 0) {
10212
+ return;
10213
+ }
10214
+ const hasSupportedQueryKey = filterKeys.some((key) => key === "q" || key === "tab" || key.startsWith("filter_"));
10215
+ if (!hasSupportedQueryKey) {
10216
+ ctx.addIssue({
10217
+ code: external_exports.ZodIssueCode.custom,
10218
+ message: "condition selection extra keys must use q, tab, or filter_*"
10219
+ });
10220
+ }
10221
+ }),
10222
+ sorts: external_exports.array(entitySelectionSortSchema).optional(),
10223
+ ids: external_exports.array(external_exports.number().int().positive()).optional()
10224
+ }).strict();
10225
+ var entitySelectionAllSchema = external_exports.object({
10226
+ type: external_exports.literal("all"),
10227
+ length: external_exports.number().int().positive().optional(),
10228
+ ids: external_exports.array(external_exports.number().int().positive()).optional(),
10229
+ condition: external_exports.record(external_exports.string(), external_exports.any()).optional(),
10230
+ sorts: external_exports.array(entitySelectionSortSchema).optional()
10231
+ }).strict();
10232
+ var entitySelectionSchema = external_exports.union([
10233
+ entitySelectionIdsSchema,
10234
+ entitySelectionConditionSchema,
10235
+ entitySelectionAllSchema
10236
+ ]);
10237
+ var entitySelectionLooseSchema = external_exports.union([
10238
+ entitySelectionSchema,
10239
+ entitySelectionAllSchema
10240
+ ]);
10241
+ var EntitySelectionActionReqVOSchemaOverride = external_exports.lazy(
10242
+ () => external_exports.object({
10243
+ limit: external_exports.number().int().positive().optional(),
10244
+ offset: external_exports.number().int().min(0).optional(),
10245
+ policy_id: external_exports.string().optional(),
10246
+ selection: entitySelectionSchema
10247
+ }).strict()
10248
+ );
10249
+ var bulkUpdateActionSchema = external_exports.object({
10250
+ type: external_exports.string().min(1),
10251
+ value: external_exports.any().optional(),
10252
+ text_search: external_exports.string().optional(),
10253
+ text_replace: external_exports.string().optional(),
10254
+ text_append: external_exports.string().optional(),
10255
+ text_prepend: external_exports.string().optional(),
10256
+ number_add: external_exports.number().optional(),
10257
+ number_mul: external_exports.number().optional(),
10258
+ number_sub: external_exports.number().optional(),
10259
+ datetime_add: external_exports.number().optional(),
10260
+ datetime_sub: external_exports.number().optional()
10261
+ }).strict().superRefine((action, ctx) => {
10262
+ switch (action.type) {
10263
+ case "set":
10264
+ if (!("value" in action)) {
10265
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["value"], message: "set action requires value" });
10266
+ }
10267
+ break;
10268
+ case "text_replace":
10269
+ if (!action.text_search) {
10270
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["text_search"], message: "text_replace requires text_search" });
10271
+ }
10272
+ if (!action.text_replace) {
10273
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["text_replace"], message: "text_replace requires text_replace" });
10274
+ }
10275
+ break;
10276
+ case "text_append":
10277
+ if (!action.text_append) {
10278
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["text_append"], message: "text_append requires text_append" });
10279
+ }
10280
+ break;
10281
+ case "text_prepend":
10282
+ if (!action.text_prepend) {
10283
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["text_prepend"], message: "text_prepend requires text_prepend" });
10284
+ }
10285
+ break;
10286
+ case "number_add":
10287
+ if (typeof action.number_add !== "number") {
10288
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["number_add"], message: "number_add requires number_add" });
10289
+ }
10290
+ break;
10291
+ case "number_sub":
10292
+ if (typeof action.number_sub !== "number") {
10293
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["number_sub"], message: "number_sub requires number_sub" });
10294
+ }
10295
+ break;
10296
+ case "number_mul":
10297
+ if (typeof action.number_mul !== "number") {
10298
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["number_mul"], message: "number_mul requires number_mul" });
10299
+ }
10300
+ break;
10301
+ case "datetime_add":
10302
+ if (typeof action.datetime_add !== "number") {
10303
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["datetime_add"], message: "datetime_add requires datetime_add" });
10304
+ }
10305
+ break;
10306
+ case "datetime_sub":
10307
+ if (typeof action.datetime_sub !== "number") {
10308
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["datetime_sub"], message: "datetime_sub requires datetime_sub" });
10309
+ }
10310
+ break;
10311
+ case "datetime_now":
10312
+ case "formula":
10313
+ break;
10314
+ default:
10315
+ ctx.addIssue({ code: external_exports.ZodIssueCode.custom, path: ["type"], message: `unsupported bulk update action type: ${action.type}` });
10316
+ }
10317
+ });
10318
+ var EntityBulkUpdateReqVOSchemaOverride = external_exports.object({
10319
+ policy_id: external_exports.string().optional(),
10320
+ selection: entitySelectionSchema,
10321
+ fields: external_exports.array(external_exports.object({
10322
+ id: external_exports.number().int().min(1001),
10323
+ action: bulkUpdateActionSchema
10324
+ }).strict()).min(1)
10325
+ }).strict();
10326
+ var EntityTagsSelectionReqVOSchemaOverride = external_exports.object({
10327
+ policy_id: external_exports.string(),
10328
+ selection: entitySelectionSchema
10329
+ }).strict();
10330
+ var EntityTagsBatchReqVOSchemaOverride = external_exports.object({
10331
+ policy_id: external_exports.string(),
10332
+ selection: entitySelectionSchema,
10333
+ tag_names: external_exports.array(external_exports.string().min(1)).min(1)
10334
+ }).strict();
10335
+ var EntityInvokeBatchOperatorReqVOSchemaOverride = external_exports.object({
10336
+ id: external_exports.string().min(1),
10337
+ data: external_exports.object({
10338
+ action_id: external_exports.string().min(1),
10339
+ policy_id: external_exports.string().optional(),
10340
+ selection: entitySelectionLooseSchema
10341
+ }).strict(),
10342
+ params: external_exports.record(external_exports.string(), external_exports.any()).optional()
10343
+ }).strict();
10344
+
9664
10345
  // src/runtime/zod_registry.ts
10346
+ var schemaOverrides = {
10347
+ AppCreateByTenantsReqVO: AppCreateByTenantsReqVOSchemaOverride,
10348
+ EntityBulkUpdateReqVO: EntityBulkUpdateReqVOSchemaOverride,
10349
+ "EntityInvokeBatchOperatorReqVO & InvokeRequestVO": EntityInvokeBatchOperatorReqVOSchemaOverride,
10350
+ EntityQueryReqVO: EntityQueryReqVOSchemaOverride,
10351
+ EntitySaveReqVO: EntitySaveReqVOSchemaOverride,
10352
+ EntitySelectionActionReqVO: EntitySelectionActionReqVOSchemaOverride,
10353
+ "{ policy_id: string selection: any }": EntityTagsSelectionReqVOSchemaOverride,
10354
+ "{ policy_id: string selection: any tag_names: string[] }": EntityTagsBatchReqVOSchemaOverride,
10355
+ EntityUpdateReqVO: EntityUpdateReqVOSchemaOverride,
10356
+ InsertReqVO: InsertReqVOSchemaOverride
10357
+ };
9665
10358
  function getBodySchema(typeName) {
9666
10359
  const name = String(typeName || "").trim();
9667
10360
  if (!name) return null;
10361
+ if (schemaOverrides[name]) {
10362
+ return schemaOverrides[name];
10363
+ }
9668
10364
  return bodySchemas[name] ?? null;
9669
10365
  }
9670
10366
  function getUnsupportedBodySchemaReason(typeName) {
@@ -9672,6 +10368,59 @@ function getUnsupportedBodySchemaReason(typeName) {
9672
10368
  if (!name) return null;
9673
10369
  return unsupportedBodySchemas[name] ?? null;
9674
10370
  }
10371
+ var examplesIndexDocHint = {
10372
+ title: "Examples index",
10373
+ file: "/opt/arcubase-sdk/docs/runtime-reference/examples/README.md"
10374
+ };
10375
+ var docHintIndex = {
10376
+ AppCreateByTenantsReqVO: [
10377
+ { title: "Table lifecycle", file: "/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md" },
10378
+ examplesIndexDocHint
10379
+ ],
10380
+ AppCreateEntityReqVO: [
10381
+ { title: "Table lifecycle", file: "/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md" },
10382
+ examplesIndexDocHint
10383
+ ],
10384
+ EntitySaveReqVO: [
10385
+ { title: "Entity schema", file: "/opt/arcubase-sdk/docs/runtime-reference/entity-schema.md" },
10386
+ examplesIndexDocHint
10387
+ ],
10388
+ EntityQueryReqVO: [
10389
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10390
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10391
+ examplesIndexDocHint
10392
+ ],
10393
+ EntityUpdateReqVO: [
10394
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10395
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
10396
+ examplesIndexDocHint
10397
+ ],
10398
+ EntitySelectionActionReqVO: [
10399
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10400
+ examplesIndexDocHint
10401
+ ],
10402
+ InsertReqVO: [
10403
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10404
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
10405
+ examplesIndexDocHint
10406
+ ],
10407
+ EntityBulkUpdateReqVO: [
10408
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10409
+ examplesIndexDocHint
10410
+ ],
10411
+ "EntityInvokeBatchOperatorReqVO & InvokeRequestVO": [
10412
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10413
+ examplesIndexDocHint
10414
+ ],
10415
+ "{ policy_id: string selection: any }": [
10416
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10417
+ examplesIndexDocHint
10418
+ ],
10419
+ "{ policy_id: string selection: any tag_names: string[] }": [
10420
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10421
+ examplesIndexDocHint
10422
+ ]
10423
+ };
9675
10424
  function getTypeHints(typeName) {
9676
10425
  const name = String(typeName || "").trim();
9677
10426
  if (!name) return [];
@@ -9694,41 +10443,273 @@ function getTypeHints(typeName) {
9694
10443
  }
9695
10444
  return out;
9696
10445
  }
10446
+ function getDocHints(typeName) {
10447
+ const name = String(typeName || "").trim();
10448
+ if (!name) return [];
10449
+ return docHintIndex[name] ?? [];
10450
+ }
10451
+ var commandDocHintIndex = {
10452
+ "admin.app.createAppByTenants": [
10453
+ { title: "Table lifecycle", file: "/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md" },
10454
+ examplesIndexDocHint
10455
+ ],
10456
+ "admin.app.createEntity": [
10457
+ { title: "Table lifecycle", file: "/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md" },
10458
+ examplesIndexDocHint
10459
+ ],
10460
+ "admin.entity.adminGetEntityInfo": [
10461
+ { title: "Table lifecycle", file: "/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md" },
10462
+ examplesIndexDocHint
10463
+ ],
10464
+ "admin.entity.adminSaveEntity": [
10465
+ { title: "Entity schema", file: "/opt/arcubase-sdk/docs/runtime-reference/entity-schema.md" },
10466
+ { title: "Table lifecycle", file: "/opt/arcubase-sdk/docs/runtime-reference/table-lifecycle.md" },
10467
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10468
+ examplesIndexDocHint
10469
+ ],
10470
+ "user.workflow.insertEntity": [
10471
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10472
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
10473
+ examplesIndexDocHint
10474
+ ],
10475
+ "user.workflow.queryEntity": [
10476
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10477
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10478
+ examplesIndexDocHint
10479
+ ],
10480
+ "user.workflow.getEntityRow": [
10481
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10482
+ examplesIndexDocHint
10483
+ ],
10484
+ "user.workflow.updateEntityRow": [
10485
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10486
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
10487
+ examplesIndexDocHint
10488
+ ],
10489
+ "user.workflow.queryEntitySelection": [
10490
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10491
+ examplesIndexDocHint
10492
+ ],
10493
+ "user.workflow.deleteEntityRow": [
10494
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10495
+ examplesIndexDocHint
10496
+ ],
10497
+ "user.workflow.restoreEntityRow": [
10498
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" },
10499
+ examplesIndexDocHint
10500
+ ],
10501
+ "user.entity.updateEntityBulk": [
10502
+ { title: "Search and bulk actions", file: "/opt/arcubase-sdk/docs/runtime-reference/search-and-bulk-actions.md" },
10503
+ examplesIndexDocHint
10504
+ ]
10505
+ };
10506
+ function getCommandDocHints(operation) {
10507
+ const key = String(operation || "").trim();
10508
+ if (!key) return [];
10509
+ return commandDocHintIndex[key] ?? [];
10510
+ }
10511
+
10512
+ // src/runtime/upload.ts
10513
+ import fs2 from "fs";
10514
+ import path from "path";
10515
+ function readUploadSource(sourcePath) {
10516
+ const absolutePath = path.resolve(sourcePath);
10517
+ if (!fs2.existsSync(absolutePath)) {
10518
+ throw new CLIError("UPLOAD_SOURCE_NOT_FOUND", `upload source file does not exist: ${sourcePath}`, 2);
10519
+ }
10520
+ const stat = fs2.statSync(absolutePath);
10521
+ if (!stat.isFile()) {
10522
+ throw new CLIError("UPLOAD_SOURCE_INVALID", `upload source path must be a file: ${sourcePath}`, 2);
10523
+ }
10524
+ return {
10525
+ absolutePath,
10526
+ bytes: fs2.readFileSync(absolutePath)
10527
+ };
10528
+ }
10529
+ function parseJSONResponse(raw) {
10530
+ try {
10531
+ return raw ? JSON.parse(raw) : null;
10532
+ } catch {
10533
+ return raw;
10534
+ }
10535
+ }
10536
+ function requireTokenPayload(payload) {
10537
+ if (payload && typeof payload === "object" && payload.error) {
10538
+ const upstreamError = payload.error;
10539
+ const message = typeof upstreamError === "object" && upstreamError && "message" in upstreamError && typeof upstreamError.message === "string" ? upstreamError.message : typeof upstreamError === "string" ? upstreamError : JSON.stringify(upstreamError);
10540
+ throw new CLIError("UPSTREAM_BODY_ERROR", message, 1, {
10541
+ endpoint: "/upload/token",
10542
+ method: "POST",
10543
+ traceId: typeof payload.trace_id === "string" ? payload.trace_id : void 0,
10544
+ upstreamError
10545
+ });
10546
+ }
10547
+ if (!payload?.data || typeof payload.data !== "object") {
10548
+ throw new CLIError("UPLOAD_TOKEN_INVALID", "upload token response did not include data", 1);
10549
+ }
10550
+ return payload.data;
10551
+ }
10552
+ function renderUploadHelp() {
10553
+ return [
10554
+ "arcubase upload <local-file> [flags]",
10555
+ "",
10556
+ "purpose:",
10557
+ " - upload one local file through Arcubase",
10558
+ " - return a field value that can be used directly in file or image row payloads",
10559
+ "",
10560
+ "flags:",
10561
+ " --filename <name> override the uploaded filename stored in Arcubase",
10562
+ " --global request a global upload token instead of a tenant-scoped token",
10563
+ "",
10564
+ "result shape:",
10565
+ " - data is a JSON array",
10566
+ " - use that array directly as the value of a file or image field in insert/update payloads",
10567
+ "",
10568
+ "example:",
10569
+ " arcubase upload ./contract.pdf",
10570
+ " arcubase upload ./photo.jpg --filename lead-photo.jpg",
10571
+ "",
10572
+ "docs:",
10573
+ " - Uploads: /opt/arcubase-sdk/docs/runtime-reference/uploads.md",
10574
+ " - Row CRUD: /opt/arcubase-sdk/docs/runtime-reference/row-crud.md",
10575
+ " - File field: /opt/arcubase-sdk/docs/runtime-reference/entity-schema/file.md",
10576
+ " - Image field: /opt/arcubase-sdk/docs/runtime-reference/entity-schema/image.md"
10577
+ ].join("\n");
10578
+ }
10579
+ async function uploadLocalFile(env, sourcePath, options, fetchImpl = fetch) {
10580
+ const { absolutePath, bytes } = readUploadSource(sourcePath);
10581
+ const filename = options.filename && options.filename.trim() || path.basename(absolutePath);
10582
+ const tokenResponse = await fetchImpl(buildURL(env.ARCUBASE_BASE_URL, "/upload/token"), {
10583
+ method: "POST",
10584
+ headers: {
10585
+ ...buildRequestHeaders(env),
10586
+ "Content-Type": "application/json"
10587
+ },
10588
+ body: JSON.stringify({
10589
+ global: Boolean(options.global)
10590
+ })
10591
+ });
10592
+ const tokenRaw = await tokenResponse.text();
10593
+ const tokenParsed = parseJSONResponse(tokenRaw);
10594
+ if (!tokenResponse.ok) {
10595
+ throw new CLIError("UPLOAD_TOKEN_REQUEST_FAILED", typeof tokenParsed === "string" ? tokenParsed : JSON.stringify(tokenParsed), 1, {
10596
+ endpoint: "/upload/token",
10597
+ method: "POST"
10598
+ });
10599
+ }
10600
+ const tokenData = requireTokenPayload(tokenParsed);
10601
+ if (tokenData.mode !== "aliyun-oss" && tokenData.mode !== "s3-post-policy") {
10602
+ throw new CLIError("UPLOAD_MODE_UNSUPPORTED", `unsupported upload mode: ${String(tokenData.mode || "")}`, 1);
10603
+ }
10604
+ if (!tokenData.token_data?.dir || !tokenData.token_data.policy || !tokenData.token_data.host) {
10605
+ throw new CLIError("UPLOAD_TOKEN_INVALID", "upload token response is missing required token_data fields", 1);
10606
+ }
10607
+ if (tokenData.id === void 0 || tokenData.id === null || tokenData.id === "") {
10608
+ throw new CLIError("UPLOAD_TOKEN_INVALID", "upload token response is missing id", 1);
10609
+ }
10610
+ const form = new FormData();
10611
+ form.append("key", `${tokenData.token_data.dir}${filename}`);
10612
+ form.append("policy", tokenData.token_data.policy);
10613
+ if (tokenData.mode === "aliyun-oss") {
10614
+ if (!tokenData.token_data.accessid || !tokenData.token_data.signature) {
10615
+ throw new CLIError("UPLOAD_TOKEN_INVALID", "upload token response is missing required token_data fields", 1);
10616
+ }
10617
+ form.append("OSSAccessKeyId", tokenData.token_data.accessid);
10618
+ form.append("success_action_status", "200");
10619
+ form.append("signature", tokenData.token_data.signature);
10620
+ } else {
10621
+ if (!tokenData.token_data.x_amz_algorithm || !tokenData.token_data.x_amz_credential || !tokenData.token_data.x_amz_date || !tokenData.token_data.x_amz_signature) {
10622
+ throw new CLIError("UPLOAD_TOKEN_INVALID", "upload token response is missing required s3 token_data fields", 1);
10623
+ }
10624
+ form.append("x-amz-algorithm", tokenData.token_data.x_amz_algorithm);
10625
+ form.append("x-amz-credential", tokenData.token_data.x_amz_credential);
10626
+ form.append("x-amz-date", tokenData.token_data.x_amz_date);
10627
+ form.append("x-amz-signature", tokenData.token_data.x_amz_signature);
10628
+ }
10629
+ form.append("file", new Blob([new Uint8Array(bytes)]), filename);
10630
+ const uploadResponse = await fetchImpl(tokenData.token_data.host, {
10631
+ method: "POST",
10632
+ body: form
10633
+ });
10634
+ const uploadRaw = await uploadResponse.text();
10635
+ if (!uploadResponse.ok) {
10636
+ throw new CLIError("UPLOAD_TRANSFER_FAILED", uploadRaw || `upload failed with status ${uploadResponse.status}`, 1, {
10637
+ endpoint: tokenData.token_data.host,
10638
+ method: "POST"
10639
+ });
10640
+ }
10641
+ return [
10642
+ {
10643
+ upload_id: tokenData.id,
10644
+ file: filename,
10645
+ file_name: filename,
10646
+ meta: {}
10647
+ }
10648
+ ];
10649
+ }
9697
10650
 
9698
10651
  // src/runtime/execute.ts
9699
10652
  function renderRootHelp(scope) {
9700
10653
  const binary = scope === "admin" ? "arcubase-admin" : "arcubase";
10654
+ const items = listModules(scope).map((item) => ` - ${item}`);
9701
10655
  return [
9702
10656
  `${binary} <module> <command> [flags]`,
9703
10657
  "",
10658
+ ...scope === "admin" ? ["use arcubase-admin for app, entity shell, and schema management", ""] : ["use arcubase for row CRUD, search, selection actions, and bulk actions", ""],
9704
10659
  "modules:",
9705
- ...listModules(scope).map((item) => ` - ${item}`)
10660
+ ...items,
10661
+ ...scope === "user" ? ["", "special commands:", " - upload <local-file>"] : []
9706
10662
  ].join("\n");
9707
10663
  }
9708
10664
  function renderModuleHelp(scope, moduleName) {
9709
10665
  const items = listModuleCommands(scope, moduleName);
9710
10666
  if (items.length === 0) {
9711
- throw new CLIError("UNKNOWN_MODULE", `unknown module: ${moduleName}`, 2);
10667
+ const suggestions = findCommandSuggestions(moduleName, "").map((item) => `${item.binary} ${item.moduleName} ${item.commandName}`);
10668
+ throw new CLIError("UNKNOWN_MODULE", `unknown module: ${moduleName}`, 2, suggestions.length > 0 ? { suggestions } : void 0);
9712
10669
  }
9713
10670
  return [
9714
10671
  `${scope === "admin" ? "arcubase-admin" : "arcubase"} ${moduleName} <command> [flags]`,
9715
10672
  "",
10673
+ ...scope === "admin" ? ["use arcubase-admin for app, entity shell, and schema management", ""] : ["use arcubase for row CRUD, search, selection actions, and bulk actions", ""],
9716
10674
  "commands:",
9717
10675
  ...items.map((item) => ` - ${item.commandPath[1]}`)
9718
10676
  ].join("\n");
9719
10677
  }
10678
+ function buildUnknownCommandDetails(scope, moduleName, commandName) {
10679
+ const suggestions = findCommandSuggestions(moduleName, commandName).map((item) => `${item.binary} ${item.moduleName} ${item.commandName}`);
10680
+ if (scope === "admin" && moduleName === "workflow" && suggestions.some((item) => item.startsWith("arcubase workflow "))) {
10681
+ return {
10682
+ suggestions,
10683
+ reason: `${moduleName} ${commandName} is a row operation, not an arcubase-admin command`,
10684
+ hint: `use: arcubase ${moduleName} ${commandName}`
10685
+ };
10686
+ }
10687
+ return suggestions.length > 0 ? { suggestions } : void 0;
10688
+ }
9720
10689
  function renderCommandHelp(scope, moduleName, commandName) {
9721
10690
  const command = findCommand(scope, moduleName, commandName);
9722
10691
  if (!command) {
9723
- throw new CLIError("UNKNOWN_COMMAND", `unknown command: ${moduleName} ${commandName}`, 2);
10692
+ throw new CLIError("UNKNOWN_COMMAND", `unknown command: ${moduleName} ${commandName}`, 2, buildUnknownCommandDetails(scope, moduleName, commandName));
9724
10693
  }
10694
+ const docHints = [
10695
+ ...getCommandDocHints(`${scope}.${command.module}.${command.functionName}`),
10696
+ ...command.requestType ? getDocHints(command.requestType) : []
10697
+ ].filter((item, index, list) => list.findIndex((candidate) => candidate.file === item.file) === index);
9725
10698
  return [
9726
10699
  `${scope === "admin" ? "arcubase-admin" : "arcubase"} ${moduleName} ${commandName}`,
9727
10700
  `method: ${command.method}`,
9728
10701
  `endpoint: ${normalizeExternalEndpoint(command.endpoint)}`,
9729
10702
  command.requestType ? `body: ${command.requestType} via --body-file` : "body: none",
10703
+ "query flags: --query-file",
9730
10704
  command.endpointParams.length ? `path flags: ${command.endpointParams.map((item) => `--${item.replace(/_/g, "-")}`).join(", ")}` : "path flags: none",
9731
- command.scalarParams.length ? `scalar flags: ${command.scalarParams.map((item) => `--${item.name.replace(/([A-Z])/g, "-$1").toLowerCase()}`).join(", ")}` : "scalar flags: none"
10705
+ command.scalarParams.length ? `scalar flags: ${command.scalarParams.map((item) => `--${item.name.replace(/([A-Z])/g, "-$1").toLowerCase()}`).join(", ")}` : "scalar flags: none",
10706
+ ...scope === "admin" && moduleName === "entity" && commandName === "admin-save-entity" ? [
10707
+ "next step:",
10708
+ " - switch to arcubase for row operations",
10709
+ " - row insert: arcubase workflow insert-entity --app-id <app_id> --entity-id <entity_id> --body-file insert.json",
10710
+ " - row query: arcubase workflow query-entity --app-id <app_id> --entity-id <entity_id> --body-file query.json"
10711
+ ] : [],
10712
+ ...docHints.length > 0 ? ["docs:", ...docHints.map((item) => ` - ${item.title}: ${item.file}`)] : []
9732
10713
  ].join("\n");
9733
10714
  }
9734
10715
  function coerceScalar(value, typeText) {
@@ -9772,8 +10753,221 @@ function buildScalarQuery(command, flags) {
9772
10753
  }
9773
10754
  return Object.keys(out).length > 0 ? out : void 0;
9774
10755
  }
9775
- async function executeCLI(scope, argv, env = loadRuntimeEnv(scope), fetchImpl = fetch) {
10756
+ function isRecord2(value) {
10757
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
10758
+ }
10759
+ function extractEntityFields(payload) {
10760
+ const candidate = isRecord2(payload) && isRecord2(payload.data) ? payload.data : payload;
10761
+ if (!isRecord2(candidate) || !Array.isArray(candidate.fields)) {
10762
+ return [];
10763
+ }
10764
+ return candidate.fields.filter((item) => isRecord2(item)).filter((item) => typeof item.id === "number" && typeof item.type === "string").map((item) => ({
10765
+ id: item.id,
10766
+ type: item.type,
10767
+ label: typeof item.label === "string" ? item.label : void 0
10768
+ }));
10769
+ }
10770
+ async function fetchEntityFields(runtimeEnv, appId, entityId, fetchImpl) {
10771
+ const response = await fetchImpl(buildURL(runtimeEnv.ARCUBASE_BASE_URL, `/entity/${encodeURIComponent(appId)}/${encodeURIComponent(entityId)}`), {
10772
+ method: "GET",
10773
+ headers: buildRequestHeaders(runtimeEnv)
10774
+ });
10775
+ const raw = await response.text();
10776
+ let parsedResponse = raw;
10777
+ try {
10778
+ parsedResponse = raw ? JSON.parse(raw) : null;
10779
+ } catch {
10780
+ parsedResponse = raw;
10781
+ }
10782
+ if (!response.ok) {
10783
+ throw new CLIError("UPSTREAM_REQUEST_FAILED", typeof parsedResponse === "string" ? parsedResponse : JSON.stringify(parsedResponse), 1);
10784
+ }
10785
+ if (isRecord2(parsedResponse) && parsedResponse.error) {
10786
+ const upstreamError = parsedResponse.error;
10787
+ const upstreamMessage = isRecord2(upstreamError) && typeof upstreamError.message === "string" ? upstreamError.message : typeof upstreamError === "string" ? upstreamError : JSON.stringify(upstreamError);
10788
+ throw new CLIError("UPSTREAM_BODY_ERROR", upstreamMessage, 1, {
10789
+ endpoint: `/entity/${appId}/${entityId}`,
10790
+ method: "GET",
10791
+ upstreamError
10792
+ });
10793
+ }
10794
+ const fields = extractEntityFields(parsedResponse);
10795
+ return new Map(fields.map((field) => [field.id, field]));
10796
+ }
10797
+ function looksLikeLocalFilePath(value) {
10798
+ return value.startsWith("./") || value.startsWith("../") || value.startsWith("/") || value.startsWith("~/") || /\.(pdf|png|jpe?g|gif|webp|bmp|svg|heic|txt|csv|xlsx?|docx?|pptx?|zip)$/i.test(value);
10799
+ }
10800
+ function isValidUploadEntry(value) {
10801
+ if (!isRecord2(value)) {
10802
+ return false;
10803
+ }
10804
+ const hasUploadId = typeof value.upload_id === "number" || typeof value.upload_id === "string";
10805
+ const hasAssetsId = typeof value.assets_id === "number" || typeof value.assets_id === "string";
10806
+ return (hasUploadId || hasAssetsId) && typeof value.file === "string" && typeof value.file_name === "string";
10807
+ }
10808
+ async function validateFileFieldPayloads(scope, command, runtimeEnv, flags, body, fetchImpl) {
10809
+ if (!isRecord2(body)) {
10810
+ return;
10811
+ }
10812
+ const container = command.functionName === "updateEntityRow" ? body.data : body.fields;
10813
+ if (!isRecord2(container)) {
10814
+ return;
10815
+ }
10816
+ const appId = flagValue(flags, "app-id");
10817
+ const entityId = flagValue(flags, "entity-id");
10818
+ if (!appId || !entityId) {
10819
+ return;
10820
+ }
10821
+ const fieldsById = await fetchEntityFields(runtimeEnv, appId, entityId, fetchImpl);
10822
+ for (const [fieldIdText, value] of Object.entries(container)) {
10823
+ if (!/^\d+$/.test(fieldIdText)) {
10824
+ continue;
10825
+ }
10826
+ const fieldId = Number(fieldIdText);
10827
+ const field = fieldsById.get(fieldId);
10828
+ if (!field || field.type !== "file" && field.type !== "image") {
10829
+ continue;
10830
+ }
10831
+ const fieldPath = command.functionName === "updateEntityRow" ? `body.data.${fieldId}` : `body.fields.${fieldId}`;
10832
+ const fieldName = field.label ? `${field.label} (${field.type})` : `${field.type} field ${fieldId}`;
10833
+ if (typeof value === "string") {
10834
+ const guidance = looksLikeLocalFilePath(value) ? `do not put a local file path directly into ${fieldName}; run "arcubase upload ${value}" first and use the returned data array as the field value` : `${fieldName} must be an array returned by "arcubase upload <local-file>", not a string`;
10835
+ throw new CLIError("BODY_VALIDATION_FAILED", guidance, 2, {
10836
+ operation: `${scope}.${command.module}.${command.functionName}`,
10837
+ requestType: command.requestType ?? void 0,
10838
+ issues: [{ path: fieldPath, message: guidance }],
10839
+ docHints: [
10840
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
10841
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" }
10842
+ ]
10843
+ });
10844
+ }
10845
+ if (isRecord2(value)) {
10846
+ const guidance = `${fieldName} must be an array. Run "arcubase upload <local-file>" and use the returned data array directly as the field value`;
10847
+ throw new CLIError("BODY_VALIDATION_FAILED", guidance, 2, {
10848
+ operation: `${scope}.${command.module}.${command.functionName}`,
10849
+ requestType: command.requestType ?? void 0,
10850
+ issues: [{ path: fieldPath, message: guidance }],
10851
+ docHints: [
10852
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
10853
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" }
10854
+ ]
10855
+ });
10856
+ }
10857
+ if (!Array.isArray(value)) {
10858
+ const guidance = `${fieldName} must be an array returned by "arcubase upload <local-file>"`;
10859
+ throw new CLIError("BODY_VALIDATION_FAILED", guidance, 2, {
10860
+ operation: `${scope}.${command.module}.${command.functionName}`,
10861
+ requestType: command.requestType ?? void 0,
10862
+ issues: [{ path: fieldPath, message: guidance }],
10863
+ docHints: [
10864
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
10865
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" }
10866
+ ]
10867
+ });
10868
+ }
10869
+ for (const [index, item] of value.entries()) {
10870
+ if (!isValidUploadEntry(item)) {
10871
+ const guidance = `${fieldName} entries must include upload_id or assets_id plus file and file_name. Use "arcubase upload <local-file>" and copy the returned array item as-is`;
10872
+ throw new CLIError("BODY_VALIDATION_FAILED", guidance, 2, {
10873
+ operation: `${scope}.${command.module}.${command.functionName}`,
10874
+ requestType: command.requestType ?? void 0,
10875
+ issues: [{ path: `${fieldPath}.${index}`, message: guidance }],
10876
+ docHints: [
10877
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
10878
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" }
10879
+ ]
10880
+ });
10881
+ }
10882
+ }
10883
+ }
10884
+ }
10885
+ function throwBodyValidationFailure(message, requestType, path2, scope, command) {
10886
+ throw new CLIError("BODY_VALIDATION_FAILED", message, 2, {
10887
+ operation: `${scope}.${command.module}.${command.functionName}`,
10888
+ requestType,
10889
+ issues: [
10890
+ {
10891
+ path: path2,
10892
+ message
10893
+ }
10894
+ ],
10895
+ tsHints: getTypeHints(requestType),
10896
+ docHints: getDocHints(requestType)
10897
+ });
10898
+ }
10899
+ function validateActionSpecificBody(scope, command, flags, body) {
10900
+ if (!isRecord2(body) || !command.requestType) {
10901
+ return;
10902
+ }
10903
+ if (command.module === "workflow" && command.functionName === "queryEntitySelection") {
10904
+ const action = flagValue(flags, "action");
10905
+ const selection = body.selection;
10906
+ if (!isRecord2(selection) || typeof selection.type !== "string") {
10907
+ return;
10908
+ }
10909
+ const type = selection.type;
10910
+ const condition = isRecord2(selection.condition) ? selection.condition : void 0;
10911
+ const conditionKeys = condition ? Object.keys(condition).filter((key) => key !== "selectAll") : [];
10912
+ if (action === "query") {
10913
+ if (type === "condition") {
10914
+ throwBodyValidationFailure(
10915
+ "selection.type=condition is not supported for action=query; use selection.type=ids or selection.type=all, or use workflow query-entity for search conditions",
10916
+ command.requestType,
10917
+ "body.selection.type",
10918
+ scope,
10919
+ command
10920
+ );
10921
+ }
10922
+ return;
10923
+ }
10924
+ if ((action === "archive" || action === "delete" || action === "batch_print") && type === "all") {
10925
+ throwBodyValidationFailure(
10926
+ `selection.type=all is not supported for action=${action}; use ids or condition`,
10927
+ command.requestType,
10928
+ "body.selection.type",
10929
+ scope,
10930
+ command
10931
+ );
10932
+ }
10933
+ if ((action === "archive" || action === "delete") && type === "condition" && conditionKeys.length === 0) {
10934
+ throwBodyValidationFailure(
10935
+ `selection.condition for action=${action} requires at least one q, tab, or filter_* key in addition to selectAll`,
10936
+ command.requestType,
10937
+ "body.selection.condition",
10938
+ scope,
10939
+ command
10940
+ );
10941
+ }
10942
+ }
10943
+ }
10944
+ async function executeCLI(scope, argv, env, fetchImpl = fetch) {
9776
10945
  const parsed = parseCLIArgs(argv);
10946
+ if (scope === "user" && parsed.commandTokens[0] === "upload") {
10947
+ if (hasFlag(parsed.flags, "help") || parsed.commandTokens.length === 1) {
10948
+ return { kind: "help", text: renderUploadHelp() };
10949
+ }
10950
+ const sourcePath = parsed.commandTokens[1];
10951
+ if (!sourcePath) {
10952
+ throw new CLIError("UPLOAD_SOURCE_REQUIRED", "upload requires a local file path", 2);
10953
+ }
10954
+ const runtimeEnv2 = env ?? loadRuntimeEnv(scope);
10955
+ const data = await uploadLocalFile(
10956
+ runtimeEnv2,
10957
+ sourcePath,
10958
+ {
10959
+ filename: flagValue(parsed.flags, "filename"),
10960
+ global: hasFlag(parsed.flags, "global")
10961
+ },
10962
+ fetchImpl
10963
+ );
10964
+ return {
10965
+ kind: "result",
10966
+ commandPath: ["upload", sourcePath],
10967
+ status: 200,
10968
+ data
10969
+ };
10970
+ }
9777
10971
  if (parsed.commandTokens.length === 0) {
9778
10972
  return { kind: "help", text: renderRootHelp(scope) };
9779
10973
  }
@@ -9787,15 +10981,17 @@ async function executeCLI(scope, argv, env = loadRuntimeEnv(scope), fetchImpl =
9787
10981
  if (hasFlag(parsed.flags, "help")) {
9788
10982
  return { kind: "help", text: renderCommandHelp(scope, moduleName, commandName) };
9789
10983
  }
10984
+ const runtimeEnv = env ?? loadRuntimeEnv(scope);
9790
10985
  const command = findCommand(scope, moduleName, commandName);
9791
10986
  if (!command) {
9792
- throw new CLIError("UNKNOWN_COMMAND", `unknown command: ${moduleName} ${commandName}`, 2);
10987
+ throw new CLIError("UNKNOWN_COMMAND", `unknown command: ${moduleName} ${commandName}`, 2, buildUnknownCommandDetails(scope, moduleName, commandName));
9793
10988
  }
9794
10989
  const endpoint = normalizeExternalEndpoint(resolveEndpoint(command.endpoint, parsed.flags));
9795
10990
  const scalarQuery = buildScalarQuery(command, parsed.flags);
9796
10991
  const fileQuery = loadQueryFromFlags(parsed.flags);
9797
10992
  const query = { ...scalarQuery ?? {}, ...fileQuery ?? {} };
9798
10993
  const body = loadBodyFromFlags(parsed.flags);
10994
+ let validatedBody = body;
9799
10995
  if (command.requestType) {
9800
10996
  if (body === void 0) {
9801
10997
  throw new CLIError("MISSING_BODY_FILE", `command requires --body-file for ${command.requestType}`, 2);
@@ -9815,10 +11011,13 @@ async function executeCLI(scope, argv, env = loadRuntimeEnv(scope), fetchImpl =
9815
11011
  path: issue.path.length > 0 ? `body.${issue.path.join(".")}` : "body",
9816
11012
  message: issue.message
9817
11013
  })),
9818
- tsHints: getTypeHints(command.requestType)
11014
+ tsHints: getTypeHints(command.requestType),
11015
+ docHints: getDocHints(command.requestType)
9819
11016
  }
9820
11017
  );
9821
11018
  }
11019
+ validatedBody = parsedBody.data;
11020
+ validateActionSpecificBody(scope, command, parsed.flags, validatedBody);
9822
11021
  } else {
9823
11022
  const unsupportedReason = getUnsupportedBodySchemaReason(command.requestType);
9824
11023
  if (unsupportedReason) {
@@ -9826,19 +11025,22 @@ async function executeCLI(scope, argv, env = loadRuntimeEnv(scope), fetchImpl =
9826
11025
  operation: `${scope}.${command.module}.${command.functionName}`,
9827
11026
  requestType: command.requestType,
9828
11027
  reason: unsupportedReason,
9829
- tsHints: getTypeHints(command.requestType)
11028
+ tsHints: getTypeHints(command.requestType),
11029
+ docHints: getDocHints(command.requestType)
9830
11030
  });
9831
11031
  }
9832
11032
  }
9833
11033
  }
9834
- const headers = buildRequestHeaders(env);
9835
- const response = await fetchImpl(buildURL(env.ARCUBASE_BASE_URL, endpoint, Object.keys(query).length > 0 ? query : void 0), {
11034
+ if (scope === "user" && (command.functionName === "insertEntity" || command.functionName === "updateEntityRow")) {
11035
+ await validateFileFieldPayloads(scope, command, runtimeEnv, parsed.flags, validatedBody, fetchImpl);
11036
+ }
11037
+ const response = await fetchImpl(buildURL(runtimeEnv.ARCUBASE_BASE_URL, endpoint, Object.keys(query).length > 0 ? query : void 0), {
9836
11038
  method: command.method,
9837
11039
  headers: {
9838
- ...headers,
11040
+ ...buildRequestHeaders(runtimeEnv),
9839
11041
  "Content-Type": "application/json"
9840
11042
  },
9841
- body: body === void 0 || command.method === "GET" ? void 0 : JSON.stringify(body)
11043
+ body: validatedBody === void 0 || command.method === "GET" ? void 0 : JSON.stringify(validatedBody)
9842
11044
  });
9843
11045
  const raw = await response.text();
9844
11046
  let parsedResponse = raw;
@@ -9849,6 +11051,26 @@ async function executeCLI(scope, argv, env = loadRuntimeEnv(scope), fetchImpl =
9849
11051
  if (!response.ok) {
9850
11052
  throw new CLIError("UPSTREAM_REQUEST_FAILED", typeof parsedResponse === "string" ? parsedResponse : JSON.stringify(parsedResponse), 1);
9851
11053
  }
11054
+ if (parsedResponse && typeof parsedResponse === "object" && "error" in parsedResponse && parsedResponse.error) {
11055
+ const upstreamError = parsedResponse.error;
11056
+ const upstreamMessage = typeof upstreamError === "object" && upstreamError && "message" in upstreamError && typeof upstreamError.message === "string" ? upstreamError.message : typeof upstreamError === "string" ? upstreamError : JSON.stringify(upstreamError);
11057
+ const traceId = typeof parsedResponse.trace_id === "string" ? parsedResponse.trace_id : typeof parsedResponse.request_id === "string" ? parsedResponse.request_id : void 0;
11058
+ const uploadBindFailure = upstreamMessage.includes("failed to bind upload to assets");
11059
+ throw new CLIError("UPSTREAM_BODY_ERROR", upstreamMessage, 1, {
11060
+ endpoint,
11061
+ method: command.method,
11062
+ traceId,
11063
+ upstreamError,
11064
+ ...uploadBindFailure ? {
11065
+ reason: "the row payload used a file/image upload entry, but Arcubase could not bind that upload into an asset record",
11066
+ hint: "verify the upload storage host is reachable from the Arcubase server, then rerun arcubase upload and retry the row insert/update",
11067
+ docHints: [
11068
+ { title: "Uploads", file: "/opt/arcubase-sdk/docs/runtime-reference/uploads.md" },
11069
+ { title: "Row CRUD", file: "/opt/arcubase-sdk/docs/runtime-reference/row-crud.md" }
11070
+ ]
11071
+ } : {}
11072
+ });
11073
+ }
9852
11074
  return {
9853
11075
  kind: "result",
9854
11076
  commandPath: command.commandPath,