kintone-migrator 0.20.0 → 0.20.2

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/README.md CHANGED
@@ -217,22 +217,29 @@ All commands support `--app <name>` and `--all` for [multi-app mode](#multi-app-
217
217
  Initializes a project from a kintone space. Fetches all apps in the specified space, generates a `kintone-migrator.yaml` config file with `files` object format, and captures all domain configurations for each app.
218
218
 
219
219
  ```bash
220
- kintone-migrator init <spaceId>
221
- kintone-migrator init <spaceId> --yes # Skip confirmation prompts
220
+ kintone-migrator init --space-id 1
221
+ kintone-migrator init -s 1 --yes # Skip confirmation prompts
222
+ kintone-migrator init -s 1 --dry-run # Preview without writing files
223
+ kintone-migrator init -s 1 -o ./myproject # Output to a specific directory
222
224
  ```
223
225
 
224
226
  | Option | Description |
225
227
  |---|---|
226
- | `<spaceId>` | kintone space ID (required argument) |
228
+ | `--space-id`, `-s` | kintone space ID (required, must be numeric) |
227
229
  | `--domain`, `-d` | kintone domain |
228
230
  | `--api-token`, `-t` | API token |
229
231
  | `--username`, `-u` | Username |
230
232
  | `--password`, `-p` | Password |
231
233
  | `--guest-space-id`, `-g` | Guest space ID |
232
- | `--output`, `-o` | Output config file path (default: `kintone-migrator.yaml`) |
234
+ | `--output`, `-o` | Output directory |
233
235
  | `--yes`, `-y` | Skip confirmation prompts |
236
+ | `--dry-run`, `-n` | Preview what would be created without writing any files |
234
237
 
235
- The generated config uses the `files` object format with all domain file paths pre-configured. For each app, schema, view, settings, notification, report, action, process, field-acl, app-acl, record-acl, admin-notes, and plugin configurations are captured.
238
+ The generated config uses the `files` object format with all domain file paths pre-configured. For each app, schema, seed, customize, view, settings, notification, report, action, process, field-acl, app-acl, record-acl, admin-notes, and plugin configurations are captured.
239
+
240
+ App names in the generated config are resolved with the following priority: app code > app name > `app-{appId}`. Duplicate names are automatically suffixed (e.g., `-2`, `-3`). File paths follow the `<appName>/<domain>.yaml` convention (e.g., `customer/schema.yaml`).
241
+
242
+ Authentication settings are intentionally omitted from the generated config to avoid committing credentials to version control. Set `KINTONE_API_TOKEN` or `KINTONE_USERNAME` + `KINTONE_PASSWORD` environment variables, or add `auth` to the config file manually.
236
243
 
237
244
  ### `schema` -- Form Schema Management
238
245
 
package/dist/index.mjs CHANGED
@@ -1202,15 +1202,103 @@ var KintoneFormConfigurator = class {
1202
1202
  }
1203
1203
  };
1204
1204
 
