@carthooks/arcubase-cli 0.1.19 → 0.1.20

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.
@@ -17416,6 +17416,334 @@ async function uploadLocalFileWithToken(filePath, token, fetchImpl = fetch) {
17416
17416
  return { objectKey };
17417
17417
  }
17418
17418
 
17419
+ // src/generated/help_examples.generated.ts
17420
+ var helpExamplesIndex = {
17421
+ "admin.access-rule.assign-users": {
17422
+ "command": [
17423
+ "access-rule",
17424
+ "assign-users"
17425
+ ],
17426
+ "examples": [
17427
+ {
17428
+ "title": "replace user_scope with user, department, and actor scopes",
17429
+ "body": {
17430
+ "update": [
17431
+ "user_scope"
17432
+ ],
17433
+ "options": {
17434
+ "user_scope": [
17435
+ {
17436
+ "type": "user",
17437
+ "id": 2188889901
17438
+ },
17439
+ {
17440
+ "type": "department",
17441
+ "id": 2188881201
17442
+ },
17443
+ {
17444
+ "type": "actor",
17445
+ "id": 2188883301
17446
+ }
17447
+ ]
17448
+ }
17449
+ },
17450
+ "notes": [
17451
+ "id must be numeric arcubaseUserId, department ID, or actor ID",
17452
+ "assign-users updates user_scope only and preserves the existing policy"
17453
+ ]
17454
+ }
17455
+ ]
17456
+ },
17457
+ "admin.access-rule.create": {
17458
+ "command": [
17459
+ "access-rule",
17460
+ "create"
17461
+ ],
17462
+ "examples": [
17463
+ {
17464
+ "title": "full access for a specific arcubaseUserId",
17465
+ "body": {
17466
+ "name": "Admin full access",
17467
+ "enabled": true,
17468
+ "type": "form",
17469
+ "options": {
17470
+ "user_scope": [
17471
+ {
17472
+ "type": "user",
17473
+ "id": 2188889977
17474
+ }
17475
+ ],
17476
+ "policy": {
17477
+ "key": "custom",
17478
+ "actions": [
17479
+ "view",
17480
+ "add",
17481
+ "edit",
17482
+ "delete"
17483
+ ],
17484
+ "all_fields_read": true,
17485
+ "all_fields_write": true
17486
+ },
17487
+ "list_options": {}
17488
+ }
17489
+ }
17490
+ },
17491
+ {
17492
+ "title": "department can view/add/edit only rows created by themselves",
17493
+ "body": {
17494
+ "name": "Sales own rows",
17495
+ "enabled": true,
17496
+ "type": "form",
17497
+ "options": {
17498
+ "user_scope": [
17499
+ {
17500
+ "type": "department",
17501
+ "id": 2188881201
17502
+ }
17503
+ ],
17504
+ "policy": {
17505
+ "key": "custom",
17506
+ "actions": [
17507
+ "view",
17508
+ "add",
17509
+ "edit"
17510
+ ],
17511
+ "all_fields_read": true,
17512
+ "all_fields_write": true,
17513
+ "condition": {
17514
+ "mode": "and",
17515
+ "conditions": [
17516
+ {
17517
+ "column": "creator",
17518
+ "operator": "$eq",
17519
+ "value": "[[current_user]]",
17520
+ "value_from": 0,
17521
+ "value_relation": ""
17522
+ }
17523
+ ]
17524
+ }
17525
+ },
17526
+ "list_options": {}
17527
+ }
17528
+ },
17529
+ "notes": [
17530
+ "department scope includes child departments",
17531
+ "row-owner isolation uses [[current_user]] on creator"
17532
+ ]
17533
+ },
17534
+ {
17535
+ "title": "role actor can view rows matching a complex AND/OR condition",
17536
+ "body": {
17537
+ "name": "Sales manager review queue",
17538
+ "enabled": true,
17539
+ "type": "form",
17540
+ "options": {
17541
+ "user_scope": [
17542
+ {
17543
+ "type": "actor",
17544
+ "id": 2188883301
17545
+ }
17546
+ ],
17547
+ "policy": {
17548
+ "key": "custom",
17549
+ "actions": [
17550
+ "view",
17551
+ "edit"
17552
+ ],
17553
+ "all_fields_read": true,
17554
+ "all_fields_write": true,
17555
+ "condition": {
17556
+ "mode": "and",
17557
+ "conditions": [
17558
+ {
17559
+ "column": ":1004",
17560
+ "operator": "$eq",
17561
+ "value": "pending",
17562
+ "value_from": 0,
17563
+ "value_relation": ""
17564
+ },
17565
+ {
17566
+ "column": "",
17567
+ "operator": "",
17568
+ "value": null,
17569
+ "value_from": 0,
17570
+ "value_relation": "",
17571
+ "subset": {
17572
+ "mode": "or",
17573
+ "conditions": [
17574
+ {
17575
+ "column": "creator",
17576
+ "operator": "$eq",
17577
+ "value": "[[current_user]]",
17578
+ "value_from": 0,
17579
+ "value_relation": ""
17580
+ },
17581
+ {
17582
+ "column": ":1005",
17583
+ "operator": "$eq",
17584
+ "value": "high",
17585
+ "value_from": 0,
17586
+ "value_relation": ""
17587
+ }
17588
+ ]
17589
+ }
17590
+ }
17591
+ ]
17592
+ }
17593
+ },
17594
+ "list_options": {}
17595
+ }
17596
+ },
17597
+ "notes": [
17598
+ "actor means Arcubase role id",
17599
+ 'condition mode "and" and "or" are canonical; legacy "all" also reads as AND',
17600
+ "nested OR uses subset and leaves the wrapper column/operator empty"
17601
+ ]
17602
+ }
17603
+ ]
17604
+ },
17605
+ "user.row.bulk-update": {
17606
+ "command": [
17607
+ "row",
17608
+ "bulk-update"
17609
+ ],
17610
+ "examples": [
17611
+ {
17612
+ "title": "bulk update explicit row ids",
17613
+ "body": {
17614
+ "selection": {
17615
+ "type": "ids",
17616
+ "ids": [
17617
+ 2188890411,
17618
+ 2188890412
17619
+ ],
17620
+ "length": 2
17621
+ },
17622
+ "fields": [
17623
+ {
17624
+ "id": 1003,
17625
+ "action": {
17626
+ "type": "set",
17627
+ "value": 2
17628
+ }
17629
+ }
17630
+ ]
17631
+ }
17632
+ },
17633
+ {
17634
+ "title": "bulk update rows selected by a search condition",
17635
+ "body": {
17636
+ "selection": {
17637
+ "type": "condition",
17638
+ "condition": {
17639
+ "selectAll": true,
17640
+ "q": "SO-1001"
17641
+ },
17642
+ "length": 0
17643
+ },
17644
+ "fields": [
17645
+ {
17646
+ "id": 1003,
17647
+ "action": {
17648
+ "type": "set",
17649
+ "value": 2
17650
+ }
17651
+ }
17652
+ ]
17653
+ },
17654
+ "notes": [
17655
+ "condition selection uses row-query style keys such as q, tab, and filter_:1002"
17656
+ ]
17657
+ }
17658
+ ]
17659
+ },
17660
+ "user.row.selection-action": {
17661
+ "command": [
17662
+ "row",
17663
+ "selection-action"
17664
+ ],
17665
+ "examples": [
17666
+ {
17667
+ "title": "run a custom action against explicit row ids",
17668
+ "body": {
17669
+ "selection": {
17670
+ "type": "ids",
17671
+ "ids": [
17672
+ 2188890411
17673
+ ],
17674
+ "length": 1
17675
+ }
17676
+ },
17677
+ "notes": [
17678
+ "pass the backend action code with --action",
17679
+ "for query action, use ids or all selection instead of condition selection"
17680
+ ]
17681
+ },
17682
+ {
17683
+ "title": "run a custom action against a filtered selection",
17684
+ "body": {
17685
+ "selection": {
17686
+ "type": "condition",
17687
+ "condition": {
17688
+ "selectAll": true,
17689
+ "q": "SO-1001"
17690
+ },
17691
+ "length": 0
17692
+ }
17693
+ },
17694
+ "notes": [
17695
+ "condition selection uses row-query style keys such as q, tab, and filter_:1002"
17696
+ ]
17697
+ }
17698
+ ]
17699
+ }
17700
+ };
17701
+
17702
+ // src/runtime/help_examples.ts
17703
+ function binaryForScope(scope) {
17704
+ return scope === "admin" ? "arcubase-admin" : "arcubase";
17705
+ }
17706
+ function getFixedValue(value) {
17707
+ if (!("fixedValue" in value)) {
17708
+ return void 0;
17709
+ }
17710
+ const fixedValue = value.fixedValue;
17711
+ return typeof fixedValue === "string" ? fixedValue : void 0;
17712
+ }
17713
+ function pathFlagPlaceholders(command) {
17714
+ const parts = command.pathParams.filter((item) => !getFixedValue(item)).map((item) => `--${item.flag} <${item.flag.replace(/-/g, "_")}>`);
17715
+ return parts.length > 0 ? `${parts.join(" ")} ` : "";
17716
+ }
17717
+ function helpExampleKey(scope, command) {
17718
+ return `${scope}.${command.commandPath.join(".")}`;
17719
+ }
17720
+ function getCommandHelpExamples(scope, command) {
17721
+ return helpExamplesIndex[helpExampleKey(scope, command)]?.examples ?? [];
17722
+ }
17723
+ function renderCommandHelpExamples(scope, command) {
17724
+ const binary = binaryForScope(scope);
17725
+ const examples = getCommandHelpExamples(scope, command);
17726
+ const header = `${binary} ${command.commandPath.join(" ")} --help-examples`;
17727
+ if (examples.length === 0) {
17728
+ return [
17729
+ header,
17730
+ "",
17731
+ "No dedicated examples for this final command yet.",
17732
+ `Use ${binary} ${command.commandPath.join(" ")} --help for flags, docs, and type paths.`
17733
+ ].join("\n");
17734
+ }
17735
+ return [
17736
+ header,
17737
+ "",
17738
+ ...examples.flatMap((example) => [
17739
+ `${example.title}:`,
17740
+ ...example.body === void 0 ? [] : [` ${binary} ${command.commandPath.join(" ")} ${pathFlagPlaceholders(command)}--body-json '${JSON.stringify(example.body)}'`.replace(/\s+/g, " ").trim()],
17741
+ ...(example.notes ?? []).map((note) => ` - ${note}`),
17742
+ ""
17743
+ ])
17744
+ ].join("\n").trimEnd();
17745
+ }
17746
+
17419
17747
  // src/runtime/execute.ts
