@carthooks/arcubase-cli 0.1.20 → 0.1.21

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 (36) hide show
  1. package/bundle/arcubase-admin.mjs +339 -51
  2. package/bundle/arcubase.mjs +339 -51
  3. package/dist/generated/command_registry.generated.d.ts +15 -0
  4. package/dist/generated/command_registry.generated.d.ts.map +1 -1
  5. package/dist/generated/command_registry.generated.js +21 -0
  6. package/dist/generated/help_examples.generated.d.ts +114 -0
  7. package/dist/generated/help_examples.generated.d.ts.map +1 -1
  8. package/dist/generated/help_examples.generated.js +155 -0
  9. package/dist/generated/type_index.generated.d.ts +4 -0
  10. package/dist/generated/type_index.generated.d.ts.map +1 -1
  11. package/dist/generated/type_index.generated.js +4 -0
  12. package/dist/generated/zod_registry.generated.d.ts +4 -1
  13. package/dist/generated/zod_registry.generated.d.ts.map +1 -1
  14. package/dist/generated/zod_registry.generated.js +4 -0
  15. package/dist/runtime/execute.d.ts.map +1 -1
  16. package/dist/runtime/execute.js +104 -31
  17. package/dist/runtime/zod_registry.d.ts.map +1 -1
  18. package/dist/runtime/zod_registry.js +8 -0
  19. package/package.json +1 -1
  20. package/sdk-dist/api/admin/ingress.ts +11 -0
  21. package/sdk-dist/docs/runtime-reference/access-rule.md +19 -0
  22. package/sdk-dist/docs/runtime-reference/help-examples/admin/access-rule/bulk-apply.json +105 -0
  23. package/sdk-dist/generated/command_registry.generated.ts +21 -0
  24. package/sdk-dist/generated/help_examples.generated.ts +155 -0
  25. package/sdk-dist/generated/type_index.generated.ts +4 -0
  26. package/sdk-dist/generated/zod_registry.generated.ts +6 -0
  27. package/sdk-dist/types/ingress.ts +31 -0
  28. package/src/generated/command_registry.generated.ts +21 -0
  29. package/src/generated/help_examples.generated.ts +155 -0
  30. package/src/generated/type_index.generated.ts +4 -0
  31. package/src/generated/zod_registry.generated.ts +6 -0
  32. package/src/runtime/execute.ts +159 -51
  33. package/src/runtime/zod_registry.ts +8 -0
  34. package/src/tests/command_registry.test.ts +1 -0
  35. package/src/tests/execute_validation.test.ts +86 -0
  36. package/src/tests/help.test.ts +13 -0
@@ -605,6 +605,27 @@ var adminCommands = [
605
605
  "queryParams": [],
606
606
  "responseType": "AppIngressUpdateRespVO"
607
607
  },