1205
+ //#endregion
1206
+ //#region src/core/adapters/kintone/recordConverter.ts
1207
+ const SYSTEM_FIELDS = new Set([
1208
+ "$id",
1209
+ "$revision",
1210
+ "RECORD_NUMBER",
1211
+ "CREATOR",
1212
+ "CREATED_TIME",
1213
+ "MODIFIER",
1214
+ "UPDATED_TIME",
1215
+ "STATUS",
1216
+ "STATUS_ASSIGNEE",
1217
+ "CATEGORY"
1218
+ ]);
1219
+ const SYSTEM_FIELD_TYPES$1 = new Set([
1220
+ "RECORD_NUMBER",
1221
+ "__ID__",
1222
+ "__REVISION__",
1223
+ "CREATOR",
1224
+ "CREATED_TIME",
1225
+ "MODIFIER",
1226
+ "UPDATED_TIME",
1227
+ "STATUS",
1228
+ "STATUS_ASSIGNEE",
1229
+ "CATEGORY"
1230
+ ]);
1231
+ function isSubtableRowArray(value) {
1232
+ if (!Array.isArray(value)) return false;
1233
+ if (value.length === 0) return false;
1234
+ const first = value[0];
1235
+ return typeof first === "object" && first !== null && !("code" in first && Object.keys(first).length === 1);
1236
+ }
1237
+ function toKintoneFieldValue(value) {
1238
+ if (typeof value === "string") return { value };
1239
+ if (Array.isArray(value)) {
1240
+ if (value.length === 0) return { value: [] };
1241
+ const first = value[0];
1242
+ if (typeof first === "string") return { value };
1243
+ if (typeof first === "object" && first !== null && "code" in first && !isSubtableRowArray(value)) return { value };
1244
+ return { value: value.map((row) => {
1245
+ const kintoneRow = {};
1246
+ for (const [k, cellValue] of Object.entries(row)) kintoneRow[k] = { value: cellValue };
1247
+ return { value: kintoneRow };
1248
+ }) };
1249
+ }
1250
+ return { value };
1251
+ }
1252
+ function fromKintoneFieldValue(value) {
1253
+ if (value === null || value === void 0) return "";
1254
+ if (typeof value === "string") return value;
1255
+ if (typeof value === "number") return String(value);
1256
+ if (Array.isArray(value)) {
1257
+ if (value.length === 0) return [];
1258
+ const first = value[0];
1259
+ if (typeof first === "string") return value;
1260
+ if (typeof first === "object" && first !== null) {
1261
+ if ("code" in first && !("value" in first)) return value.filter(hasCode).map((u) => ({ code: u.code }));
1262
+ if ("value" in first) return value.filter(isKintoneSubtableRow).map((row) => {
1263
+ const cells = row.value;
1264
+ const flat = {};
1265
+ for (const [k, cell] of Object.entries(cells)) {
1266
+ if (SYSTEM_FIELDS.has(k)) continue;
1267
+ if (Array.isArray(cell.value)) flat[k] = cell.value.map(String);
1268
+ else flat[k] = cell.value === null || cell.value === void 0 ? "" : String(cell.value);
1269
+ }
1270
+ return flat;
1271
+ });
1272
+ }
1273
+ return value.map(String);
1274
+ }
1275
+ return String(value);
1276
+ }
1277
+ function toKintoneRecord(record) {
1278
+ const kintoneRecord = {};
1279
+ for (const [fieldCode, fieldValue] of Object.entries(record)) {
1280
+ if (SYSTEM_FIELDS.has(fieldCode)) continue;
1281
+ kintoneRecord[fieldCode] = toKintoneFieldValue(fieldValue);
1282
+ }
1283
+ return kintoneRecord;
1284
+ }
1285
+ function fromKintoneRecord(record) {
1286
+ const seedRecord = {};
1287
+ for (const [fieldCode, cell] of Object.entries(record)) {
1288
+ if (SYSTEM_FIELDS.has(fieldCode)) continue;
1289
+ const fieldType = hasOptionalType(cell) ? cell.type : void 0;
1290
+ if (fieldType !== void 0 && SYSTEM_FIELD_TYPES$1.has(fieldType)) continue;
1291
+ seedRecord[fieldCode] = fromKintoneFieldValue(cell.value);
1292
+ }
1293
+ return seedRecord;
1294
+ }
1295
+
1205
1296
  //#endregion
1206
1297
  //#region src/core/adapters/kintone/recordManager.ts