17420
17748
  function renderRootHelp(scope) {
17421
17749
  const binary = scope === "admin" ? "arcubase-admin" : "arcubase";
@@ -17453,9 +17781,32 @@ function buildUnknownCommandDetails(scope, moduleName, commandName) {
17453
17781
  const suggestions = findCommandSuggestions(moduleName, commandName).map((item) => `${item.binary} ${item.moduleName}${item.commandName ? ` ${item.commandName}` : ""}`);
17454
17782
  return suggestions.length > 0 ? { suggestions } : void 0;
17455
17783
  }
17784
+ function throwHelpExamplesRequiresCommand(scope, moduleName) {
17785
+ const binary = binaryForScope2(scope);
17786
+ const suggestions = scope === "admin" ? [
17787
+ `${binary} access-rule create --help-examples`,
17788
+ `${binary} access-rule assign-users --help-examples`
17789
+ ] : [
17790
+ `${binary} row bulk-update --help-examples`,
17791
+ `${binary} row selection-action --help-examples`
17792
+ ];
17793
+ throw new CLIError(
17794
+ "HELP_EXAMPLES_REQUIRES_COMMAND",
17795
+ "--help-examples must follow a final command",
17796
+ 2,
17797
+ {
17798
+ operation: moduleName ? `${binary} ${moduleName} --help-examples` : `${binary} --help-examples`,
17799
+ hint: `retry with a final command, for example: ${suggestions[0]}`,
17800
+ suggestions
17801
+ }
17802
+ );
17803
+ }
17456
17804
  function sdkPath2(file2, env = process.env) {
17457
17805
  return resolveArcubaseSDKPath(file2, env);
17458
17806
  }
17807
+ function binaryForScope2(scope) {
17808
+ return scope === "admin" ? "arcubase-admin" : "arcubase";
17809
+ }
17459
17810
  function renderCommandHelp(scope, moduleName, commandName, env = process.env) {
17460
17811
  const command = findCommand(scope, moduleName, commandName);
17461
17812
  if (!command) {
@@ -17466,7 +17817,7 @@ function renderCommandHelp(scope, moduleName, commandName, env = process.env) {
17466
17817
  ...command.requestType ? getDocHints(command.requestType, env) : []
17467
17818
  ].filter((item, index, list) => list.findIndex((candidate) => candidate.file === item.file) === index);
17468
17819
  const typeHints = command.requestType ? getTypeHints(command.requestType, env) : [];
17469
- const pathFlags = command.pathParams.filter((item) => !getFixedValue(item)).map((item) => `--${item.flag}`);
17820
+ const pathFlags = command.pathParams.filter((item) => !getFixedValue2(item)).map((item) => `--${item.flag}`);
17470
17821
  const queryFlags = visibleQueryParams(command).map((item) => `--${item.flag}`);
17471
17822
  const isTableCreate = scope === "admin" && command.commandPath[0] === "table" && command.commandPath[1] === "create";
17472
17823
  const bodyTypeText = isTableCreate ? "TableCreateWithSchema" : command.requestType;
@@ -17478,6 +17829,7 @@ function renderCommandHelp(scope, moduleName, commandName, env = process.env) {
17478
17829
  command.requestType ? `body: ${bodyTypeText} via --body-json <json-string> | --body-file <file>.json` : "body: none",
17479
17830
  queryFlags.length ? `query flags: ${queryFlags.join(", ")}, --query-file` : "query flags: --query-file",
17480
17831
  pathFlags.length ? `path flags: ${pathFlags.join(", ")}` : "path flags: none",
17832
+ `copyable examples: ${scope === "admin" ? "arcubase-admin" : "arcubase"} ${command.commandPath.join(" ")} --help-examples`,
17481
17833
  ...isTableCreate ? [
17482
17834
  "body-json example:",
17483
17835
  ` - ${tableCreateExampleBody}`,
@@ -17576,7 +17928,7 @@ function coerceScalar(value, typeText) {
17576
17928
  }
17577
17929
  return value;
17578
17930
  }
17579
- function getFixedValue(value) {
17931
+ function getFixedValue2(value) {
17580
17932
  if (!("fixedValue" in value)) {
17581
17933
  return void 0;
17582
17934
  }
@@ -17586,7 +17938,7 @@ function getFixedValue(value) {
17586
17938
  function resolveEndpoint(command, flags) {
17587
17939
  let resolved = command.endpoint;
17588
17940
  for (const mapping of command.pathParams) {
17589
- const value = getFixedValue(mapping) ?? flagValue(flags, mapping.flag);
17941
+ const value = getFixedValue2(mapping) ?? flagValue(flags, mapping.flag);
17590
17942
  if (!value) {
17591
17943
  throw new CLIError("MISSING_PATH_FLAG", `missing required flag --${mapping.flag}`, 2);
17592
17944
  }
@@ -17600,21 +17952,21 @@ function buildScalarQuery(command, flags) {
17600
17952
  }
17601
17953
  const out = {};
17602
17954
  for (const scalar of effectiveQueryParams(command)) {
17603
- const value = getFixedValue(scalar) ?? flagValue(flags, scalar.flag);
17955
+ const value = getFixedValue2(scalar) ?? flagValue(flags, scalar.flag);
17604
17956
  if (value === void 0) continue;
17605
17957
  out[scalar.key] = coerceScalar(value, scalar.type);
17606
17958
  }
17607
17959
  return Object.keys(out).length > 0 ? out : void 0;
17608
17960
  }
17609
17961
  function validateKnownFlags(command, flags, env = process.env) {
17610
- const allowed = /* @__PURE__ */ new Set(["help", "body-file", "body-json", "query-file"]);
17962
+ const allowed = /* @__PURE__ */ new Set(["help", "help-examples", "body-file", "body-json", "query-file"]);
17611
17963
  for (const mapping of command.pathParams) {
17612
- if (!getFixedValue(mapping)) {
17964
+ if (!getFixedValue2(mapping)) {
17613
17965
  allowed.add(mapping.flag);
17614
17966
  }
17615
17967
  }
17616
17968
  for (const query of visibleQueryParams(command)) {
17617
- if (!getFixedValue(query)) {
17969
+ if (!getFixedValue2(query)) {
17618
17970
  allowed.add(query.flag);
17619
17971
  }
17620
17972
  }
@@ -17652,7 +18004,7 @@ function buildUnknownFlagDetails(command, flag, env = process.env) {
17652
18004
  `do not pass request body fields as --${flag}`,
17653
18005
  "put request body fields inside --body-json <json-string>"
17654
18006
  ];
17655
- details.suggestions = [`retry with: ${command.commandPath.join(" ")} ${command.pathParams.filter((item) => !getFixedValue(item)).map((item) => `--${item.flag} <${item.flag.replace(/-/g, "_")}>`).join(" ")} --body-json '<json>'`.replace(/\s+/g, " ").trim()];
18007
+ details.suggestions = [`retry with: ${command.commandPath.join(" ")} ${command.pathParams.filter((item) => !getFixedValue2(item)).map((item) => `--${item.flag} <${item.flag.replace(/-/g, "_")}>`).join(" ")} --body-json '<json>'`.replace(/\s+/g, " ").trim()];
17656
18008
  }
17657
18009
  if (command.commandPath[0] === "row" && command.commandPath[1] === "query" && ["limit", "offset", "search", "sorts", "view-mode"].includes(flag)) {
17658
18010
  Object.assign(details, buildBodyGuidance("EntityQueryReqVO"));
@@ -17668,7 +18020,7 @@ function buildUnknownFlagDetails(command, flag, env = process.env) {
17668
18020
  }
17669
18021
  function visibleQueryParams(command) {
17670
18022
  if (!command) return [];
17671
- return effectiveQueryParams(command).filter((item) => !getFixedValue(item));
18023
+ return effectiveQueryParams(command).filter((item) => !getFixedValue2(item));
17672
18024
  }
17673
18025
  function effectiveQueryParams(command) {
17674
18026
  if (!command) return [];
@@ -18324,6 +18676,31 @@ function validateAccessRuleUserScope(scope, command, requestType, userScope, env
18324
18676
  }
18325
18677
  }
18326
18678
  }
18679
+ function normalizeNumericAccessRuleUserScopeIDs(scope, command, body) {
18680
+ if (!isAccessRuleCommand(scope, command) || !isRecord3(body) || !isRecord3(body.options) || !Array.isArray(body.options.user_scope)) {
18681
+ return body;
18682
+ }
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;
18696
+ return {
18697
+ ...body,
18698
+ options: {
18699
+ ...body.options,
18700
+ user_scope: userScope
18701
+ }
18702
+ };
18703
+ }
18327
18704
  function requireUpdateContains(scope, command, body, field, env) {
18328
18705
  if (!(field in body)) return;
18329
18706
  const update = stringArray(body.update);
@@ -19124,16 +19501,32 @@ async function executeCLI(scope, argv, env, fetchImpl = fetch) {
19124
19501
  const parsed = parseCLIArgs(argv);
19125
19502
  const envInput = env ?? process.env;
19126
19503
  if (parsed.commandTokens.length === 0) {
19504
+ if (hasFlag(parsed.flags, "help-examples")) {
19505
+ throwHelpExamplesRequiresCommand(scope);
19506
+ }
19127
19507
  return { kind: "help", text: renderRootHelp(scope) };
19128
19508
  }
19129
19509
  const [moduleName, commandName] = parsed.commandTokens;
19130
19510
  if (!moduleName) {
19511
+ if (hasFlag(parsed.flags, "help-examples")) {
19512
+ throwHelpExamplesRequiresCommand(scope);
19513
+ }
19131
19514
  return { kind: "help", text: renderRootHelp(scope) };
19132
19515
  }
19133
19516
  const singleTokenCommand = !commandName ? findCommand(scope, moduleName) : null;
19134
19517
  if (!commandName && !singleTokenCommand) {
19518
+ if (hasFlag(parsed.flags, "help-examples")) {
19519
+ throwHelpExamplesRequiresCommand(scope, moduleName);
19520
+ }
19135
19521
  return { kind: "help", text: renderModuleHelp(scope, moduleName, envInput) };
19136
19522
  }
19523
+ if (hasFlag(parsed.flags, "help-examples")) {
19524
+ const command2 = commandName ? findCommand(scope, moduleName, commandName) : singleTokenCommand;
19525
+ if (!command2) {
19526
+ throw new CLIError("UNKNOWN_COMMAND", `unknown command: ${moduleName}${commandName ? ` ${commandName}` : ""}`, 2, buildUnknownCommandDetails(scope, moduleName, commandName ?? ""));
19527
+ }
19528
+ return { kind: "help", text: renderCommandHelpExamples(scope, command2) };
19529
+ }
19137
19530
  if (hasFlag(parsed.flags, "help")) {
19138
19531
  return { kind: "help", text: renderCommandHelp(scope, moduleName, commandName, envInput) };
19139
19532
  }
@@ -19178,6 +19571,7 @@ async function executeCLI(scope, argv, env, fetchImpl = fetch) {
19178
19571
  if (body === void 0) {
19179
19572
  throw new CLIError("MISSING_BODY_FILE", `command requires --body-json or --body-file for ${command.requestType}; prefer --body-json to avoid file creation`, 2);
19180
19573
  }
19574
+ body = normalizeNumericAccessRuleUserScopeIDs(scope, command, body);
19181
19575
  if (isTableCreateCommand(scope, command)) {
19182
19576
  requireTableCreateSchemaBody(scope, command, body, runtimeEnv);
19183
19577
  validateActionSpecificRawBody(scope, command, body, runtimeEnv);