608
+ {
609
+ "scope": "admin",
610
+ "module": "access-rule",
611
+ "functionName": "bulkApplyEntityIngress",
612
+ "commandPath": [
613
+ "access-rule",
614
+ "bulk-apply"
615
+ ],
616
+ "method": "POST",
617
+ "endpoint": "/api/apps/:app_id/access-rules/bulk-apply",
618
+ "pathParams": [
619
+ {
620
+ "param": "app_id",
621
+ "flag": "app-id"
622
+ }
623
+ ],
624
+ "controller": "App.IngressBulkApply",
625
+ "requestType": "AppIngressBulkApplyReqVO",
626
+ "queryParams": [],
627
+ "responseType": "AppIngressBulkApplyRespVO"
628
+ },
608
629
  {
609
630
  "scope": "admin",
610
631
  "module": "workflow",
@@ -16092,6 +16113,8 @@ var PermitFieldPermitSchema = external_exports.lazy(() => external_exports.objec
16092
16113
  var PermitPolicySchema = external_exports.lazy(() => external_exports.object({ "actions": external_exports.array(external_exports.string()).optional(), "all_fields_read": external_exports.boolean().optional(), "all_fields_write": external_exports.boolean().optional(), "condition": PermitConditionSetSchema.optional(), "description": external_exports.string().optional(), "fields": external_exports.array(PermitFieldPermitSchema).optional(), "id": external_exports.string().optional(), "key": external_exports.string().optional(), "name": external_exports.string().optional() }).strict());
16093
16114
  var PermitUserScopeSchema = external_exports.lazy(() => external_exports.object({ "data": external_exports.object({ "name": external_exports.string().optional() }).strict().optional(), "id": external_exports.number().optional(), "type": external_exports.string().optional() }).strict());
16094
16115
  var EntityIngressOptionsSchema = external_exports.lazy(() => external_exports.object({ "i18n_name": I18NTextSchema.optional(), "list_options": PermitEntityListOptionsSchema.optional(), "policy": PermitPolicySchema.optional(), "user_scope": external_exports.array(PermitUserScopeSchema).optional() }).strict());
16116
+ var AppIngressBulkApplyItemVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "rule_id": external_exports.number().optional(), "table_id": external_exports.number().optional(), "type": external_exports.string().optional() }).strict());
16117
+ var AppIngressBulkApplyReqVOSchema = external_exports.lazy(() => external_exports.object({ "dry_run": external_exports.boolean().optional(), "items": external_exports.array(AppIngressBulkApplyItemVOSchema).optional(), "mode": external_exports.string().optional() }).strict());
16095
16118
  var AppIngressCreateReqVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "entity_id": external_exports.number().optional(), "icon_name": external_exports.string().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "type": external_exports.string().optional(), "url": external_exports.string().optional() }).strict());
16096
16119
  var AppIngressUpdateReqVOSchema = external_exports.lazy(() => external_exports.object({ "enabled": external_exports.boolean().optional(), "name": external_exports.string().optional(), "options": EntityIngressOptionsSchema.optional(), "update": external_exports.array(external_exports.string()).optional() }).strict());
16097
16120
  var GormDeletedAtSchema = external_exports.lazy(() => external_exports.object({ "Time": external_exports.string().optional(), "Valid": external_exports.boolean().optional() }).strict());