1207
- function toKintoneRecordForResponse(record) {
1298
+ function extractId(record) {
1208
1299
  const $id = record.$id;
1209
1300
  if (!$id || typeof $id.value !== "string") throw new SystemError(SystemErrorCode.ExternalApiError, "Record missing $id field from kintone API response");
1210
- return record;
1211
- }
1212
- function toSdkRecord(record) {
1213
- return record;
1301
+ return $id.value;
1214
1302
  }
1215
1303
  var KintoneRecordManager = class {
1216
1304
  constructor(client, appId) {
@@ -1222,7 +1310,13 @@ var KintoneRecordManager = class {
1222
1310
  return (await this.client.record.getAllRecords({
1223
1311
  app: this.appId,
1224
1312
  ...condition !== void 0 ? { condition } : {}
1225
- })).map((r) => toKintoneRecordForResponse(r));
1313
+ })).map((r) => {
1314
+ const raw = r;
1315
+ return {
1316
+ id: extractId(r),
1317
+ record: fromKintoneRecord(raw)
1318
+ };
1319
+ });
1226
1320
  } catch (error) {
1227
1321
  if (isBusinessRuleError(error)) throw error;
1228
1322
  if (error instanceof SystemError) throw error;
@@ -1234,7 +1328,7 @@ var KintoneRecordManager = class {
1234
1328
  try {
1235
1329
  await this.client.record.addAllRecords({
1236
1330
  app: this.appId,
1237
- records: records.map(toSdkRecord)
1331
+ records: records.map(toKintoneRecord)
1238
1332
  });
1239
1333
  } catch (error) {
1240
1334
  if (isBusinessRuleError(error)) throw error;
@@ -1249,7 +1343,7 @@ var KintoneRecordManager = class {
1249
1343
  app: this.appId,
1250
1344
  records: records.map((r) => ({
1251
1345
  id: Number(r.id),
1252
- record: toSdkRecord(r.record)
1346
+ record: toKintoneRecord(r.record)
1253
1347
  }))
1254
1348
  });
1255
1349
  } catch (error) {
@@ -1265,10 +1359,10 @@ var KintoneRecordManager = class {
1265
1359
  fields: ["$id"]
1266
1360
  });
1267
1361
  if (records.length === 0) return { deletedCount: 0 };
1268
- const validated = records.map((r) => toKintoneRecordForResponse(r));
1362
+ const ids = records.map((r) => ({ id: Number(extractId(r)) }));
1269
1363
  await this.client.record.deleteAllRecords({
1270
1364
  app: this.appId,
1271
- records: validated.map((r) => ({ id: Number(r.$id.value) }))
1365
+ records: ids
1272
1366
  });
1273
1367
  return { deletedCount: records.length };
1274
1368
  } catch (error) {
@@ -5852,99 +5946,6 @@ async function saveReport({ container, input }) {
5852
5946
  await container.reportStorage.update(input.configText);
5853
5947
  }
5854
5948
 
5855
- //#endregion
5856
- //#region src/core/domain/seedData/services/recordConverter.ts
5857
- const SYSTEM_FIELDS = new Set([
5858
- "$id",
5859
- "$revision",
5860
- "RECORD_NUMBER",
5861
- "CREATOR",
5862
- "CREATED_TIME",
5863
- "MODIFIER",
5864
- "UPDATED_TIME",
5865
- "STATUS",
5866
- "STATUS_ASSIGNEE",
5867
- "CATEGORY"
5868
- ]);
5869
- const SYSTEM_FIELD_TYPES$1 = new Set([
5870
- "RECORD_NUMBER",
5871
- "__ID__",
5872
- "__REVISION__",
5873
- "CREATOR",
5874
- "CREATED_TIME",
5875
- "MODIFIER",
5876
- "UPDATED_TIME",
5877
- "STATUS",
5878
- "STATUS_ASSIGNEE",
5879
- "CATEGORY"
5880
- ]);
5881
- function isSubtableRowArray(value) {
5882
- if (!Array.isArray(value)) return false;
5883
- if (value.length === 0) return false;
5884
- const first = value[0];
5885
- return typeof first === "object" && first !== null && !("code" in first && Object.keys(first).length === 1);
5886
- }
5887
- function toKintoneFieldValue(value) {
5888
- if (typeof value === "string") return { value };
5889
- if (Array.isArray(value)) {
5890
- if (value.length === 0) return { value: [] };
5891
- const first = value[0];
5892
- if (typeof first === "string") return { value };
5893
- if (typeof first === "object" && first !== null && "code" in first && !isSubtableRowArray(value)) return { value };
5894
- return { value: value.map((row) => {
5895
- const kintoneRow = {};
5896
- for (const [k, cellValue] of Object.entries(row)) kintoneRow[k] = { value: cellValue };
5897
- return { value: kintoneRow };
5898
- }) };
5899
- }
5900
- return { value };
5901
- }
5902
- function fromKintoneFieldValue(value) {
5903
- if (value === null || value === void 0) return "";
5904
- if (typeof value === "string") return value;
5905
- if (typeof value === "number") return String(value);
5906
- if (Array.isArray(value)) {
5907
- if (value.length === 0) return [];
5908
- const first = value[0];
5909
- if (typeof first === "string") return value;
5910
- if (typeof first === "object" && first !== null) {
5911
- if ("code" in first && !("value" in first)) return value.filter(hasCode).map((u) => ({ code: u.code }));
5912
- if ("value" in first) return value.filter(isKintoneSubtableRow).map((row) => {
5913
- const cells = row.value;
5914
- const flat = {};
5915
- for (const [k, cell] of Object.entries(cells)) {
5916
- if (SYSTEM_FIELDS.has(k)) continue;
5917
- if (Array.isArray(cell.value)) flat[k] = cell.value.map(String);
5918
- else flat[k] = cell.value === null || cell.value === void 0 ? "" : String(cell.value);
5919
- }
5920
- return flat;
5921
- });
5922
- }
5923
- return value.map(String);
5924
- }
5925
- return String(value);
5926
- }
5927
- const RecordConverter = {
5928
- toKintoneRecord: (record) => {
5929
- const kintoneRecord = {};
5930
- for (const [fieldCode, fieldValue] of Object.entries(record)) {
5931
- if (SYSTEM_FIELDS.has(fieldCode)) continue;
5932
- kintoneRecord[fieldCode] = toKintoneFieldValue(fieldValue);
5933
- }
5934
- return kintoneRecord;
5935
- },
5936
- fromKintoneRecord: (record) => {
5937
- const seedRecord = {};
5938
- for (const [fieldCode, cell] of Object.entries(record)) {
5939
- if (SYSTEM_FIELDS.has(fieldCode)) continue;
5940
- const fieldType = hasOptionalType(cell) ? cell.type : void 0;
5941
- if (fieldType !== void 0 && SYSTEM_FIELD_TYPES$1.has(fieldType)) continue;
5942
- seedRecord[fieldCode] = fromKintoneFieldValue(cell.value);
5943
- }
5944
- return seedRecord;
5945
- }
5946
- };
5947
-
5948
5949
  //#endregion
5949
5950
  //#region src/core/domain/seedData/services/seedSerializer.ts
5950
5951
  const SeedSerializer = { serialize: (seedData) => {
@@ -5974,7 +5975,7 @@ const UpsertKey = { create: (key) => {
5974
5975
  //#region src/core/application/seedData/captureSeed.ts
5975
5976
  async function captureSeed({ container, input }) {
5976
5977
  const key = input.keyField ? UpsertKey.create(input.keyField) : null;
5977
- const records = (await container.recordManager.getAllRecords()).map(RecordConverter.fromKintoneRecord);
5978
+ const records = (await container.recordManager.getAllRecords()).map(({ record }) => record);
5978
5979
  const seedText = SeedSerializer.serialize({
5979
5980
  key,
5980
5981
  records
@@ -6290,7 +6291,7 @@ var init_default = define({
6290
6291
  const auth = resolveAuth(apiToken, username, password);
6291
6292
  const guestSpaceId = values["guest-space-id"] ?? process.env.KINTONE_GUEST_SPACE_ID;
6292
6293
  const output = values.output;
6293
- const configPath = output ? join(output, DEFAULT_CONFIG_PATH) : DEFAULT_CONFIG_PATH;
6294
+ const configPath = DEFAULT_CONFIG_PATH;
6294
6295
  const skipConfirm = values.yes ?? false;
6295
6296
  const dryRun = values["dry-run"] ?? false;
6296
6297
  const { spaceReader, projectConfigStorage } = createInitCliContainer({
@@ -9392,13 +9393,11 @@ function recordsEqual(seed, existing, keyField) {
9392
9393
  const UpsertPlanner = { plan: (key, seedRecords, existingRecords) => {
9393
9394
  const keyField = key;
9394
9395
  const existingMap = /* @__PURE__ */ new Map();
9395
- for (const kintoneRecord of existingRecords) {
9396
- const id = kintoneRecord.$id.value;
9397
- const converted = RecordConverter.fromKintoneRecord(kintoneRecord);
9398
- const keyValue = converted[keyField];
9396
+ for (const { id, record } of existingRecords) {
9397
+ const keyValue = record[keyField];
9399
9398
  if (typeof keyValue === "string") existingMap.set(keyValue, {
9400
9399
  id,
9401
- record: converted
9400
+ record
9402
9401
  });
9403
9402
  }
9404
9403
  const toAdd = [];
@@ -9430,8 +9429,7 @@ async function upsertSeed({ container, input }) {
9430
9429
  const seedData = SeedParser.parse(result.content);
9431
9430
  if (input.clean) {
9432
9431
  const { deletedCount } = await container.recordManager.deleteAllRecords();
9433
- const kintoneRecords = seedData.records.map(RecordConverter.toKintoneRecord);
9434
- if (kintoneRecords.length > 0) await container.recordManager.addRecords(kintoneRecords);
9432
+ if (seedData.records.length > 0) await container.recordManager.addRecords(seedData.records);
9435
9433
  return {
9436
9434
  added: seedData.records.length,
9437
9435
  updated: 0,
@@ -9441,8 +9439,7 @@ async function upsertSeed({ container, input }) {
9441
9439
  };
9442
9440
  }
9443
9441
  if (seedData.key === null) {
9444
- const kintoneRecords = seedData.records.map(RecordConverter.toKintoneRecord);
9445
- if (kintoneRecords.length > 0) await container.recordManager.addRecords(kintoneRecords);
9442
+ if (seedData.records.length > 0) await container.recordManager.addRecords(seedData.records);
9446
9443
  return {
9447
9444
  added: seedData.records.length,
9448
9445
  updated: 0,
@@ -9453,17 +9450,8 @@ async function upsertSeed({ container, input }) {
9453
9450
  }
9454
9451
  const existingRecords = await container.recordManager.getAllRecords();
9455
9452
  const plan = UpsertPlanner.plan(seedData.key, seedData.records, existingRecords);
9456
- if (plan.toAdd.length > 0) {
9457
- const kintoneRecords = plan.toAdd.map(RecordConverter.toKintoneRecord);
9458
- await container.recordManager.addRecords(kintoneRecords);
9459
- }
9460
- if (plan.toUpdate.length > 0) {
9461
- const kintoneUpdates = plan.toUpdate.map((item) => ({
9462
- id: item.id,
9463
- record: RecordConverter.toKintoneRecord(item.record)
9464
- }));
9465
- await container.recordManager.updateRecords(kintoneUpdates);
9466
- }
9453
+ if (plan.toAdd.length > 0) await container.recordManager.addRecords(plan.toAdd);
9454
+ if (plan.toUpdate.length > 0) await container.recordManager.updateRecords(plan.toUpdate);
9467
9455
  return {
9468
9456
  added: plan.toAdd.length,
9469
9457
  updated: plan.toUpdate.length,