kintone-migrator 0.24.4 → 0.24.5
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.
- package/dist/index.mjs +274 -205
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -4,9 +4,10 @@ import "dotenv/config";
|
|
|
4
4
|
import { cli, define } from "gunshi";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
import { parse, stringify } from "yaml";
|
|
7
|
+
import { KintoneRestAPIClient, KintoneRestAPIError } from "@kintone/rest-api-client";
|
|
7
8
|
import { access, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
8
9
|
import { basename, dirname, extname, join, resolve } from "node:path";
|
|
9
|
-
import {
|
|
10
|
+
import { realpathSync } from "node:fs";
|
|
10
11
|
import * as v from "valibot";
|
|
11
12
|
import pc from "picocolors";
|
|
12
13
|
|
|
@@ -290,6 +291,10 @@ var ConflictError = class extends ApplicationError {
|
|
|
290
291
|
function isConflictError(error) {
|
|
291
292
|
return error instanceof ConflictError;
|
|
292
293
|
}
|
|
294
|
+
const UnauthenticatedErrorCode = {
|
|
295
|
+
AuthenticationRequired: "AUTHENTICATION_REQUIRED",
|
|
296
|
+
InvalidCredentials: "INVALID_CREDENTIALS"
|
|
297
|
+
};
|
|
293
298
|
var UnauthenticatedError = class extends ApplicationError {
|
|
294
299
|
name = "UnauthenticatedError";
|
|
295
300
|
constructor(code, message, cause) {
|
|
@@ -300,6 +305,7 @@ var UnauthenticatedError = class extends ApplicationError {
|
|
|
300
305
|
function isUnauthenticatedError(error) {
|
|
301
306
|
return error instanceof UnauthenticatedError;
|
|
302
307
|
}
|
|
308
|
+
const ForbiddenErrorCode = { InsufficientPermissions: "INSUFFICIENT_PERMISSIONS" };
|
|
303
309
|
var ForbiddenError = class extends ApplicationError {
|
|
304
310
|
name = "ForbiddenError";
|
|
305
311
|
constructor(code, message, cause) {
|
|
@@ -549,6 +555,60 @@ async function applyAction({ container }) {
|
|
|
549
555
|
});
|
|
550
556
|
}
|
|
551
557
|
|
|
558
|
+
//#endregion
|
|
559
|
+
//#region src/core/adapters/kintone/wrapKintoneError.ts
|
|
560
|
+
const KINTONE_REVISION_CONFLICT_CODE = "GAIA_CO02";
|
|
561
|
+
const KINTONE_MAINTENANCE_MODE_CODE = "GAIA_NO02";
|
|
562
|
+
function formatMessage(message, error) {
|
|
563
|
+
if (error instanceof KintoneRestAPIError && error.message) return `${message}: ${error.message}`;
|
|
564
|
+
if (error instanceof Error && error.message) return `${message}: ${error.message}`;
|
|
565
|
+
return message;
|
|
566
|
+
}
|
|
567
|
+
/**
|
|
568
|
+
* Converts an unknown error thrown during a kintone API call into the
|
|
569
|
+
* appropriate application-layer error.
|
|
570
|
+
*
|
|
571
|
+
* - {@link BusinessRuleError} and {@link ApplicationError} (including all
|
|
572
|
+
* subclasses such as {@link SystemError}, {@link ValidationError}, etc.)
|
|
573
|
+
* are re-thrown as-is so that errors raised inside a try block are never
|
|
574
|
+
* silently converted.
|
|
575
|
+
* - {@link KintoneRestAPIError} is mapped by HTTP status (401/403/404/409/400)
|
|
576
|
+
* and kintone-specific codes (`GAIA_CO02` revision conflict, `GAIA_NO02`
|
|
577
|
+
* maintenance mode).
|
|
578
|
+
* - Everything else becomes a {@link SystemError} with code `ExternalApiError`.
|
|
579
|
+
*
|
|
580
|
+
* **Note on 400 mapping**: All 400 errors (except `GAIA_CO02`) are mapped to
|
|
581
|
+
* {@link ValidationError}. This is intentionally simplified — some 400 codes
|
|
582
|
+
* (e.g. `GAIA_RE18` record limit, `GAIA_AP01` app unavailable) may have
|
|
583
|
+
* different semantics, but for this CLI tool a single `ValidationError`
|
|
584
|
+
* with the original kintone message in the error detail is sufficient.
|
|
585
|
+
*
|
|
586
|
+
* @throws {UnauthenticatedError} when kintone returns 401
|
|
587
|
+
* @throws {ForbiddenError} when kintone returns 403 (non-GAIA_NO02)
|
|
588
|
+
* @throws {SystemError} when kintone returns 403 with GAIA_NO02 (maintenance mode)
|
|
589
|
+
* @throws {NotFoundError} when kintone returns 404
|
|
590
|
+
* @throws {ConflictError} when kintone returns 409 or GAIA_CO02
|
|
591
|
+
* @throws {ValidationError} when kintone returns 400 (non-GAIA_CO02)
|
|
592
|
+
* @throws {SystemError} for all other kintone errors and unknown errors
|
|
593
|
+
*/
|
|
594
|
+
function wrapKintoneError(error, message) {
|
|
595
|
+
if (isBusinessRuleError(error)) throw error;
|
|
596
|
+
if (error instanceof ApplicationError) throw error;
|
|
597
|
+
const detail = formatMessage(message, error);
|
|
598
|
+
if (error instanceof KintoneRestAPIError) {
|
|
599
|
+
if (error.status === 401) throw new UnauthenticatedError(UnauthenticatedErrorCode.InvalidCredentials, detail, error);
|
|
600
|
+
if (error.status === 403) {
|
|
601
|
+
if (error.code === KINTONE_MAINTENANCE_MODE_CODE) throw new SystemError(SystemErrorCode.ExternalApiError, `${detail} (app is in maintenance mode — GAIA_NO02)`, error);
|
|
602
|
+
throw new ForbiddenError(ForbiddenErrorCode.InsufficientPermissions, detail, error);
|
|
603
|
+
}
|
|
604
|
+
if (error.status === 404) throw new NotFoundError(NotFoundErrorCode.NotFound, detail, error);
|
|
605
|
+
if (error.code === KINTONE_REVISION_CONFLICT_CODE) throw new ConflictError(ConflictErrorCode.Conflict, `${detail} (revision conflict — GAIA_CO02). Please retry the operation.`, error);
|
|
606
|
+
if (error.status === 409) throw new ConflictError(ConflictErrorCode.Conflict, detail, error);
|
|
607
|
+
if (error.status === 400) throw new ValidationError(ValidationErrorCode.InvalidInput, detail, error);
|
|
608
|
+
}
|
|
609
|
+
throw new SystemError(SystemErrorCode.ExternalApiError, detail, error);
|
|
610
|
+
}
|
|
611
|
+
|
|
552
612
|
//#endregion
|
|
553
613
|
//#region src/core/adapters/kintone/actionConfigurator.ts
|
|
554
614
|
function fromKintoneDestApp(raw) {
|
|
@@ -634,9 +694,7 @@ var KintoneActionConfigurator = class {
|
|
|
634
694
|
revision: response.revision
|
|
635
695
|
};
|
|
636
696
|
} catch (error) {
|
|
637
|
-
|
|
638
|
-
if (error instanceof SystemError) throw error;
|
|
639
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get app actions", error);
|
|
697
|
+
throw wrapKintoneError(error, "Failed to get app actions");
|
|
640
698
|
}
|
|
641
699
|
}
|
|
642
700
|
async updateActions(params) {
|
|
@@ -650,15 +708,22 @@ var KintoneActionConfigurator = class {
|
|
|
650
708
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
651
709
|
return { revision: (await this.client.app.updateAppActions(requestParams)).revision };
|
|
652
710
|
} catch (error) {
|
|
653
|
-
|
|
654
|
-
if (error instanceof SystemError) throw error;
|
|
655
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update app actions", error);
|
|
711
|
+
throw wrapKintoneError(error, "Failed to update app actions");
|
|
656
712
|
}
|
|
657
713
|
}
|
|
658
714
|
};
|
|
659
715
|
|
|
660
716
|
//#endregion
|
|
661
717
|
//#region src/core/adapters/kintone/appDeployer.ts
|
|
718
|
+
const VALID_DEPLOY_STATUSES = new Set([
|
|
719
|
+
"PROCESSING",
|
|
720
|
+
"SUCCESS",
|
|
721
|
+
"FAIL",
|
|
722
|
+
"CANCEL"
|
|
723
|
+
]);
|
|
724
|
+
function isDeployStatus(value) {
|
|
725
|
+
return VALID_DEPLOY_STATUSES.has(value);
|
|
726
|
+
}
|
|
662
727
|
const DEFAULT_POLL_INTERVAL_MS = 1e3;
|
|
663
728
|
const DEFAULT_MAX_RETRIES = 180;
|
|
664
729
|
var KintoneAppDeployer = class {
|
|
@@ -673,12 +738,10 @@ var KintoneAppDeployer = class {
|
|
|
673
738
|
async deploy() {
|
|
674
739
|
try {
|
|
675
740
|
await this.client.app.deployApp({ apps: [{ app: this.appId }] });
|
|
676
|
-
await this.waitForDeployment();
|
|
677
741
|
} catch (error) {
|
|
678
|
-
|
|
679
|
-
if (error instanceof SystemError) throw error;
|
|
680
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to deploy app", error);
|
|
742
|
+
throw wrapKintoneError(error, "Failed to deploy app");
|
|
681
743
|
}
|
|
744
|
+
await this.waitForDeployment();
|
|
682
745
|
}
|
|
683
746
|
async waitForDeployment() {
|
|
684
747
|
let lastPollError;
|
|
@@ -692,20 +755,21 @@ var KintoneAppDeployer = class {
|
|
|
692
755
|
consecutivePollFailures = 0;
|
|
693
756
|
} catch (error) {
|
|
694
757
|
if (isBusinessRuleError(error)) throw error;
|
|
695
|
-
if (error instanceof
|
|
758
|
+
if (error instanceof ApplicationError) throw error;
|
|
696
759
|
lastPollError = error;
|
|
697
760
|
consecutivePollFailures++;
|
|
698
761
|
if (consecutivePollFailures >= maxConsecutivePollFailures) throw new SystemError(SystemErrorCode.ExternalApiError, `Deploy status polling failed ${maxConsecutivePollFailures} consecutive times`, lastPollError);
|
|
699
762
|
continue;
|
|
700
763
|
}
|
|
701
|
-
|
|
702
|
-
|
|
764
|
+
if (apps.length === 0) throw new SystemError(SystemErrorCode.ExternalApiError, "Deploy status response contained no apps");
|
|
765
|
+
const rawStatus = apps[0].status;
|
|
766
|
+
if (rawStatus === void 0) throw new SystemError(SystemErrorCode.ExternalApiError, "Deploy status unavailable");
|
|
767
|
+
if (!isDeployStatus(rawStatus)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected deploy status: ${rawStatus}`);
|
|
768
|
+
switch (rawStatus) {
|
|
703
769
|
case "SUCCESS": return;
|
|
704
770
|
case "FAIL": throw new SystemError(SystemErrorCode.ExternalApiError, "App deployment failed");
|
|
705
771
|
case "CANCEL": throw new SystemError(SystemErrorCode.ExternalApiError, "App deployment was cancelled");
|
|
706
772
|
case "PROCESSING": continue;
|
|
707
|
-
case void 0: throw new SystemError(SystemErrorCode.ExternalApiError, "Deploy status unavailable");
|
|
708
|
-
default: throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected deploy status: ${status}`);
|
|
709
773
|
}
|
|
710
774
|
}
|
|
711
775
|
throw new SystemError(SystemErrorCode.ExternalApiError, "App deployment timed out", lastPollError);
|
|
@@ -834,9 +898,7 @@ var KintoneCustomizationConfigurator = class {
|
|
|
834
898
|
revision: response.revision
|
|
835
899
|
};
|
|
836
900
|
} catch (error) {
|
|
837
|
-
|
|
838
|
-
if (error instanceof SystemError) throw error;
|
|
839
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get app customization", error);
|
|
901
|
+
throw wrapKintoneError(error, "Failed to get app customization");
|
|
840
902
|
}
|
|
841
903
|
}
|
|
842
904
|
async updateCustomization(params) {
|
|
@@ -857,9 +919,7 @@ var KintoneCustomizationConfigurator = class {
|
|
|
857
919
|
const response = await this.client.app.updateAppCustomize(requestParams);
|
|
858
920
|
return { revision: String(response.revision) };
|
|
859
921
|
} catch (error) {
|
|
860
|
-
|
|
861
|
-
if (error instanceof SystemError) throw error;
|
|
862
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update app customization", error);
|
|
922
|
+
throw wrapKintoneError(error, "Failed to update app customization");
|
|
863
923
|
}
|
|
864
924
|
}
|
|
865
925
|
};
|
|
@@ -871,31 +931,77 @@ var KintoneFileDownloader = class {
|
|
|
871
931
|
this.client = client;
|
|
872
932
|
}
|
|
873
933
|
async download(fileKey) {
|
|
874
|
-
if (!fileKey) throw new
|
|
934
|
+
if (!fileKey) throw new ValidationError(ValidationErrorCode.InvalidInput, "fileKey must not be empty");
|
|
875
935
|
try {
|
|
876
936
|
return await this.client.file.downloadFile({ fileKey });
|
|
877
937
|
} catch (error) {
|
|
878
|
-
|
|
879
|
-
if (error instanceof SystemError) throw error;
|
|
880
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, `Failed to download file: ${fileKey}`, error);
|
|
938
|
+
throw wrapKintoneError(error, `Failed to download file: ${fileKey}`);
|
|
881
939
|
}
|
|
882
940
|
}
|
|
883
941
|
};
|
|
884
942
|
|
|
943
|
+
//#endregion
|
|
944
|
+
//#region src/lib/safePath.ts
|
|
945
|
+
/**
|
|
946
|
+
* Returns `true` if {@link targetPath} resolves to a location within
|
|
947
|
+
* {@link baseDir}. Follows symlinks via `realpathSync` for both the base
|
|
948
|
+
* directory and the target path when they exist on disk, falling back to
|
|
949
|
+
* textual `resolve` otherwise (e.g. in tests with virtual paths or when
|
|
950
|
+
* the target does not yet exist).
|
|
951
|
+
*
|
|
952
|
+
* {@link targetPath} may be either a relative path (resolved against
|
|
953
|
+
* {@link baseDir}) or an absolute path (used as-is by `path.resolve`).
|
|
954
|
+
* When callers have already resolved the path via `path.resolve(baseDir, x)`,
|
|
955
|
+
* passing the result as an absolute path is safe — `resolve(base, absPath)`
|
|
956
|
+
* returns `absPath` unchanged.
|
|
957
|
+
*
|
|
958
|
+
* **Limitation — symlinks**: If the target path does not exist on disk,
|
|
959
|
+
* symlink components within it cannot be resolved. The check falls back
|
|
960
|
+
* to a textual prefix comparison, which still catches `..` traversal but
|
|
961
|
+
* cannot detect symlink-based escapes for non-existent paths.
|
|
962
|
+
*
|
|
963
|
+
* **Limitation — TOCTOU**: This check is inherently subject to a
|
|
964
|
+
* time-of-check-to-time-of-use race: the filesystem may change between
|
|
965
|
+
* this call and the subsequent file operation. This is acceptable for a
|
|
966
|
+
* CLI tool where the user controls the local environment.
|
|
967
|
+
*
|
|
968
|
+
* **Note**: This function performs synchronous I/O (`realpathSync`).
|
|
969
|
+
*
|
|
970
|
+
* Callers decide how to handle the result.
|
|
971
|
+
*/
|
|
972
|
+
function isSafePath(targetPath, baseDir) {
|
|
973
|
+
if (targetPath.includes("\0") || baseDir.includes("\0")) return false;
|
|
974
|
+
let resolvedBase;
|
|
975
|
+
try {
|
|
976
|
+
resolvedBase = realpathSync(baseDir);
|
|
977
|
+
} catch {
|
|
978
|
+
resolvedBase = resolve(baseDir);
|
|
979
|
+
}
|
|
980
|
+
const textualTarget = resolve(resolvedBase, targetPath);
|
|
981
|
+
let resolvedTarget;
|
|
982
|
+
try {
|
|
983
|
+
resolvedTarget = realpathSync(textualTarget);
|
|
984
|
+
} catch {
|
|
985
|
+
resolvedTarget = textualTarget;
|
|
986
|
+
}
|
|
987
|
+
return resolvedTarget.startsWith(`${resolvedBase}/`) || resolvedTarget === resolvedBase;
|
|
988
|
+
}
|
|
989
|
+
|
|
885
990
|
//#endregion
|
|
886
991
|
//#region src/core/adapters/kintone/fileUploader.ts
|
|
887
992
|
var KintoneFileUploader = class {
|
|
888
|
-
constructor(client) {
|
|
993
|
+
constructor(client, baseDir) {
|
|
889
994
|
this.client = client;
|
|
995
|
+
this.baseDir = baseDir;
|
|
890
996
|
}
|
|
891
997
|
async upload(filePath) {
|
|
892
|
-
if (!filePath) throw new
|
|
998
|
+
if (!filePath) throw new ValidationError(ValidationErrorCode.InvalidInput, "filePath must not be empty");
|
|
999
|
+
const resolvedPath = resolve(this.baseDir, filePath);
|
|
1000
|
+
if (!isSafePath(resolvedPath, this.baseDir)) throw new ValidationError(ValidationErrorCode.InvalidInput, `Path traversal detected: "${resolvedPath}" escapes base directory "${this.baseDir}"`);
|
|
893
1001
|
try {
|
|
894
|
-
return { fileKey: (await this.client.file.uploadFile({ file: { path:
|
|
1002
|
+
return { fileKey: (await this.client.file.uploadFile({ file: { path: resolvedPath } })).fileKey };
|
|
895
1003
|
} catch (error) {
|
|
896
|
-
|
|
897
|
-
if (error instanceof SystemError) throw error;
|
|
898
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, `Failed to upload file: ${filePath}`, error);
|
|
1004
|
+
throw wrapKintoneError(error, `Failed to upload file: ${filePath}`);
|
|
899
1005
|
}
|
|
900
1006
|
}
|
|
901
1007
|
};
|
|
@@ -976,10 +1082,6 @@ const DECORATION_TYPES$1 = new Set([
|
|
|
976
1082
|
"SPACER",
|
|
977
1083
|
"HR"
|
|
978
1084
|
]);
|
|
979
|
-
const KINTONE_REVISION_CONFLICT_CODE = "GAIA_CO02";
|
|
980
|
-
function isRevisionConflict(error) {
|
|
981
|
-
return error instanceof KintoneRestAPIError && error.code === KINTONE_REVISION_CONFLICT_CODE;
|
|
982
|
-
}
|
|
983
1085
|
/**
|
|
984
1086
|
* Tracks the latest known kintone form revision for optimistic concurrency control.
|
|
985
1087
|
*
|
|
@@ -992,10 +1094,13 @@ function isRevisionConflict(error) {
|
|
|
992
1094
|
var RevisionTracker = class {
|
|
993
1095
|
revision = void 0;
|
|
994
1096
|
track(revision) {
|
|
995
|
-
if (
|
|
1097
|
+
if (revision === "") throw new SystemError(SystemErrorCode.ExternalApiError, "Unexpected empty revision from kintone API");
|
|
1098
|
+
const revisionNum = Number(revision);
|
|
1099
|
+
if (!Number.isFinite(revisionNum)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric revision from kintone API: "${revision}"`);
|
|
1100
|
+
if (this.revision === void 0 || revisionNum > this.revision) this.revision = revisionNum;
|
|
996
1101
|
}
|
|
997
1102
|
get current() {
|
|
998
|
-
return this.revision;
|
|
1103
|
+
return this.revision !== void 0 ? String(this.revision) : void 0;
|
|
999
1104
|
}
|
|
1000
1105
|
};
|
|
1001
1106
|
function assertRecord(value, fieldPath) {
|
|
@@ -1267,7 +1372,7 @@ var KintoneFormConfigurator = class {
|
|
|
1267
1372
|
app: this.appId,
|
|
1268
1373
|
preview: true
|
|
1269
1374
|
});
|
|
1270
|
-
this.revisionTracker.track(revision);
|
|
1375
|
+
if (revision) this.revisionTracker.track(revision);
|
|
1271
1376
|
const fields = /* @__PURE__ */ new Map();
|
|
1272
1377
|
for (const [code, prop] of Object.entries(properties)) {
|
|
1273
1378
|
const kintoneProp = prop;
|
|
@@ -1278,9 +1383,7 @@ var KintoneFormConfigurator = class {
|
|
|
1278
1383
|
}
|
|
1279
1384
|
return fields;
|
|
1280
1385
|
} catch (error) {
|
|
1281
|
-
|
|
1282
|
-
if (error instanceof SystemError) throw error;
|
|
1283
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get form fields", error);
|
|
1386
|
+
throw wrapKintoneError(error, "Failed to get form fields");
|
|
1284
1387
|
}
|
|
1285
1388
|
}
|
|
1286
1389
|
async addFields(fields) {
|
|
@@ -1294,10 +1397,7 @@ var KintoneFormConfigurator = class {
|
|
|
1294
1397
|
});
|
|
1295
1398
|
if (response.revision) this.revisionTracker.track(response.revision);
|
|
1296
1399
|
} catch (error) {
|
|
1297
|
-
|
|
1298
|
-
if (error instanceof SystemError) throw error;
|
|
1299
|
-
if (isRevisionConflict(error)) throw new ConflictError(ConflictErrorCode.Conflict, "Form configuration was modified by another process. Please retry the operation.", error);
|
|
1300
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to add form fields", error);
|
|
1400
|
+
throw wrapKintoneError(error, "Failed to add form fields");
|
|
1301
1401
|
}
|
|
1302
1402
|
}
|
|
1303
1403
|
async updateFields(fields) {
|
|
@@ -1311,10 +1411,7 @@ var KintoneFormConfigurator = class {
|
|
|
1311
1411
|
});
|
|
1312
1412
|
if (response.revision) this.revisionTracker.track(response.revision);
|
|
1313
1413
|
} catch (error) {
|
|
1314
|
-
|
|
1315
|
-
if (error instanceof SystemError) throw error;
|
|
1316
|
-
if (isRevisionConflict(error)) throw new ConflictError(ConflictErrorCode.Conflict, "Form configuration was modified by another process. Please retry the operation.", error);
|
|
1317
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update form fields", error);
|
|
1414
|
+
throw wrapKintoneError(error, "Failed to update form fields");
|
|
1318
1415
|
}
|
|
1319
1416
|
}
|
|
1320
1417
|
async deleteFields(fieldCodes) {
|
|
@@ -1326,10 +1423,7 @@ var KintoneFormConfigurator = class {
|
|
|
1326
1423
|
});
|
|
1327
1424
|
if (response.revision) this.revisionTracker.track(response.revision);
|
|
1328
1425
|
} catch (error) {
|
|
1329
|
-
|
|
1330
|
-
if (error instanceof SystemError) throw error;
|
|
1331
|
-
if (isRevisionConflict(error)) throw new ConflictError(ConflictErrorCode.Conflict, "Form configuration was modified by another process. Please retry the operation.", error);
|
|
1332
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to delete form fields", error);
|
|
1426
|
+
throw wrapKintoneError(error, "Failed to delete form fields");
|
|
1333
1427
|
}
|
|
1334
1428
|
}
|
|
1335
1429
|
async getLayout() {
|
|
@@ -1338,12 +1432,10 @@ var KintoneFormConfigurator = class {
|
|
|
1338
1432
|
app: this.appId,
|
|
1339
1433
|
preview: true
|
|
1340
1434
|
});
|
|
1341
|
-
this.revisionTracker.track(response.revision);
|
|
1435
|
+
if (response.revision) this.revisionTracker.track(response.revision);
|
|
1342
1436
|
return response.layout.map(fromKintoneLayoutItem);
|
|
1343
1437
|
} catch (error) {
|
|
1344
|
-
|
|
1345
|
-
if (error instanceof SystemError) throw error;
|
|
1346
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get form layout", error);
|
|
1438
|
+
throw wrapKintoneError(error, "Failed to get form layout");
|
|
1347
1439
|
}
|
|
1348
1440
|
}
|
|
1349
1441
|
async updateLayout(layout) {
|
|
@@ -1356,10 +1448,7 @@ var KintoneFormConfigurator = class {
|
|
|
1356
1448
|
});
|
|
1357
1449
|
if (response.revision) this.revisionTracker.track(response.revision);
|
|
1358
1450
|
} catch (error) {
|
|
1359
|
-
|
|
1360
|
-
if (error instanceof SystemError) throw error;
|
|
1361
|
-
if (isRevisionConflict(error)) throw new ConflictError(ConflictErrorCode.Conflict, "Form configuration was modified by another process. Please retry the operation.", error);
|
|
1362
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update form layout", error);
|
|
1451
|
+
throw wrapKintoneError(error, "Failed to update form layout");
|
|
1363
1452
|
}
|
|
1364
1453
|
}
|
|
1365
1454
|
};
|
|
@@ -1480,9 +1569,7 @@ var KintoneRecordManager = class {
|
|
|
1480
1569
|
};
|
|
1481
1570
|
});
|
|
1482
1571
|
} catch (error) {
|
|
1483
|
-
|
|
1484
|
-
if (error instanceof SystemError) throw error;
|
|
1485
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get records", error);
|
|
1572
|
+
throw wrapKintoneError(error, "Failed to get records");
|
|
1486
1573
|
}
|
|
1487
1574
|
}
|
|
1488
1575
|
async addRecords(records) {
|
|
@@ -1493,9 +1580,7 @@ var KintoneRecordManager = class {
|
|
|
1493
1580
|
records: records.map(toKintoneRecord)
|
|
1494
1581
|
});
|
|
1495
1582
|
} catch (error) {
|
|
1496
|
-
|
|
1497
|
-
if (error instanceof SystemError) throw error;
|
|
1498
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to add records", error);
|
|
1583
|
+
throw wrapKintoneError(error, "Failed to add records");
|
|
1499
1584
|
}
|
|
1500
1585
|
}
|
|
1501
1586
|
async updateRecords(records) {
|
|
@@ -1509,9 +1594,7 @@ var KintoneRecordManager = class {
|
|
|
1509
1594
|
}))
|
|
1510
1595
|
});
|
|
1511
1596
|
} catch (error) {
|
|
1512
|
-
|
|
1513
|
-
if (error instanceof SystemError) throw error;
|
|
1514
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update records", error);
|
|
1597
|
+
throw wrapKintoneError(error, "Failed to update records");
|
|
1515
1598
|
}
|
|
1516
1599
|
}
|
|
1517
1600
|
async deleteAllRecords() {
|
|
@@ -1528,9 +1611,7 @@ var KintoneRecordManager = class {
|
|
|
1528
1611
|
});
|
|
1529
1612
|
return { deletedCount: records.length };
|
|
1530
1613
|
} catch (error) {
|
|
1531
|
-
|
|
1532
|
-
if (error instanceof SystemError) throw error;
|
|
1533
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to delete all records", error);
|
|
1614
|
+
throw wrapKintoneError(error, "Failed to delete all records");
|
|
1534
1615
|
}
|
|
1535
1616
|
}
|
|
1536
1617
|
};
|
|
@@ -1544,10 +1625,15 @@ function createLocalFileCustomizationStorage(filePath) {
|
|
|
1544
1625
|
//#endregion
|
|
1545
1626
|
//#region src/core/adapters/local/fileWriter.ts
|
|
1546
1627
|
var LocalFileWriter = class {
|
|
1628
|
+
constructor(baseDir = process.cwd()) {
|
|
1629
|
+
this.baseDir = baseDir;
|
|
1630
|
+
}
|
|
1547
1631
|
async write(filePath, data) {
|
|
1632
|
+
const resolvedPath = resolve(this.baseDir, filePath);
|
|
1633
|
+
if (!isSafePath(resolvedPath, this.baseDir)) throw new ValidationError(ValidationErrorCode.InvalidInput, `Path traversal detected: "${resolvedPath}" escapes base directory "${this.baseDir}"`);
|
|
1548
1634
|
try {
|
|
1549
|
-
await mkdir(dirname(
|
|
1550
|
-
await writeFile(
|
|
1635
|
+
await mkdir(dirname(resolvedPath), { recursive: true });
|
|
1636
|
+
await writeFile(resolvedPath, Buffer.from(new Uint8Array(data)));
|
|
1551
1637
|
} catch (error) {
|
|
1552
1638
|
throw new SystemError(SystemErrorCode.StorageError, `Failed to write file: ${filePath}`, error);
|
|
1553
1639
|
}
|
|
@@ -1594,9 +1680,9 @@ function createCustomizationCliContainer(config) {
|
|
|
1594
1680
|
return {
|
|
1595
1681
|
customizationConfigurator: new KintoneCustomizationConfigurator(client, config.appId),
|
|
1596
1682
|
customizationStorage: createLocalFileCustomizationStorage(config.customizeFilePath),
|
|
1597
|
-
fileUploader: new KintoneFileUploader(client),
|
|
1683
|
+
fileUploader: new KintoneFileUploader(client, process.cwd()),
|
|
1598
1684
|
fileDownloader: new KintoneFileDownloader(client),
|
|
1599
|
-
fileWriter: new LocalFileWriter(),
|
|
1685
|
+
fileWriter: new LocalFileWriter(process.cwd()),
|
|
1600
1686
|
appDeployer: new KintoneAppDeployer(client, config.appId)
|
|
1601
1687
|
};
|
|
1602
1688
|
}
|
|
@@ -2936,9 +3022,7 @@ var KintoneAdminNotesConfigurator = class {
|
|
|
2936
3022
|
revision: response.revision
|
|
2937
3023
|
};
|
|
2938
3024
|
} catch (error) {
|
|
2939
|
-
|
|
2940
|
-
if (error instanceof SystemError) throw error;
|
|
2941
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get admin notes", error);
|
|
3025
|
+
throw wrapKintoneError(error, "Failed to get admin notes");
|
|
2942
3026
|
}
|
|
2943
3027
|
}
|
|
2944
3028
|
async updateAdminNotes(params) {
|
|
@@ -2951,9 +3035,7 @@ var KintoneAdminNotesConfigurator = class {
|
|
|
2951
3035
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
2952
3036
|
return { revision: (await this.client.app.updateAdminNotes(requestParams)).revision };
|
|
2953
3037
|
} catch (error) {
|
|
2954
|
-
|
|
2955
|
-
if (error instanceof SystemError) throw error;
|
|
2956
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update admin notes", error);
|
|
3038
|
+
throw wrapKintoneError(error, "Failed to update admin notes");
|
|
2957
3039
|
}
|
|
2958
3040
|
}
|
|
2959
3041
|
};
|
|
@@ -3303,9 +3385,7 @@ var KintoneAppPermissionConfigurator = class {
|
|
|
3303
3385
|
revision: response.revision
|
|
3304
3386
|
};
|
|
3305
3387
|
} catch (error) {
|
|
3306
|
-
|
|
3307
|
-
if (error instanceof SystemError) throw error;
|
|
3308
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get app ACL", error);
|
|
3388
|
+
throw wrapKintoneError(error, "Failed to get app ACL");
|
|
3309
3389
|
}
|
|
3310
3390
|
}
|
|
3311
3391
|
async updateAppPermissions(params) {
|
|
@@ -3317,9 +3397,7 @@ var KintoneAppPermissionConfigurator = class {
|
|
|
3317
3397
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
3318
3398
|
return { revision: (await this.client.app.updateAppAcl(requestParams)).revision };
|
|
3319
3399
|
} catch (error) {
|
|
3320
|
-
|
|
3321
|
-
if (error instanceof SystemError) throw error;
|
|
3322
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update app ACL", error);
|
|
3400
|
+
throw wrapKintoneError(error, "Failed to update app ACL");
|
|
3323
3401
|
}
|
|
3324
3402
|
}
|
|
3325
3403
|
};
|
|
@@ -4212,9 +4290,7 @@ var KintoneFieldPermissionConfigurator = class {
|
|
|
4212
4290
|
revision: response.revision
|
|
4213
4291
|
};
|
|
4214
4292
|
} catch (error) {
|
|
4215
|
-
|
|
4216
|
-
if (error instanceof SystemError) throw error;
|
|
4217
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get field ACL", error);
|
|
4293
|
+
throw wrapKintoneError(error, "Failed to get field ACL");
|
|
4218
4294
|
}
|
|
4219
4295
|
}
|
|
4220
4296
|
async updateFieldPermissions(params) {
|
|
@@ -4226,9 +4302,7 @@ var KintoneFieldPermissionConfigurator = class {
|
|
|
4226
4302
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
4227
4303
|
return { revision: (await this.client.app.updateFieldAcl(requestParams)).revision };
|
|
4228
4304
|
} catch (error) {
|
|
4229
|
-
|
|
4230
|
-
if (error instanceof SystemError) throw error;
|
|
4231
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update field ACL", error);
|
|
4305
|
+
throw wrapKintoneError(error, "Failed to update field ACL");
|
|
4232
4306
|
}
|
|
4233
4307
|
}
|
|
4234
4308
|
};
|
|
@@ -4557,6 +4631,14 @@ var field_acl_default = define({
|
|
|
4557
4631
|
run: () => {}
|
|
4558
4632
|
});
|
|
4559
4633
|
|
|
4634
|
+
//#endregion
|
|
4635
|
+
//#region src/core/adapters/kintone/parseKintoneIntegerField.ts
|
|
4636
|
+
function parseKintoneIntegerField(raw, fieldName) {
|
|
4637
|
+
const n = Number(raw);
|
|
4638
|
+
if (!Number.isFinite(n) || !Number.isInteger(n)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-integer ${fieldName} from kintone API: ${raw}`);
|
|
4639
|
+
return n;
|
|
4640
|
+
}
|
|
4641
|
+
|
|
4560
4642
|
//#endregion
|
|
4561
4643
|
//#region src/core/adapters/kintone/generalSettingsConfigurator.ts
|
|
4562
4644
|
const VALID_THEMES$1 = new Set([
|
|
@@ -4601,8 +4683,8 @@ function fromKintoneTitleField(raw) {
|
|
|
4601
4683
|
function fromKintoneNumberPrecision(raw) {
|
|
4602
4684
|
if (!VALID_ROUNDING_MODES$1.has(raw.roundingMode)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected roundingMode from kintone API: ${raw.roundingMode}`);
|
|
4603
4685
|
return {
|
|
4604
|
-
digits:
|
|
4605
|
-
decimalPlaces:
|
|
4686
|
+
digits: parseKintoneIntegerField(raw.digits, "digits"),
|
|
4687
|
+
decimalPlaces: parseKintoneIntegerField(raw.decimalPlaces, "decimalPlaces"),
|
|
4606
4688
|
roundingMode: raw.roundingMode
|
|
4607
4689
|
};
|
|
4608
4690
|
}
|
|
@@ -4620,7 +4702,7 @@ function fromKintoneSettings(raw) {
|
|
|
4620
4702
|
...raw.enableDuplicateRecord !== void 0 ? { enableDuplicateRecord: raw.enableDuplicateRecord } : {},
|
|
4621
4703
|
...raw.enableInlineRecordEditing !== void 0 ? { enableInlineRecordEditing: raw.enableInlineRecordEditing } : {},
|
|
4622
4704
|
...raw.numberPrecision !== void 0 ? { numberPrecision: fromKintoneNumberPrecision(raw.numberPrecision) } : {},
|
|
4623
|
-
...raw.firstMonthOfFiscalYear !== void 0 ? { firstMonthOfFiscalYear:
|
|
4705
|
+
...raw.firstMonthOfFiscalYear !== void 0 ? { firstMonthOfFiscalYear: parseKintoneIntegerField(raw.firstMonthOfFiscalYear, "firstMonthOfFiscalYear") } : {}
|
|
4624
4706
|
};
|
|
4625
4707
|
}
|
|
4626
4708
|
function toKintoneIcon(icon) {
|
|
@@ -4673,9 +4755,7 @@ var KintoneGeneralSettingsConfigurator = class {
|
|
|
4673
4755
|
revision: raw.revision ?? "-1"
|
|
4674
4756
|
};
|
|
4675
4757
|
} catch (error) {
|
|
4676
|
-
|
|
4677
|
-
if (error instanceof SystemError) throw error;
|
|
4678
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get general settings", error);
|
|
4758
|
+
throw wrapKintoneError(error, "Failed to get general settings");
|
|
4679
4759
|
}
|
|
4680
4760
|
}
|
|
4681
4761
|
async updateGeneralSettings(params) {
|
|
@@ -4687,9 +4767,7 @@ var KintoneGeneralSettingsConfigurator = class {
|
|
|
4687
4767
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
4688
4768
|
return { revision: (await this.client.app.updateAppSettings(requestParams)).revision };
|
|
4689
4769
|
} catch (error) {
|
|
4690
|
-
|
|
4691
|
-
if (error instanceof SystemError) throw error;
|
|
4692
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update general settings", error);
|
|
4770
|
+
throw wrapKintoneError(error, "Failed to update general settings");
|
|
4693
4771
|
}
|
|
4694
4772
|
}
|
|
4695
4773
|
};
|
|
@@ -4764,8 +4842,7 @@ function fromKintonePerRecordNotification(raw) {
|
|
|
4764
4842
|
};
|
|
4765
4843
|
}
|
|
4766
4844
|
function fromKintoneReminderNotification(raw) {
|
|
4767
|
-
const daysLater =
|
|
4768
|
-
if (!Number.isFinite(daysLater)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric daysLater from kintone API: ${raw.timing.daysLater}`);
|
|
4845
|
+
const daysLater = parseKintoneIntegerField(raw.timing.daysLater, "daysLater");
|
|
4769
4846
|
const result = {
|
|
4770
4847
|
code: raw.timing.code,
|
|
4771
4848
|
daysLater,
|
|
@@ -4774,8 +4851,7 @@ function fromKintoneReminderNotification(raw) {
|
|
|
4774
4851
|
targets: raw.targets.map(fromKintoneTarget)
|
|
4775
4852
|
};
|
|
4776
4853
|
if ("hoursLater" in raw.timing) {
|
|
4777
|
-
const hoursLater =
|
|
4778
|
-
if (!Number.isFinite(hoursLater)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric hoursLater from kintone API: ${raw.timing.hoursLater}`);
|
|
4854
|
+
const hoursLater = parseKintoneIntegerField(raw.timing.hoursLater, "hoursLater");
|
|
4779
4855
|
return {
|
|
4780
4856
|
...result,
|
|
4781
4857
|
hoursLater
|
|
@@ -4850,9 +4926,7 @@ var KintoneNotificationConfigurator = class {
|
|
|
4850
4926
|
revision: response.revision
|
|
4851
4927
|
};
|
|
4852
4928
|
} catch (error) {
|
|
4853
|
-
|
|
4854
|
-
if (error instanceof SystemError) throw error;
|
|
4855
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get general notifications", error);
|
|
4929
|
+
throw wrapKintoneError(error, "Failed to get general notifications");
|
|
4856
4930
|
}
|
|
4857
4931
|
}
|
|
4858
4932
|
async updateGeneralNotifications(params) {
|
|
@@ -4865,9 +4939,7 @@ var KintoneNotificationConfigurator = class {
|
|
|
4865
4939
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
4866
4940
|
return { revision: (await this.client.app.updateGeneralNotifications(requestParams)).revision };
|
|
4867
4941
|
} catch (error) {
|
|
4868
|
-
|
|
4869
|
-
if (error instanceof SystemError) throw error;
|
|
4870
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update general notifications", error);
|
|
4942
|
+
throw wrapKintoneError(error, "Failed to update general notifications");
|
|
4871
4943
|
}
|
|
4872
4944
|
}
|
|
4873
4945
|
async getPerRecordNotifications() {
|
|
@@ -4881,9 +4953,7 @@ var KintoneNotificationConfigurator = class {
|
|
|
4881
4953
|
revision: response.revision
|
|
4882
4954
|
};
|
|
4883
4955
|
} catch (error) {
|
|
4884
|
-
|
|
4885
|
-
if (error instanceof SystemError) throw error;
|
|
4886
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get per-record notifications", error);
|
|
4956
|
+
throw wrapKintoneError(error, "Failed to get per-record notifications");
|
|
4887
4957
|
}
|
|
4888
4958
|
}
|
|
4889
4959
|
async updatePerRecordNotifications(params) {
|
|
@@ -4895,9 +4965,7 @@ var KintoneNotificationConfigurator = class {
|
|
|
4895
4965
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
4896
4966
|
return { revision: (await this.client.app.updatePerRecordNotifications(requestParams)).revision };
|
|
4897
4967
|
} catch (error) {
|
|
4898
|
-
|
|
4899
|
-
if (error instanceof SystemError) throw error;
|
|
4900
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update per-record notifications", error);
|
|
4968
|
+
throw wrapKintoneError(error, "Failed to update per-record notifications");
|
|
4901
4969
|
}
|
|
4902
4970
|
}
|
|
4903
4971
|
async getReminderNotifications() {
|
|
@@ -4913,9 +4981,7 @@ var KintoneNotificationConfigurator = class {
|
|
|
4913
4981
|
revision: response.revision
|
|
4914
4982
|
};
|
|
4915
4983
|
} catch (error) {
|
|
4916
|
-
|
|
4917
|
-
if (error instanceof SystemError) throw error;
|
|
4918
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get reminder notifications", error);
|
|
4984
|
+
throw wrapKintoneError(error, "Failed to get reminder notifications");
|
|
4919
4985
|
}
|
|
4920
4986
|
}
|
|
4921
4987
|
async updateReminderNotifications(params) {
|
|
@@ -4928,9 +4994,7 @@ var KintoneNotificationConfigurator = class {
|
|
|
4928
4994
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
4929
4995
|
return { revision: (await this.client.app.updateReminderNotifications(requestParams)).revision };
|
|
4930
4996
|
} catch (error) {
|
|
4931
|
-
|
|
4932
|
-
if (error instanceof SystemError) throw error;
|
|
4933
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update reminder notifications", error);
|
|
4997
|
+
throw wrapKintoneError(error, "Failed to update reminder notifications");
|
|
4934
4998
|
}
|
|
4935
4999
|
}
|
|
4936
5000
|
};
|
|
@@ -4974,22 +5038,18 @@ var KintonePluginConfigurator = class {
|
|
|
4974
5038
|
revision: response.revision
|
|
4975
5039
|
};
|
|
4976
5040
|
} catch (error) {
|
|
4977
|
-
|
|
4978
|
-
if (error instanceof SystemError) throw error;
|
|
4979
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get plugins", error);
|
|
5041
|
+
throw wrapKintoneError(error, "Failed to get plugins");
|
|
4980
5042
|
}
|
|
4981
5043
|
}
|
|
4982
5044
|
async addPlugins(params) {
|
|
4983
5045
|
try {
|
|
4984
5046
|
return { revision: (await this.client.app.addPlugins({
|
|
4985
5047
|
app: this.appId,
|
|
4986
|
-
ids: params.ids,
|
|
5048
|
+
ids: [...params.ids],
|
|
4987
5049
|
revision: params.revision
|
|
4988
5050
|
})).revision };
|
|
4989
5051
|
} catch (error) {
|
|
4990
|
-
|
|
4991
|
-
if (error instanceof SystemError) throw error;
|
|
4992
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to add plugins", error);
|
|
5052
|
+
throw wrapKintoneError(error, "Failed to add plugins");
|
|
4993
5053
|
}
|
|
4994
5054
|
}
|
|
4995
5055
|
};
|
|
@@ -5122,9 +5182,7 @@ var KintoneProcessManagementConfigurator = class {
|
|
|
5122
5182
|
revision: response.revision
|
|
5123
5183
|
};
|
|
5124
5184
|
} catch (error) {
|
|
5125
|
-
|
|
5126
|
-
if (error instanceof SystemError) throw error;
|
|
5127
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get process management settings", error);
|
|
5185
|
+
throw wrapKintoneError(error, "Failed to get process management settings");
|
|
5128
5186
|
}
|
|
5129
5187
|
}
|
|
5130
5188
|
async updateProcessManagement(params) {
|
|
@@ -5140,9 +5198,7 @@ var KintoneProcessManagementConfigurator = class {
|
|
|
5140
5198
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
5141
5199
|
return { revision: (await this.client.app.updateProcessManagement(requestParams)).revision };
|
|
5142
5200
|
} catch (error) {
|
|
5143
|
-
|
|
5144
|
-
if (error instanceof SystemError) throw error;
|
|
5145
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update process management settings", error);
|
|
5201
|
+
throw wrapKintoneError(error, "Failed to update process management settings");
|
|
5146
5202
|
}
|
|
5147
5203
|
}
|
|
5148
5204
|
};
|
|
@@ -5225,9 +5281,7 @@ var KintoneRecordPermissionConfigurator = class {
|
|
|
5225
5281
|
revision: response.revision
|
|
5226
5282
|
};
|
|
5227
5283
|
} catch (error) {
|
|
5228
|
-
|
|
5229
|
-
if (error instanceof SystemError) throw error;
|
|
5230
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get record ACL", error);
|
|
5284
|
+
throw wrapKintoneError(error, "Failed to get record ACL");
|
|
5231
5285
|
}
|
|
5232
5286
|
}
|
|
5233
5287
|
async updateRecordPermissions(params) {
|
|
@@ -5239,9 +5293,7 @@ var KintoneRecordPermissionConfigurator = class {
|
|
|
5239
5293
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
5240
5294
|
return { revision: (await this.client.app.updateRecordAcl(requestParams)).revision };
|
|
5241
5295
|
} catch (error) {
|
|
5242
|
-
|
|
5243
|
-
if (error instanceof SystemError) throw error;
|
|
5244
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update record ACL", error);
|
|
5296
|
+
throw wrapKintoneError(error, "Failed to update record ACL");
|
|
5245
5297
|
}
|
|
5246
5298
|
}
|
|
5247
5299
|
};
|
|
@@ -5514,9 +5566,7 @@ var KintoneReportConfigurator = class {
|
|
|
5514
5566
|
revision: response.revision
|
|
5515
5567
|
};
|
|
5516
5568
|
} catch (error) {
|
|
5517
|
-
|
|
5518
|
-
if (error instanceof SystemError) throw error;
|
|
5519
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get reports", error);
|
|
5569
|
+
throw wrapKintoneError(error, "Failed to get reports");
|
|
5520
5570
|
}
|
|
5521
5571
|
}
|
|
5522
5572
|
async updateReports(params) {
|
|
@@ -5530,9 +5580,7 @@ var KintoneReportConfigurator = class {
|
|
|
5530
5580
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
5531
5581
|
return { revision: (await this.client.app.updateReports(requestParams)).revision };
|
|
5532
5582
|
} catch (error) {
|
|
5533
|
-
|
|
5534
|
-
if (error instanceof SystemError) throw error;
|
|
5535
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update reports", error);
|
|
5583
|
+
throw wrapKintoneError(error, "Failed to update reports");
|
|
5536
5584
|
}
|
|
5537
5585
|
}
|
|
5538
5586
|
};
|
|
@@ -5582,21 +5630,17 @@ function fromKintoneView(name, raw) {
|
|
|
5582
5630
|
}
|
|
5583
5631
|
return {
|
|
5584
5632
|
type: raw.type,
|
|
5585
|
-
index: (
|
|
5586
|
-
const idx = typeof raw.index === "string" ? Number(raw.index) : raw.index;
|
|
5587
|
-
if (!Number.isFinite(idx)) throw new SystemError(SystemErrorCode.ExternalApiError, `Unexpected non-numeric index from kintone API: ${raw.index}`);
|
|
5588
|
-
return idx;
|
|
5589
|
-
})(),
|
|
5633
|
+
index: parseKintoneIntegerField(raw.index, "index"),
|
|
5590
5634
|
name,
|
|
5591
|
-
...raw.builtinType !== void 0
|
|
5592
|
-
...raw.fields !== void 0
|
|
5593
|
-
...raw.date !== void 0
|
|
5594
|
-
...raw.title !== void 0
|
|
5595
|
-
...raw.html !== void 0
|
|
5596
|
-
...raw.pager !== void 0
|
|
5597
|
-
...device !== void 0
|
|
5598
|
-
...raw.filterCond !== void 0
|
|
5599
|
-
...raw.sort !== void 0
|
|
5635
|
+
...raw.builtinType !== void 0 ? { builtinType: raw.builtinType } : {},
|
|
5636
|
+
...raw.fields !== void 0 ? { fields: raw.fields } : {},
|
|
5637
|
+
...raw.date !== void 0 ? { date: raw.date } : {},
|
|
5638
|
+
...raw.title !== void 0 ? { title: raw.title } : {},
|
|
5639
|
+
...raw.html !== void 0 ? { html: raw.html } : {},
|
|
5640
|
+
...raw.pager !== void 0 ? { pager: raw.pager } : {},
|
|
5641
|
+
...device !== void 0 ? { device } : {},
|
|
5642
|
+
...raw.filterCond !== void 0 ? { filterCond: raw.filterCond } : {},
|
|
5643
|
+
...raw.sort !== void 0 ? { sort: raw.sort } : {}
|
|
5600
5644
|
};
|
|
5601
5645
|
}
|
|
5602
5646
|
function toKintoneView(config) {
|
|
@@ -5636,9 +5680,7 @@ var KintoneViewConfigurator = class {
|
|
|
5636
5680
|
revision: response.revision
|
|
5637
5681
|
};
|
|
5638
5682
|
} catch (error) {
|
|
5639
|
-
|
|
5640
|
-
if (error instanceof SystemError) throw error;
|
|
5641
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to get views", error);
|
|
5683
|
+
throw wrapKintoneError(error, "Failed to get views");
|
|
5642
5684
|
}
|
|
5643
5685
|
}
|
|
5644
5686
|
async updateViews(params) {
|
|
@@ -5652,9 +5694,7 @@ var KintoneViewConfigurator = class {
|
|
|
5652
5694
|
if (params.revision !== void 0) requestParams.revision = params.revision;
|
|
5653
5695
|
return { revision: (await this.client.app.updateViews(requestParams)).revision };
|
|
5654
5696
|
} catch (error) {
|
|
5655
|
-
|
|
5656
|
-
if (error instanceof SystemError) throw error;
|
|
5657
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to update views", error);
|
|
5697
|
+
throw wrapKintoneError(error, "Failed to update views");
|
|
5658
5698
|
}
|
|
5659
5699
|
}
|
|
5660
5700
|
};
|
|
@@ -5770,7 +5810,7 @@ var KintoneSpaceReader = class {
|
|
|
5770
5810
|
this.client = client;
|
|
5771
5811
|
}
|
|
5772
5812
|
async getSpaceApps(spaceId) {
|
|
5773
|
-
if (!spaceId) throw new
|
|
5813
|
+
if (!spaceId) throw new ValidationError(ValidationErrorCode.InvalidInput, "spaceId must not be empty");
|
|
5774
5814
|
try {
|
|
5775
5815
|
const rawApps = (await this.client.space.getSpace({ id: spaceId })).attachedApps;
|
|
5776
5816
|
if (rawApps === void 0) throw new SystemError(SystemErrorCode.ExternalApiError, `Space API response for space ID ${spaceId} does not contain attachedApps. The kintone API response format may have changed.`);
|
|
@@ -5789,9 +5829,7 @@ var KintoneSpaceReader = class {
|
|
|
5789
5829
|
}
|
|
5790
5830
|
return result;
|
|
5791
5831
|
} catch (error) {
|
|
5792
|
-
|
|
5793
|
-
if (error instanceof SystemError) throw error;
|
|
5794
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, `Failed to get space info for space ID: ${spaceId}`, error);
|
|
5832
|
+
throw wrapKintoneError(error, `Failed to get space info for space ID: ${spaceId}`);
|
|
5795
5833
|
}
|
|
5796
5834
|
}
|
|
5797
5835
|
};
|
|
@@ -9217,15 +9255,19 @@ var KintoneFormDumpReader = class {
|
|
|
9217
9255
|
}
|
|
9218
9256
|
async getRawFormData() {
|
|
9219
9257
|
try {
|
|
9220
|
-
const [fields, layout] = await Promise.all([this.client.app.getFormFields({
|
|
9258
|
+
const [fields, layout] = await Promise.all([this.client.app.getFormFields({
|
|
9259
|
+
app: this.appId,
|
|
9260
|
+
preview: true
|
|
9261
|
+
}), this.client.app.getFormLayout({
|
|
9262
|
+
app: this.appId,
|
|
9263
|
+
preview: true
|
|
9264
|
+
})]);
|
|
9221
9265
|
return {
|
|
9222
9266
|
fields,
|
|
9223
9267
|
layout
|
|
9224
9268
|
};
|
|
9225
9269
|
} catch (error) {
|
|
9226
|
-
|
|
9227
|
-
if (error instanceof SystemError) throw error;
|
|
9228
|
-
throw new SystemError(SystemErrorCode.ExternalApiError, "Failed to fetch raw form data for dump", error);
|
|
9270
|
+
throw wrapKintoneError(error, "Failed to fetch raw form data for dump");
|
|
9229
9271
|
}
|
|
9230
9272
|
}
|
|
9231
9273
|
};
|
|
@@ -9233,11 +9275,16 @@ var KintoneFormDumpReader = class {
|
|
|
9233
9275
|
//#endregion
|
|
9234
9276
|
//#region src/core/adapters/local/dumpStorage.ts
|
|
9235
9277
|
var LocalFileDumpStorage = class {
|
|
9236
|
-
constructor(filePrefix) {
|
|
9278
|
+
constructor(filePrefix, baseDir = process.cwd()) {
|
|
9237
9279
|
this.filePrefix = filePrefix;
|
|
9280
|
+
this.baseDir = baseDir;
|
|
9281
|
+
const fieldsPath = resolve(this.baseDir, `${this.filePrefix}fields.json`);
|
|
9282
|
+
if (!isSafePath(fieldsPath, this.baseDir)) throw new ValidationError(ValidationErrorCode.InvalidInput, `Path traversal detected: "${fieldsPath}" escapes base directory "${this.baseDir}"`);
|
|
9283
|
+
const layoutPath = resolve(this.baseDir, `${this.filePrefix}layout.json`);
|
|
9284
|
+
if (!isSafePath(layoutPath, this.baseDir)) throw new ValidationError(ValidationErrorCode.InvalidInput, `Path traversal detected: "${layoutPath}" escapes base directory "${this.baseDir}"`);
|
|
9238
9285
|
}
|
|
9239
9286
|
async saveFields(content) {
|
|
9240
|
-
const filePath = `${this.filePrefix}fields.json
|
|
9287
|
+
const filePath = resolve(this.baseDir, `${this.filePrefix}fields.json`);
|
|
9241
9288
|
try {
|
|
9242
9289
|
await mkdir(dirname(filePath), { recursive: true });
|
|
9243
9290
|
await writeFile(filePath, content, "utf-8");
|
|
@@ -9246,7 +9293,7 @@ var LocalFileDumpStorage = class {
|
|
|
9246
9293
|
}
|
|
9247
9294
|
}
|
|
9248
9295
|
async saveLayout(content) {
|
|
9249
|
-
const filePath = `${this.filePrefix}layout.json
|
|
9296
|
+
const filePath = resolve(this.baseDir, `${this.filePrefix}layout.json`);
|
|
9250
9297
|
try {
|
|
9251
9298
|
await mkdir(dirname(filePath), { recursive: true });
|
|
9252
9299
|
await writeFile(filePath, content, "utf-8");
|
|
@@ -9261,7 +9308,7 @@ var LocalFileDumpStorage = class {
|
|
|
9261
9308
|
function createDumpCliContainer(config) {
|
|
9262
9309
|
return {
|
|
9263
9310
|
formDumpReader: new KintoneFormDumpReader(config.client ?? createKintoneClient(config), config.appId),
|
|
9264
|
-
dumpStorage: new LocalFileDumpStorage(config.filePrefix)
|
|
9311
|
+
dumpStorage: new LocalFileDumpStorage(config.filePrefix, process.cwd())
|
|
9265
9312
|
};
|
|
9266
9313
|
}
|
|
9267
9314
|
|
|
@@ -9337,11 +9384,14 @@ var dump_default = define({
|
|
|
9337
9384
|
function splitSubtableInnerFields(desired, current) {
|
|
9338
9385
|
const newInnerFields = /* @__PURE__ */ new Map();
|
|
9339
9386
|
const existingInnerFields = /* @__PURE__ */ new Map();
|
|
9387
|
+
const deletedInnerFieldCodes = [];
|
|
9340
9388
|
for (const [code, def] of desired.properties.fields) if (current.properties.fields.has(code)) existingInnerFields.set(code, def);
|
|
9341
9389
|
else newInnerFields.set(code, def);
|
|
9390
|
+
for (const code of current.properties.fields.keys()) if (!desired.properties.fields.has(code)) deletedInnerFieldCodes.push(code);
|
|
9342
9391
|
return {
|
|
9343
9392
|
newInnerFields,
|
|
9344
|
-
existingInnerFields
|
|
9393
|
+
existingInnerFields,
|
|
9394
|
+
deletedInnerFieldCodes
|
|
9345
9395
|
};
|
|
9346
9396
|
}
|
|
9347
9397
|
|
|
@@ -9448,6 +9498,11 @@ function validateReferenceTableRelatedApp(field) {
|
|
|
9448
9498
|
if (app.trim().length === 0) return [issue("error", field.code, field.type, "EMPTY_RELATED_APP", `Field "${field.code}" referenceTable.relatedApp.app must be non-empty`)];
|
|
9449
9499
|
return [];
|
|
9450
9500
|
}
|
|
9501
|
+
function validateSubtableHasInnerFields(field) {
|
|
9502
|
+
if (field.type !== "SUBTABLE") return [];
|
|
9503
|
+
if (field.properties.fields.size === 0) return [issue("error", field.code, field.type, "EMPTY_SUBTABLE", `Subtable "${field.code}" must have at least one inner field`)];
|
|
9504
|
+
return [];
|
|
9505
|
+
}
|
|
9451
9506
|
const FIELD_VALIDATORS = [
|
|
9452
9507
|
validateLabelNonEmpty,
|
|
9453
9508
|
validateSelectionOptions,
|
|
@@ -9457,7 +9512,8 @@ const FIELD_VALIDATORS = [
|
|
|
9457
9512
|
validateFileThumbnailSize,
|
|
9458
9513
|
validateReferenceTableSize,
|
|
9459
9514
|
validateLookupStructure,
|
|
9460
|
-
validateReferenceTableRelatedApp
|
|
9515
|
+
validateReferenceTableRelatedApp,
|
|
9516
|
+
validateSubtableHasInnerFields
|
|
9461
9517
|
];
|
|
9462
9518
|
function validateField(field) {
|
|
9463
9519
|
return FIELD_VALIDATORS.flatMap((validator) => validator(field));
|
|
@@ -9506,6 +9562,7 @@ async function executeMigration({ container }) {
|
|
|
9506
9562
|
const deleted = diff.entries.filter((e) => e.type === "deleted");
|
|
9507
9563
|
const fieldsToAdd = [];
|
|
9508
9564
|
const fieldsToUpdate = [];
|
|
9565
|
+
const innerFieldsToDelete = [];
|
|
9509
9566
|
for (const entry of added) {
|
|
9510
9567
|
if (entry.after === void 0) continue;
|
|
9511
9568
|
if (subtableInnerCodes.has(entry.fieldCode)) continue;
|
|
@@ -9517,22 +9574,21 @@ async function executeMigration({ container }) {
|
|
|
9517
9574
|
const after = entry.after;
|
|
9518
9575
|
const before = entry.before;
|
|
9519
9576
|
if (after.type === "SUBTABLE" && before !== void 0 && before.type === "SUBTABLE") {
|
|
9520
|
-
const { newInnerFields, existingInnerFields } = splitSubtableInnerFields(after, before);
|
|
9521
|
-
if (newInnerFields.size > 0)
|
|
9522
|
-
...after,
|
|
9523
|
-
properties: { fields: newInnerFields }
|
|
9524
|
-
});
|
|
9577
|
+
const { newInnerFields, existingInnerFields, deletedInnerFieldCodes } = splitSubtableInnerFields(after, before);
|
|
9578
|
+
if (newInnerFields.size > 0) throw new ValidationError(ValidationErrorCode.InvalidInput, `kintone REST API does not support adding fields to an existing subtable. Use the schema override command instead. Subtable: ${after.code}`);
|
|
9525
9579
|
if (existingInnerFields.size > 0) fieldsToUpdate.push({
|
|
9526
9580
|
...after,
|
|
9527
9581
|
properties: { fields: existingInnerFields }
|
|
9528
9582
|
});
|
|
9529
|
-
|
|
9583
|
+
for (const code of deletedInnerFieldCodes) innerFieldsToDelete.push(code);
|
|
9584
|
+
} else if (before !== void 0 && before.type !== after.type) throw new ValidationError(ValidationErrorCode.InvalidInput, `Field type change detected for "${after.code}" (${before.type} → ${after.type}). Use the schema override command instead.`);
|
|
9585
|
+
else fieldsToUpdate.push(after);
|
|
9530
9586
|
}
|
|
9531
9587
|
if (fieldsToAdd.length > 0) await container.formConfigurator.addFields(fieldsToAdd);
|
|
9532
9588
|
if (fieldsToUpdate.length > 0) await container.formConfigurator.updateFields(fieldsToUpdate);
|
|
9533
|
-
if (deleted.length > 0) {
|
|
9589
|
+
if (deleted.length > 0 || innerFieldsToDelete.length > 0) {
|
|
9534
9590
|
const currentSubtableInnerCodes = collectSubtableInnerFieldCodes(currentFields);
|
|
9535
|
-
const fieldCodes = deleted.filter((e) => !currentSubtableInnerCodes.has(e.fieldCode)).map((e) => e.fieldCode);
|
|
9591
|
+
const fieldCodes = [...deleted.filter((e) => !currentSubtableInnerCodes.has(e.fieldCode)).map((e) => e.fieldCode), ...innerFieldsToDelete];
|
|
9536
9592
|
if (fieldCodes.length > 0) await container.formConfigurator.deleteFields(fieldCodes);
|
|
9537
9593
|
}
|
|
9538
9594
|
if (hasLayoutChanges) await container.formConfigurator.updateLayout(schema.layout);
|
|
@@ -9647,22 +9703,35 @@ async function forceOverrideForm({ container }) {
|
|
|
9647
9703
|
const toAdd = [];
|
|
9648
9704
|
const toUpdate = [];
|
|
9649
9705
|
const toDelete = [];
|
|
9706
|
+
const innerFieldsToDelete = [];
|
|
9650
9707
|
for (const [fieldCode, schemaDef] of schema.fields) {
|
|
9651
9708
|
if (subtableInnerCodes.has(fieldCode)) continue;
|
|
9652
9709
|
if (currentFields.has(fieldCode)) if (schemaDef.type === "SUBTABLE") {
|
|
9653
9710
|
const currentDef = currentFields.get(fieldCode);
|
|
9654
9711
|
if (currentDef !== void 0 && currentDef.type === "SUBTABLE") {
|
|
9655
|
-
const { newInnerFields, existingInnerFields } = splitSubtableInnerFields(schemaDef, currentDef);
|
|
9656
|
-
|
|
9657
|
-
|
|
9658
|
-
|
|
9659
|
-
|
|
9660
|
-
|
|
9661
|
-
|
|
9662
|
-
|
|
9663
|
-
|
|
9712
|
+
const { newInnerFields, existingInnerFields, deletedInnerFieldCodes } = splitSubtableInnerFields(schemaDef, currentDef);
|
|
9713
|
+
const allInnerFieldsRemoved = existingInnerFields.size === 0 && deletedInnerFieldCodes.length > 0;
|
|
9714
|
+
if (newInnerFields.size > 0 || allInnerFieldsRemoved) {
|
|
9715
|
+
toDelete.push(fieldCode);
|
|
9716
|
+
toAdd.push(schemaDef);
|
|
9717
|
+
} else {
|
|
9718
|
+
toUpdate.push({
|
|
9719
|
+
...schemaDef,
|
|
9720
|
+
properties: { fields: existingInnerFields }
|
|
9721
|
+
});
|
|
9722
|
+
for (const code of deletedInnerFieldCodes) innerFieldsToDelete.push(code);
|
|
9723
|
+
}
|
|
9724
|
+
} else {
|
|
9725
|
+
toDelete.push(fieldCode);
|
|
9726
|
+
toAdd.push(schemaDef);
|
|
9727
|
+
}
|
|
9728
|
+
} else {
|
|
9729
|
+
const currentDef = currentFields.get(fieldCode);
|
|
9730
|
+
if (currentDef !== void 0 && currentDef.type !== schemaDef.type) {
|
|
9731
|
+
toDelete.push(fieldCode);
|
|
9732
|
+
toAdd.push(schemaDef);
|
|
9664
9733
|
} else toUpdate.push(schemaDef);
|
|
9665
|
-
}
|
|
9734
|
+
}
|
|
9666
9735
|
else toAdd.push(schemaDef);
|
|
9667
9736
|
}
|
|
9668
9737
|
const currentSubtableInnerCodes = collectSubtableInnerFieldCodes(currentFields);
|
|
@@ -9670,9 +9739,9 @@ async function forceOverrideForm({ container }) {
|
|
|
9670
9739
|
if (currentSubtableInnerCodes.has(fieldCode)) continue;
|
|
9671
9740
|
if (!schema.fields.has(fieldCode)) toDelete.push(fieldCode);
|
|
9672
9741
|
}
|
|
9742
|
+
if (toDelete.length > 0 || innerFieldsToDelete.length > 0) await container.formConfigurator.deleteFields([...toDelete, ...innerFieldsToDelete]);
|
|
9673
9743
|
if (toAdd.length > 0) await container.formConfigurator.addFields(toAdd);
|
|
9674
9744
|
if (toUpdate.length > 0) await container.formConfigurator.updateFields(toUpdate);
|
|
9675
|
-
if (toDelete.length > 0) await container.formConfigurator.deleteFields(toDelete);
|
|
9676
9745
|
await container.formConfigurator.updateLayout(schema.layout);
|
|
9677
9746
|
}
|
|
9678
9747
|
|