@@ -16159,6 +16182,7 @@ var UserProfileUpdateReqVOSchema = external_exports.lazy(() => external_exports.
16159
16182
  var bodySchemas = {
16160
16183
  "AppCreateByTenantsReqVO": AppCreateByTenantsReqVOSchema,
16161
16184
  "AppCreateEntityReqVO": AppCreateEntityReqVOSchema,
16185
+ "AppIngressBulkApplyReqVO": AppIngressBulkApplyReqVOSchema,
16162
16186
  "AppIngressCreateReqVO": AppIngressCreateReqVOSchema,
16163
16187
  "AppIngressUpdateReqVO": AppIngressUpdateReqVOSchema,
16164
16188
  "AppUpdateAppReqVO": AppUpdateAppReqVOSchema,
@@ -16193,6 +16217,10 @@ var typeIndex = {
16193
16217
  "file": "types/app.ts",
16194
16218
  "dependencies": []
16195
16219
  },
16220
+ "AppIngressBulkApplyReqVO": {
16221
+ "file": "types/ingress.ts",
16222
+ "dependencies": []
16223
+ },
16196
16224
  "AppIngressCreateReqVO": {
16197
16225
  "file": "types/ingress.ts",
16198
16226
  "dependencies": []
@@ -17053,6 +17081,10 @@ var docHintIndex = {
17053
17081
  { title: "Access rule", file: "docs/runtime-reference/access-rule.md" },
17054
17082
  examplesIndexDocHint
17055
17083
  ],
17084
+ AppIngressBulkApplyReqVO: [
17085
+ { title: "Access rule", file: "docs/runtime-reference/access-rule.md" },
17086
+ examplesIndexDocHint
17087
+ ],
17056
17088
  EntitySaveReqVO: [
17057
17089
  { title: "Table schema cheatsheet", file: "docs/runtime-reference/entity-schema.md" },
17058
17090
  { title: "Field type index", file: "docs/runtime-reference/entity-schema/README.md" },
@@ -17166,6 +17198,10 @@ var commandDocHintIndex = {
17166
17198
  { title: "Access rule", file: "docs/runtime-reference/access-rule.md" },
17167
17199
  examplesIndexDocHint
17168
17200
  ],
17201
+ "admin.access-rule.bulkApplyEntityIngress": [
17202
+ { title: "Access rule", file: "docs/runtime-reference/access-rule.md" },
17203
+ examplesIndexDocHint
17204
+ ],
17169
17205
  "user.row.insertEntity": [
17170
17206
  { title: "Row CRUD", file: "docs/runtime-reference/row-crud.md" },
17171
17207
  { title: "Uploads", file: "docs/runtime-reference/uploads.md" },
@@ -17454,6 +17490,161 @@ var helpExamplesIndex = {
17454
17490
  }
17455
17491
  ]
17456
17492
  },
17493
+ "admin.access-rule.bulk-apply": {
17494
+ "command": [
17495
+ "access-rule",
17496
+ "bulk-apply"
17497
+ ],
17498
+ "examples": [
17499
+ {
17500
+ "title": "upsert permission matrix by rule name in one transaction",
17501
+ "body": {
17502
+ "mode": "upsert_by_name",
17503
+ "items": [
17504
+ {
17505
+ "table_id": 2188893443,
17506
+ "name": "Customer archive - sales owns rows",
17507
+ "enabled": true,
17508
+ "type": "form",
17509
+ "options": {
17510
+ "user_scope": [
17511
+ {
17512
+ "type": "department",
17513
+ "id": 2188881201
17514
+ }
17515
+ ],
17516
+ "policy": {
17517
+ "key": "custom",
17518
+ "actions": [
17519
+ "view",
17520
+ "add",
17521
+ "edit"
17522
+ ],
17523
+ "all_fields_read": true,
17524
+ "all_fields_write": true,
17525
+ "condition": {
17526
+ "mode": "and",
17527
+ "conditions": [
17528
+ {
17529
+ "column": "creator",
17530
+ "operator": "$eq",
17531
+ "value": "[[current_user]]",
17532
+ "value_from": 0,
17533
+ "value_relation": ""
17534
+ }
17535
+ ]
17536
+ }
17537
+ },
17538
+ "list_options": {}
17539
+ }
17540
+ },
17541
+ {
17542
+ "table_id": 2188893444,
17543
+ "name": "Lead review - manager queue",
17544
+ "enabled": true,
17545
+ "type": "form",
17546
+ "options": {
17547
+ "user_scope": [
17548
+ {
17549
+ "type": "actor",
17550
+ "id": 2188883301
17551
+ }
17552
+ ],
17553
+ "policy": {
17554
+ "key": "custom",
17555
+ "actions": [
17556
+ "view",
17557
+ "edit"
17558
+ ],
17559
+ "all_fields_read": true,
17560
+ "all_fields_write": true,
17561
+ "condition": {
17562
+ "mode": "and",
17563
+ "conditions": [
17564
+ {
17565
+ "column": ":1004",
17566
+ "operator": "$eq",
17567
+ "value": "pending",
17568
+ "value_from": 0,
17569
+ "value_relation": ""
17570
+ },
17571
+ {
17572
+ "column": "",
17573
+ "operator": "",
17574
+ "value": null,
17575
+ "value_from": 0,
17576
+ "value_relation": "",
17577
+ "subset": {
17578
+ "mode": "or",
17579
+ "conditions": [
17580
+ {
17581
+ "column": "creator",
17582
+ "operator": "$eq",
17583
+ "value": "[[current_user]]",
17584
+ "value_from": 0,
17585
+ "value_relation": ""
17586
+ },
17587
+ {
17588
+ "column": ":1005",
17589
+ "operator": "$eq",
17590
+ "value": "high",
17591
+ "value_from": 0,
17592
+ "value_relation": ""
17593
+ }
17594
+ ]
17595
+ }
17596
+ }
17597
+ ]
17598
+ }
17599
+ },
17600
+ "list_options": {}
17601
+ }
17602
+ }
17603
+ ]
17604
+ },
17605
+ "notes": [
17606
+ "mode upsert_by_name is for idempotent setup; rule names must be unique within each table",
17607
+ "department, actor, and specific user scopes all use options.user_scope",
17608
+ "row-owner isolation uses [[current_user]] in policy.condition"
17609
+ ]
17610
+ },
17611
+ {
17612
+ "title": "preview updates for existing rule ids",
17613
+ "body": {
17614
+ "mode": "update_existing",
17615
+ "dry_run": true,
17616
+ "items": [
17617
+ {
17618
+ "table_id": 2188893443,
17619
+ "rule_id": 2188893557,
17620
+ "enabled": true,
17621
+ "options": {
17622
+ "user_scope": [
17623
+ {
17624
+ "type": "user",
17625
+ "id": 2188889901
17626
+ }
17627
+ ],
17628
+ "policy": {
17629
+ "key": "custom",
17630
+ "actions": [
17631
+ "view"
17632
+ ],
17633
+ "all_fields_read": true,
17634
+ "all_fields_write": false
17635
+ },
17636
+ "list_options": {}
17637
+ }
17638
+ }
17639
+ ]
17640
+ },
17641
+ "notes": [
17642
+ "dry_run true returns the planned operations without writing",
17643
+ "update_existing requires rule_id on every item"
17644
+ ]
17645
+ }
17646
+ ]
17647
+ },
17457
17648
  "admin.access-rule.create": {
17458
17649
  "command": [
17459
17650
  "access-rule",
@@ -18269,6 +18460,9 @@ function buildTableCreateExampleBody() {
18269
18460
  function isAssignUsersCommand(scope, command) {
18270
18461
  return scope === "admin" && command.commandPath[0] === "access-rule" && command.commandPath[1] === "assign-users";
18271
18462
  }
18463
+ function isAccessRuleBulkApplyCommand(scope, command) {
18464
+ return scope === "admin" && command.commandPath[0] === "access-rule" && command.commandPath[1] === "bulk-apply";
18465
+ }
18272
18466
  function isTableCreateCommand(scope, command) {
18273
18467
  return scope === "admin" && command.commandPath[0] === "table" && command.commandPath[1] === "create";
18274
18468
  }
@@ -18677,30 +18871,111 @@ function validateAccessRuleUserScope(scope, command, requestType, userScope, env
18677
18871
  }
18678
18872
  }
18679
18873
  function normalizeNumericAccessRuleUserScopeIDs(scope, command, body) {
18680
- if (!isAccessRuleCommand(scope, command) || !isRecord3(body) || !isRecord3(body.options) || !Array.isArray(body.options.user_scope)) {
18874
+ if (!isAccessRuleCommand(scope, command) || !isRecord3(body)) {
18681
18875
  return body;
18682
18876
  }
18683
- let changed = false;
18684
- const userScope = body.options.user_scope.map((item) => {
18685
- if (!isRecord3(item) || typeof item.id !== "string" || !/^\d+$/.test(item.id)) {
18686
- return item;
18687
- }
18688
- const id = Number(item.id);
18689
- if (!Number.isFinite(id) || !Number.isInteger(id)) {
18690
- return item;
18691
- }
18692
- changed = true;
18693
- return { ...item, id };
18694
- });
18695
- if (!changed) return body;
18877
+ const normalizeUserScope = (userScope) => {
18878
+ if (!Array.isArray(userScope)) return { value: userScope, changed: false };
18879
+ let changed = false;
18880
+ const value = userScope.map((item) => {
18881
+ if (!isRecord3(item) || typeof item.id !== "string" || !/^\d+$/.test(item.id)) {
18882
+ return item;
18883
+ }
18884
+ const id = Number(item.id);
18885
+ if (!Number.isFinite(id) || !Number.isInteger(id)) {
18886
+ return item;
18887
+ }
18888
+ changed = true;
18889
+ return { ...item, id };
18890
+ });
18891
+ return { value, changed };
18892
+ };
18893
+ const normalizeNumericID = (value) => {
18894
+ if (typeof value !== "string" || !/^\d+$/.test(value)) return { value, changed: false };
18895
+ const id = Number(value);
18896
+ if (!Number.isFinite(id) || !Number.isInteger(id)) return { value, changed: false };
18897
+ return { value: id, changed: true };
18898
+ };
18899
+ if (isAccessRuleBulkApplyCommand(scope, command) && Array.isArray(body.items)) {
18900
+ let changed = false;
18901
+ const items = body.items.map((item) => {
18902
+ if (!isRecord3(item)) return item;
18903
+ let nextItem = item;
18904
+ for (const key of ["table_id", "rule_id"]) {
18905
+ const normalized = normalizeNumericID(nextItem[key]);
18906
+ if (normalized.changed) {
18907
+ nextItem = { ...nextItem, [key]: normalized.value };
18908
+ changed = true;
18909
+ }
18910
+ }
18911
+ if (isRecord3(nextItem.options)) {
18912
+ const normalizedUserScope2 = normalizeUserScope(nextItem.options.user_scope);
18913
+ if (normalizedUserScope2.changed) {
18914
+ nextItem = {
18915
+ ...nextItem,
18916
+ options: {
18917
+ ...nextItem.options,
18918
+ user_scope: normalizedUserScope2.value
18919
+ }
18920
+ };
18921
+ changed = true;
18922
+ }
18923
+ }
18924
+ return nextItem;
18925
+ });
18926
+ return changed ? { ...body, items } : body;
18927
+ }
18928
+ if (!isRecord3(body.options)) {
18929
+ return body;
18930
+ }
18931
+ const normalizedUserScope = normalizeUserScope(body.options.user_scope);
18932
+ if (!normalizedUserScope.changed) return body;
18696
18933
  return {
18697
18934
  ...body,
18698
18935
  options: {
18699
18936
  ...body.options,
18700
- user_scope: userScope
18937
+ user_scope: normalizedUserScope.value
18701
18938
  }
18702
18939
  };
18703
18940
  }
18941
+ function validateAccessRulePolicyShape(scope, command, requestType, policy, env, pathPrefix) {
18942
+ const actions = policy && Array.isArray(policy.actions) ? policy.actions : [];
18943
+ if (actions.includes("read") || actions.includes("write")) {
18944
+ throwBodyValidationFailure(
18945
+ 'access-rule policy.actions must use Arcubase action codes: use "view" for read and "add","edit" for write; do not use "read", "write", or "insert"',
18946
+ requestType,
18947
+ `${pathPrefix}.actions`,
18948
+ scope,
18949
+ command,
18950
+ env
18951
+ );
18952
+ }
18953
+ const fields = policy && Array.isArray(policy.fields) ? policy.fields : [];
18954
+ const allowedSystemFieldKeys = /* @__PURE__ */ new Set(["created", "updated", "creator"]);
18955
+ for (const [index, field] of fields.entries()) {
18956
+ if (!isRecord3(field)) continue;
18957
+ if (typeof field.key === "number" || typeof field.key === "string" && /^\d+$/.test(field.key)) {
18958
+ throwBodyValidationFailure(
18959
+ 'access-rule policy.fields[].key must be a string prefixed with colon, for example ":1002"; do not use raw numbers',
18960
+ requestType,
18961
+ `${pathPrefix}.fields.${index}.key`,
18962
+ scope,
18963
+ command,
18964
+ env
18965
+ );
18966
+ }
18967
+ if (typeof field.key === "string" && !field.key.startsWith(":") && !allowedSystemFieldKeys.has(field.key)) {
18968
+ throwBodyValidationFailure(
18969
+ 'access-rule policy.fields[].key must use Arcubase permission keys such as ":1002"; do not use FieldVO.key values such as "vote_name"',
18970
+ requestType,
18971
+ `${pathPrefix}.fields.${index}.key`,
18972
+ scope,
18973
+ command,
18974
+ env
18975
+ );
18976
+ }
18977
+ }
18978
+ }
18704
18979
  function requireUpdateContains(scope, command, body, field, env) {
18705
18980
  if (!(field in body)) return;
18706
18981
  const update = stringArray(body.update);
@@ -18746,6 +19021,54 @@ function validateActionSpecificRawBody(scope, command, body, env) {
18746
19021
  }
18747
19022
  }
18748
19023
  if (isAccessRuleCommand(scope, command)) {
19024
+ if (isAccessRuleBulkApplyCommand(scope, command)) {
19025
+ if (!Array.isArray(body.items) || body.items.length === 0) {
19026
+ throwBodyValidationFailure(
19027
+ "access-rule bulk-apply requires non-empty body.items; put every table/rule assignment in body.items",
19028
+ command.requestType,
19029
+ "body.items",
19030
+ scope,
19031
+ command,
19032
+ env
19033
+ );
19034
+ }
19035
+ if (body.mode !== void 0 && body.mode !== "update_existing" && body.mode !== "upsert_by_name") {
19036
+ throwBodyValidationFailure(
19037
+ 'access-rule bulk-apply body.mode must be "update_existing" or "upsert_by_name"',
19038
+ command.requestType,
19039
+ "body.mode",
19040
+ scope,
19041
+ command,
19042
+ env
19043
+ );
19044
+ }
19045
+ for (const [index, item] of body.items.entries()) {
19046
+ if (!isRecord3(item)) {
19047
+ throwBodyValidationFailure(
19048
+ "access-rule bulk-apply body.items[] must be an object",
19049
+ command.requestType,
19050
+ `body.items.${index}`,
19051
+ scope,
19052
+ command,
19053
+ env
19054
+ );
19055
+ }
19056
+ const options2 = isRecord3(item.options) ? item.options : void 0;
19057
+ if (!options2) {
19058
+ throwBodyValidationFailure(
19059
+ "access-rule bulk-apply body.items[].options is required and must include the full canonical options object",
19060
+ command.requestType,
19061
+ `body.items.${index}.options`,
19062
+ scope,
19063
+ command,
19064
+ env
19065
+ );
19066
+ }
19067
+ validateAccessRuleUserScope(scope, command, command.requestType, options2.user_scope, env, `body.items.${index}.options.user_scope`);
19068
+ validateAccessRulePolicyShape(scope, command, command.requestType, isRecord3(options2.policy) ? options2.policy : void 0, env, `body.items.${index}.options.policy`);
19069
+ }
19070
+ return;
19071
+ }
18749
19072
  const update = stringArray(body.update);
18750
19073
  if (isAccessRuleUpdateCommand(scope, command) && update.some((item) => item.startsWith("options."))) {
18751
19074
  throwBodyValidationFailure(
@@ -18790,42 +19113,7 @@ function validateActionSpecificRawBody(scope, command, body, env) {
18790
19113
  );
18791
19114
  }
18792
19115
  validateAccessRuleUserScope(scope, command, command.requestType, options?.user_scope, env);
18793
- const actions = policy && Array.isArray(policy.actions) ? policy.actions : [];
18794
- if (actions.includes("read") || actions.includes("write")) {
18795
- throwBodyValidationFailure(
18796
- 'access-rule policy.actions must use Arcubase action codes: use "view" for read and "add","edit" for write; do not use "read", "write", or "insert"',
18797
- command.requestType,
18798
- "body.options.policy.actions",
18799
- scope,
18800
- command,
18801
- env
18802
- );
18803
- }
18804
- const fields = policy && Array.isArray(policy.fields) ? policy.fields : [];
18805
- const allowedSystemFieldKeys = /* @__PURE__ */ new Set(["created", "updated", "creator"]);
18806
- for (const [index, field] of fields.entries()) {
18807
- if (!isRecord3(field)) continue;
18808
- if (typeof field.key === "number" || typeof field.key === "string" && /^\d+$/.test(field.key)) {
18809
- throwBodyValidationFailure(
18810
- 'access-rule policy.fields[].key must be a string prefixed with colon, for example ":1002"; do not use raw numbers',
18811
- command.requestType,
18812
- `body.options.policy.fields.${index}.key`,
18813
- scope,
18814
- command,
18815
- env
18816
- );
18817
- }
18818
- if (typeof field.key === "string" && !field.key.startsWith(":") && !allowedSystemFieldKeys.has(field.key)) {
18819
- throwBodyValidationFailure(
18820
- 'access-rule policy.fields[].key must use Arcubase permission keys such as ":1002"; do not use FieldVO.key values such as "vote_name"',
18821
- command.requestType,
18822
- `body.options.policy.fields.${index}.key`,
18823
- scope,
18824
- command,
18825
- env
18826
- );
18827
- }
18828
- }
19116
+ validateAccessRulePolicyShape(scope, command, command.requestType, policy, env, "body.options.policy");
18829
19117
  }
18830
19118
  }
18831
19119
  function validateActionSpecificBody(scope, command, flags, body, env) {