@interactive-inc/docs-client 0.0.8 → 0.0.10

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/build/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // lib/models.ts
2
- import { z } from "zod/v4";
2
+ import { z } from "zod";
3
3
  var zDocFilePath = z.object({
4
4
  name: z.string(),
5
5
  path: z.string(),
@@ -150,6 +150,11 @@ var zDocFileMdFrontMatter = z.record(
150
150
  ])
151
151
  );
152
152
  var zDocSchema = z.record(z.string(), zDocSchemaField);
153
+ var zDocSchemaFieldMinimal = z.object({
154
+ type: zDocSchemaFieldType,
155
+ required: z.boolean()
156
+ });
157
+ var zDocSchemaMinimal = z.record(z.string(), zDocSchemaFieldMinimal);
153
158
  var zDocFileIndexFrontMatter = z.object({
154
159
  type: z.literal("index-frontmatter"),
155
160
  icon: z.string(),
@@ -829,16 +834,16 @@ var DocSchemaFieldNumberSingleValue = class {
829
834
  }
830
835
  };
831
836
 
832
- // lib/values/doc-schema-field-relation-multiple-value.ts
833
- var DocSchemaFieldRelationMultipleValue = class {
837
+ // lib/values/doc-schema-field-text-multiple-value.ts
838
+ var DocSchemaFieldTextMultipleValue = class {
834
839
  constructor(key, value) {
835
840
  this.key = key;
836
841
  this.value = value;
842
+ Object.freeze(this);
837
843
  Object.freeze(this.value);
838
- if (this.value.default && Array.isArray(this.value.default)) {
844
+ if (this.value.default) {
839
845
  Object.freeze(this.value.default);
840
846
  }
841
- Object.freeze(this);
842
847
  }
843
848
  get type() {
844
849
  return this.value.type;
@@ -855,25 +860,22 @@ var DocSchemaFieldRelationMultipleValue = class {
855
860
  get default() {
856
861
  return this.value.default;
857
862
  }
858
- get path() {
859
- return this.value.path;
860
- }
861
863
  isArray = true;
862
864
  isSingle = false;
863
- isRelation = true;
865
+ isText = true;
866
+ toJson() {
867
+ return { ...this.value };
868
+ }
864
869
  /**
865
- * 値を検証する
870
+ * 値の型を検証する
866
871
  */
867
872
  validate(value) {
868
873
  return Array.isArray(value) && value.every((v) => typeof v === "string");
869
874
  }
870
- toJson() {
871
- return { ...this.value };
872
- }
873
875
  };
874
876
 
875
- // lib/values/doc-schema-field-relation-single-value.ts
876
- var DocSchemaFieldRelationSingleValue = class {
877
+ // lib/values/doc-schema-field-text-single-value.ts
878
+ var DocSchemaFieldTextSingleValue = class {
877
879
  constructor(key, value) {
878
880
  this.key = key;
879
881
  this.value = value;
@@ -894,12 +896,9 @@ var DocSchemaFieldRelationSingleValue = class {
894
896
  get default() {
895
897
  return this.value.default;
896
898
  }
897
- get path() {
898
- return this.value.path;
899
- }
900
899
  isArray = false;
901
900
  isSingle = true;
902
- isRelation = true;
901
+ isText = true;
903
902
  toJson() {
904
903
  return { ...this.value };
905
904
  }
@@ -911,17 +910,142 @@ var DocSchemaFieldRelationSingleValue = class {
911
910
  }
912
911
  };
913
912
 
914
- // lib/values/doc-schema-field-select-number-multiple-value.ts
915
- var DocSchemaFieldSelectNumberMultipleValue = class {
913
+ // lib/values/doc-schema-builder.ts
914
+ import { z as z2 } from "zod";
915
+ var DocSchemaBuilder = class {
916
+ /**
917
+ * 動的にZodスキーマを生成
918
+ */
919
+ createDynamicSchema(value) {
920
+ if (typeof value !== "object" || value === null) {
921
+ throw new Error("Schema must be an object");
922
+ }
923
+ const schemaObj = value;
924
+ const zodFields = {};
925
+ for (const [key, field] of Object.entries(schemaObj)) {
926
+ if (typeof field !== "object" || field === null) {
927
+ throw new Error(`Field "${key}" must be an object`);
928
+ }
929
+ const fieldObj = field;
930
+ if (!("type" in fieldObj) || !("required" in fieldObj)) {
931
+ throw new Error(
932
+ `Field "${key}" must have "type" and "required" properties`
933
+ );
934
+ }
935
+ try {
936
+ zDocSchemaFieldMinimal.parse(fieldObj);
937
+ } catch (error) {
938
+ if (error instanceof Error) {
939
+ throw new Error(`Field "${key}": ${error.message}`);
940
+ }
941
+ throw error;
942
+ }
943
+ zodFields[key] = this.createFieldSchema(fieldObj.type);
944
+ }
945
+ return z2.object(zodFields);
946
+ }
947
+ /**
948
+ * フィールドタイプに応じたZodスキーマを生成
949
+ */
950
+ createFieldSchema(type) {
951
+ const baseSchema = {
952
+ type: z2.literal(type),
953
+ required: z2.boolean(),
954
+ title: z2.string().nullable().optional(),
955
+ description: z2.string().nullable().optional()
956
+ };
957
+ if (type === "text") {
958
+ return z2.object({
959
+ ...baseSchema,
960
+ default: z2.string().nullable().default("")
961
+ });
962
+ }
963
+ if (type === "number") {
964
+ return z2.object({
965
+ ...baseSchema,
966
+ default: z2.number().nullable().default(0)
967
+ });
968
+ }
969
+ if (type === "boolean") {
970
+ return z2.object({
971
+ ...baseSchema,
972
+ default: z2.boolean().nullable().default(false)
973
+ });
974
+ }
975
+ if (type === "multi-text") {
976
+ return z2.object({
977
+ ...baseSchema,
978
+ default: z2.array(z2.string()).default([])
979
+ });
980
+ }
981
+ if (type === "multi-number") {
982
+ return z2.object({
983
+ ...baseSchema,
984
+ default: z2.array(z2.number()).default([])
985
+ });
986
+ }
987
+ if (type === "multi-boolean") {
988
+ return z2.object({
989
+ ...baseSchema,
990
+ default: z2.array(z2.boolean()).default([])
991
+ });
992
+ }
993
+ if (type === "relation") {
994
+ return z2.object({
995
+ ...baseSchema,
996
+ path: z2.string().min(1),
997
+ default: z2.string().nullable().default(null)
998
+ });
999
+ }
1000
+ if (type === "multi-relation") {
1001
+ return z2.object({
1002
+ ...baseSchema,
1003
+ path: z2.string().min(1),
1004
+ default: z2.array(z2.string()).nullable().default(null)
1005
+ });
1006
+ }
1007
+ if (type === "select-text") {
1008
+ return z2.object({
1009
+ ...baseSchema,
1010
+ options: z2.array(z2.string()),
1011
+ default: z2.string().nullable().default(null)
1012
+ });
1013
+ }
1014
+ if (type === "select-number") {
1015
+ return z2.object({
1016
+ ...baseSchema,
1017
+ options: z2.array(z2.number()),
1018
+ default: z2.number().nullable().default(null)
1019
+ });
1020
+ }
1021
+ if (type === "multi-select-text") {
1022
+ return z2.object({
1023
+ ...baseSchema,
1024
+ options: z2.array(z2.string()),
1025
+ default: z2.array(z2.string()).nullable().default(null)
1026
+ });
1027
+ }
1028
+ if (type === "multi-select-number") {
1029
+ return z2.object({
1030
+ ...baseSchema,
1031
+ options: z2.array(z2.number()),
1032
+ default: z2.array(z2.number()).nullable().default(null)
1033
+ });
1034
+ }
1035
+ throw new Error(`Unknown field type: ${type}`);
1036
+ }
1037
+ };
1038
+
1039
+ // lib/values/doc-schema-field-relation-multiple-value.ts
1040
+ var DocSchemaFieldRelationMultipleValue = class {
916
1041
  constructor(key, value) {
917
1042
  this.key = key;
918
1043
  this.value = value;
919
- Object.freeze(this);
920
1044
  Object.freeze(this.value);
921
- Object.freeze(this.value.options);
922
1045
  if (this.value.default && Array.isArray(this.value.default)) {
923
1046
  Object.freeze(this.value.default);
924
1047
  }
1048
+ Object.freeze(this);
925
1049
  }
926
1050
  get type() {
927
1051
  return this.value.type;
@@ -938,32 +1062,29 @@ var DocSchemaFieldSelectNumberMultipleValue = class {
938
1062
  get default() {
939
1063
  return this.value.default;
940
1064
  }
941
- get options() {
942
- return this.value.options;
1065
+ get path() {
1066
+ return this.value.path;
943
1067
  }
944
1068
  isArray = true;
945
1069
  isSingle = false;
946
- isSelect = true;
947
- isNumberSelect = true;
1070
+ isRelation = true;
948
1071
  /**
949
1072
  * 値を検証する
950
1073
  */
951
1074
  validate(value) {
952
- return Array.isArray(value) && value.every((v) => typeof v === "number");
1075
+ return Array.isArray(value) && value.every((v) => typeof v === "string");
953
1076
  }
954
1077
  toJson() {
955
1078
  return { ...this.value };
956
1079
  }
957
1080
  };
958
1081
 
959
- // lib/values/doc-schema-field-select-number-single-value.ts
960
- var DocSchemaFieldSelectNumberSingleValue = class {
1082
+ // lib/values/doc-schema-field-relation-single-value.ts
1083
+ var DocSchemaFieldRelationSingleValue = class {
961
1084
  constructor(key, value) {
962
1085
  this.key = key;
963
1086
  this.value = value;
964
1087
  Object.freeze(this);
965
- Object.freeze(this.value);
966
- Object.freeze(this.value.options);
967
1088
  }
968
1089
  get type() {
969
1090
  return this.value.type;
@@ -980,26 +1101,25 @@ var DocSchemaFieldSelectNumberSingleValue = class {
980
1101
  get default() {
981
1102
  return this.value.default;
982
1103
  }
983
- get options() {
984
- return this.value.options;
1104
+ get path() {
1105
+ return this.value.path;
985
1106
  }
986
1107
  isArray = false;
987
1108
  isSingle = true;
988
- isSelect = true;
989
- isNumberSelect = true;
1109
+ isRelation = true;
1110
+ toJson() {
1111
+ return { ...this.value };
1112
+ }
990
1113
  /**
991
- * 値を検証する
1114
+ * 値の型を検証する
992
1115
  */
993
1116
  validate(value) {
994
- return typeof value === "number";
995
- }
996
- toJson() {
997
- return { ...this.value };
1117
+ return typeof value === "string";
998
1118
  }
999
1119
  };
1000
1120
 
1001
- // lib/values/doc-schema-field-select-text-multiple-value.ts
1002
- var DocSchemaFieldSelectTextMultipleValue = class {
1121
+ // lib/values/doc-schema-field-select-number-multiple-value.ts
1122
+ var DocSchemaFieldSelectNumberMultipleValue = class {
1003
1123
  constructor(key, value) {
1004
1124
  this.key = key;
1005
1125
  this.value = value;
@@ -1031,28 +1151,26 @@ var DocSchemaFieldSelectTextMultipleValue = class {
1031
1151
  isArray = true;
1032
1152
  isSingle = false;
1033
1153
  isSelect = true;
1034
- isTextSelect = true;
1154
+ isNumberSelect = true;
1035
1155
  /**
1036
1156
  * 値を検証する
1037
1157
  */
1038
1158
  validate(value) {
1039
- return Array.isArray(value) && value.every((v) => typeof v === "string");
1159
+ return Array.isArray(value) && value.every((v) => typeof v === "number");
1040
1160
  }
1041
1161
  toJson() {
1042
1162
  return { ...this.value };
1043
1163
  }
1044
1164
  };
1045
1165
 
1046
- // lib/values/doc-schema-field-select-text-single-value.ts
1047
- var DocSchemaFieldSelectTextSingleValue = class {
1166
+ // lib/values/doc-schema-field-select-number-single-value.ts
1167
+ var DocSchemaFieldSelectNumberSingleValue = class {
1048
1168
  constructor(key, value) {
1049
1169
  this.key = key;
1050
1170
  this.value = value;
1051
- if (this.value.options) {
1052
- Object.freeze(this.value.options);
1053
- }
1054
- Object.freeze(this.value);
1055
1171
  Object.freeze(this);
1172
+ Object.freeze(this.value);
1173
+ Object.freeze(this.value.options);
1056
1174
  }
1057
1175
  get type() {
1058
1176
  return this.value.type;
@@ -1075,26 +1193,27 @@ var DocSchemaFieldSelectTextSingleValue = class {
1075
1193
  isArray = false;
1076
1194
  isSingle = true;
1077
1195
  isSelect = true;
1078
- isTextSelect = true;
1079
- toJson() {
1080
- return { ...this.value };
1081
- }
1196
+ isNumberSelect = true;
1082
1197
  /**
1083
- * 値の型を検証する
1198
+ * 値を検証する
1084
1199
  */
1085
1200
  validate(value) {
1086
- return typeof value === "string";
1201
+ return typeof value === "number";
1202
+ }
1203
+ toJson() {
1204
+ return { ...this.value };
1087
1205
  }
1088
1206
  };
1089
1207
 
1090
- // lib/values/doc-schema-field-text-multiple-value.ts
1091
- var DocSchemaFieldTextMultipleValue = class {
1208
+ // lib/values/doc-schema-field-select-text-multiple-value.ts
1209
+ var DocSchemaFieldSelectTextMultipleValue = class {
1092
1210
  constructor(key, value) {
1093
1211
  this.key = key;
1094
1212
  this.value = value;
1095
1213
  Object.freeze(this);
1096
1214
  Object.freeze(this.value);
1097
- if (this.value.default) {
1215
+ Object.freeze(this.value.options);
1216
+ if (this.value.default && Array.isArray(this.value.default)) {
1098
1217
  Object.freeze(this.value.default);
1099
1218
  }
1100
1219
  }
@@ -1113,25 +1232,33 @@ var DocSchemaFieldTextMultipleValue = class {
1113
1232
  get default() {
1114
1233
  return this.value.default;
1115
1234
  }
1235
+ get options() {
1236
+ return this.value.options;
1237
+ }
1116
1238
  isArray = true;
1117
1239
  isSingle = false;
1118
- isText = true;
1119
- toJson() {
1120
- return { ...this.value };
1121
- }
1240
+ isSelect = true;
1241
+ isTextSelect = true;
1122
1242
  /**
1123
- * 値の型を検証する
1243
+ * 値を検証する
1124
1244
  */
1125
1245
  validate(value) {
1126
1246
  return Array.isArray(value) && value.every((v) => typeof v === "string");
1127
1247
  }
1248
+ toJson() {
1249
+ return { ...this.value };
1250
+ }
1128
1251
  };
1129
1252
 
1130
- // lib/values/doc-schema-field-text-single-value.ts
1131
- var DocSchemaFieldTextSingleValue = class {
1253
+ // lib/values/doc-schema-field-select-text-single-value.ts
1254
+ var DocSchemaFieldSelectTextSingleValue = class {
1132
1255
  constructor(key, value) {
1133
1256
  this.key = key;
1134
1257
  this.value = value;
1258
+ if (this.value.options) {
1259
+ Object.freeze(this.value.options);
1260
+ }
1261
+ Object.freeze(this.value);
1135
1262
  Object.freeze(this);
1136
1263
  }
1137
1264
  get type() {
@@ -1149,9 +1276,13 @@ var DocSchemaFieldTextSingleValue = class {
1149
1276
  get default() {
1150
1277
  return this.value.default;
1151
1278
  }
1279
+ get options() {
1280
+ return this.value.options;
1281
+ }
1152
1282
  isArray = false;
1153
1283
  isSingle = true;
1154
- isText = true;
1284
+ isSelect = true;
1285
+ isTextSelect = true;
1155
1286
  toJson() {
1156
1287
  return { ...this.value };
1157
1288
  }
@@ -1348,9 +1479,9 @@ var DocSchemaFieldFactory = class {
1348
1479
  const baseField = {
1349
1480
  type: normalizedType,
1350
1481
  required: fieldRequired ?? false,
1351
- title: fieldTitle,
1352
- description: fieldDescription,
1353
- default: fieldDefault
1482
+ title: fieldTitle ?? null,
1483
+ description: fieldDescription ?? null,
1484
+ default: fieldDefault ?? null
1354
1485
  };
1355
1486
  return {
1356
1487
  ...baseField,
@@ -1364,9 +1495,9 @@ var DocSchemaFieldFactory = class {
1364
1495
  return {
1365
1496
  type: "text",
1366
1497
  required: false,
1367
- title: "",
1368
- description: "",
1369
- default: ""
1498
+ title: null,
1499
+ description: null,
1500
+ default: null
1370
1501
  };
1371
1502
  }
1372
1503
  /**
@@ -1402,101 +1533,367 @@ var DocSchemaFieldFactory = class {
1402
1533
  }
1403
1534
  };
1404
1535
 
1405
- // lib/values/doc-front-matter-md-value.ts
1406
- var DocFrontMatterMdValue = class _DocFrontMatterMdValue {
1407
- constructor(value) {
1408
- this.value = value;
1409
- zDocFileMdFrontMatter.parse(value);
1536
+ // lib/values/doc-schema-value.ts
1537
+ var DocSchemaValue = class _DocSchemaValue {
1538
+ value;
1539
+ zodSchema;
1540
+ constructor(value, zodSchema, schemaBuilder) {
1541
+ this.zodSchema = zodSchema || null;
1542
+ const builder = schemaBuilder || new DocSchemaBuilder();
1543
+ if (this.zodSchema) {
1544
+ const parsed = this.zodSchema.parse(value);
1545
+ this.value = _DocSchemaValue.normalizeSchema(parsed);
1546
+ } else {
1547
+ const dynamicSchema = builder.createDynamicSchema(value);
1548
+ const parsed = dynamicSchema.parse(value);
1549
+ this.value = _DocSchemaValue.normalizeSchema(parsed);
1550
+ }
1551
+ zDocSchema.parse(this.value);
1552
+ Object.freeze(this.value);
1410
1553
  Object.freeze(this);
1411
1554
  }
1412
- toJson() {
1413
- return this.value;
1414
- }
1415
- toYaml() {
1416
- return stringify2(this.value).trim();
1417
- }
1418
1555
  /**
1419
- * Markdownテキストから生成
1556
+ * フィールド名の一覧を取得
1557
+ * Zodスキーマを使用している場合は自動的に型安全
1420
1558
  */
1421
- static from(markdownText) {
1422
- const openMarkdown = new DocContentMdValue(markdownText);
1423
- const rawData = openMarkdown.frontMatter.data || {};
1424
- const data = zDocFileMdFrontMatter.parse(rawData);
1425
- return new _DocFrontMatterMdValue(data);
1559
+ get fieldNames() {
1560
+ return Object.keys(this.value);
1561
+ }
1562
+ field(name) {
1563
+ const fieldDef = this.value[name];
1564
+ if (fieldDef === void 0) {
1565
+ return null;
1566
+ }
1567
+ const factory = new DocSchemaFieldFactory();
1568
+ return factory.fromUnknown(name, fieldDef);
1426
1569
  }
1427
1570
  /**
1428
- * 空のFrontMatterを生成
1571
+ * すべてのフィールドを取得
1572
+ * 注意: 型安全性のため、個別にfield()メソッドを使用することを推奨
1429
1573
  */
1430
- static empty() {
1431
- return new _DocFrontMatterMdValue({});
1574
+ get fields() {
1575
+ const factory = new DocSchemaFieldFactory();
1576
+ return this.fieldNames.map((name) => {
1577
+ const fieldValue = this.value[name];
1578
+ if (!fieldValue) {
1579
+ throw new Error(`Field ${name} not found in schema`);
1580
+ }
1581
+ return factory.fromUnknown(name, fieldValue);
1582
+ });
1583
+ }
1584
+ get relationFields() {
1585
+ return this.fields.filter((t) => {
1586
+ return t instanceof DocSchemaFieldRelationSingleValue || t instanceof DocSchemaFieldRelationMultipleValue;
1587
+ });
1432
1588
  }
1433
1589
  /**
1434
- * データから直接生成
1590
+ * 空のスキーマエンティティを作成
1435
1591
  */
1436
- static fromData(data) {
1437
- const validatedData = zDocFileMdFrontMatter.parse(data);
1438
- return new _DocFrontMatterMdValue(validatedData);
1592
+ static empty(zodSchema, dynamicBuilder) {
1593
+ return new _DocSchemaValue({}, zodSchema, dynamicBuilder);
1439
1594
  }
1440
1595
  /**
1441
- * スキーマフィールドに基づいてデフォルト値を生成する
1596
+ * スキーマからリレーションパスを抽出
1442
1597
  */
1443
- static generateDefaultValueFromSchemaField(fieldDef) {
1444
- if (fieldDef.type === "text") {
1445
- return fieldDef.default ?? "";
1446
- }
1447
- if (fieldDef.type === "boolean") {
1448
- return fieldDef.default ?? false;
1449
- }
1450
- if (fieldDef.type === "number") {
1451
- return fieldDef.default ?? 0;
1452
- }
1453
- if (fieldDef.type === "multi-text" || fieldDef.type === "multi-number" || fieldDef.type === "multi-boolean" || fieldDef.type === "multi-relation") {
1454
- return fieldDef.default ?? [];
1455
- }
1456
- if (fieldDef.type === "relation") {
1457
- return fieldDef.default ?? null;
1598
+ extractRelationPaths() {
1599
+ if (!this.value) return /* @__PURE__ */ new Set();
1600
+ const uniqueRelationPaths = /* @__PURE__ */ new Set();
1601
+ for (const field of Object.values(this.value)) {
1602
+ if (this.isRelationField(field) && "path" in field) {
1603
+ uniqueRelationPaths.add(field.path);
1604
+ }
1458
1605
  }
1459
- return null;
1606
+ return uniqueRelationPaths;
1460
1607
  }
1461
1608
  /**
1462
- * 単一のプロパティを更新した新しいインスタンスを返す
1463
- * スキーマに基づいて型を検証し、スキーマのフィールドのみを含む新しいインスタンスを生成
1609
+ * フィールドがリレーション型かどうかを判定
1464
1610
  */
1465
- withProperty(key, value, schema) {
1466
- const currentData = this.toJson();
1467
- const fieldValue = schema.field(key);
1468
- if (fieldValue === null) {
1469
- return this;
1611
+ isRelationField(field) {
1612
+ return field.type === "relation" || field.type === "multi-relation";
1613
+ }
1614
+ /**
1615
+ * リレーションオプションを作成
1616
+ */
1617
+ createRelationOption(filePath, title) {
1618
+ const fileName = this.extractFileName(filePath);
1619
+ return zDocRelationFile.parse({
1620
+ name: fileName,
1621
+ label: title || fileName,
1622
+ value: fileName,
1623
+ path: filePath
1624
+ });
1625
+ }
1626
+ /**
1627
+ * ファイルをスキップすべきかどうかを判定
1628
+ */
1629
+ shouldSkipFile(filePath) {
1630
+ return filePath.endsWith("index.md") || filePath.endsWith("README.md");
1631
+ }
1632
+ /**
1633
+ * 値を指定された型に変換する(デフォルト値フォールバック付き)
1634
+ */
1635
+ convertValue(value, targetType, defaultValue) {
1636
+ if (this.isNullOrUndefined(value)) {
1637
+ return defaultValue;
1470
1638
  }
1471
- if (!this.validateFieldValue(key, value, fieldValue)) {
1472
- return this;
1639
+ try {
1640
+ return this.convertByType(value, targetType, defaultValue);
1641
+ } catch {
1642
+ return defaultValue;
1643
+ }
1644
+ }
1645
+ /**
1646
+ * 型別の変換処理
1647
+ */
1648
+ convertByType(value, targetType, defaultValue) {
1649
+ if (targetType === "text") {
1650
+ return this.convertToString(value, defaultValue);
1651
+ }
1652
+ if (targetType === "number") {
1653
+ return this.convertToNumber(value, defaultValue);
1654
+ }
1655
+ if (targetType === "boolean") {
1656
+ return this.convertToBoolean(value, defaultValue);
1657
+ }
1658
+ if (targetType === "multi-text" || targetType === "array") {
1659
+ return this.convertToArray(value, defaultValue);
1660
+ }
1661
+ if (targetType === "relation") {
1662
+ return this.convertToRelation(value, defaultValue);
1663
+ }
1664
+ if (targetType === "multi-relation") {
1665
+ return this.convertToMultiRelation(value, defaultValue);
1666
+ }
1667
+ return defaultValue;
1668
+ }
1669
+ /**
1670
+ * 文字列型への変換
1671
+ */
1672
+ convertToString(value, defaultValue) {
1673
+ if (typeof value === "string") return value;
1674
+ if (typeof value === "number") return String(value);
1675
+ if (typeof value === "boolean") return String(value);
1676
+ return defaultValue;
1677
+ }
1678
+ /**
1679
+ * 数値型への変換
1680
+ */
1681
+ convertToNumber(value, defaultValue) {
1682
+ if (typeof value === "number") return value;
1683
+ if (typeof value === "string") {
1684
+ const parsed = Number(value);
1685
+ return Number.isNaN(parsed) ? defaultValue : parsed;
1686
+ }
1687
+ if (typeof value === "boolean") return value ? 1 : 0;
1688
+ return defaultValue;
1689
+ }
1690
+ /**
1691
+ * ブール型への変換
1692
+ */
1693
+ convertToBoolean(value, defaultValue) {
1694
+ if (typeof value === "boolean") return value;
1695
+ if (typeof value === "string") {
1696
+ const lower = value.toLowerCase();
1697
+ if (lower === "true" || lower === "1") return true;
1698
+ if (lower === "false" || lower === "0") return false;
1699
+ return defaultValue;
1700
+ }
1701
+ if (typeof value === "number") return value !== 0;
1702
+ return defaultValue;
1703
+ }
1704
+ /**
1705
+ * 配列型への変換
1706
+ */
1707
+ convertToArray(value, defaultValue) {
1708
+ if (Array.isArray(value)) return value;
1709
+ if (typeof value === "string") {
1710
+ return value.split(",").map((item) => item.trim());
1711
+ }
1712
+ return defaultValue;
1713
+ }
1714
+ /**
1715
+ * リレーション型への変換
1716
+ */
1717
+ convertToRelation(value, defaultValue) {
1718
+ if (typeof value === "string") {
1719
+ return this.extractFileNameFromPath(value);
1720
+ }
1721
+ return defaultValue;
1722
+ }
1723
+ /**
1724
+ * マルチリレーション型への変換
1725
+ */
1726
+ convertToMultiRelation(value, defaultValue) {
1727
+ if (Array.isArray(value)) {
1728
+ return value.filter((item) => typeof item === "string").map((item) => this.extractFileNameFromPath(item));
1729
+ }
1730
+ if (typeof value === "string") {
1731
+ return value.split(",").map((item) => this.extractFileNameFromPath(item.trim()));
1732
+ }
1733
+ return defaultValue;
1734
+ }
1735
+ /**
1736
+ * パスからファイル名を抽出(拡張子除去)
1737
+ */
1738
+ extractFileNameFromPath(path) {
1739
+ const fileName = path.split("/").pop() || path;
1740
+ return fileName.replace(/\.md$/, "");
1741
+ }
1742
+ /**
1743
+ * スキーマベースでFrontMatterを補完する
1744
+ */
1745
+ getCompleteFrontMatter(rawFrontMatter) {
1746
+ const defaultFrontMatter = this.generateDefaultFrontMatter();
1747
+ return this.mergePreservingExistingValues(
1748
+ defaultFrontMatter,
1749
+ rawFrontMatter
1750
+ );
1751
+ }
1752
+ /**
1753
+ * 型安全にFrontMatterを補完する(Zodスキーマを使用している場合)
1754
+ */
1755
+ getTypedCompleteFrontMatter(rawFrontMatter) {
1756
+ if (!this.zodSchema) {
1757
+ throw new Error("Typed method requires a Zod schema");
1758
+ }
1759
+ const defaultFrontMatter = this.generateDefaultFrontMatter();
1760
+ const merged = this.mergePreservingExistingValues(
1761
+ defaultFrontMatter,
1762
+ rawFrontMatter
1763
+ );
1764
+ return this.zodSchema.parse(merged);
1765
+ }
1766
+ /**
1767
+ * スキーマからデフォルトFrontMatterを生成
1768
+ */
1769
+ generateDefaultFrontMatter() {
1770
+ if (!this.value) return {};
1771
+ const defaultFrontMatter = {};
1772
+ for (const [key, field] of Object.entries(this.value)) {
1773
+ defaultFrontMatter[key] = DocFrontMatterMdValue.generateDefaultValueFromSchemaField(field);
1473
1774
  }
1474
- const draft = {};
1475
- for (const field of schema.fields) {
1476
- const fieldKey = field.key;
1477
- if (fieldKey === key) {
1478
- draft[fieldKey] = value;
1479
- } else if (fieldKey in currentData) {
1480
- const value2 = currentData[fieldKey];
1481
- if (value2 !== void 0) {
1482
- draft[fieldKey] = value2;
1775
+ return zDocFileMdFrontMatter.parse(defaultFrontMatter);
1776
+ }
1777
+ /**
1778
+ * 既存の値を保持しながらマージ
1779
+ */
1780
+ mergePreservingExistingValues(defaults, existing) {
1781
+ const result = { ...defaults };
1782
+ if (existing && typeof existing === "object") {
1783
+ const validatedData = zDocFileMdFrontMatter.parse(existing);
1784
+ for (const [key, value] of Object.entries(validatedData)) {
1785
+ if (value !== void 0 && value !== null) {
1786
+ result[key] = value;
1483
1787
  }
1484
- } else {
1485
- draft[fieldKey] = field.toJson().default;
1486
1788
  }
1487
1789
  }
1488
- return new _DocFrontMatterMdValue(draft);
1790
+ return result;
1791
+ }
1792
+ /**
1793
+ * ファイルパスからファイル名を抽出(拡張子なし)
1794
+ */
1795
+ extractFileName(filePath) {
1796
+ return filePath.split("/").pop()?.replace(".md", "") || filePath;
1797
+ }
1798
+ /**
1799
+ * nullまundefinedのチェック
1800
+ */
1801
+ isNullOrUndefined(value) {
1802
+ return value === null || value === void 0;
1803
+ }
1804
+ toJson() {
1805
+ return this.value;
1806
+ }
1807
+ /**
1808
+ * スキーマを正規化(エラーを出すように変更)
1809
+ */
1810
+ static normalizeSchema(schema) {
1811
+ if (typeof schema !== "object" || schema === null) {
1812
+ throw new Error("Schema must be an object");
1813
+ }
1814
+ const normalized = {};
1815
+ const factory = new DocSchemaFieldFactory();
1816
+ for (const [key, field] of Object.entries(schema)) {
1817
+ const normalizedField = factory.fromUnknown(key, field);
1818
+ normalized[key] = normalizedField.toJson();
1819
+ }
1820
+ return normalized;
1821
+ }
1822
+ };
1823
+
1824
+ // lib/values/doc-front-matter-md-value.ts
1825
+ var DocFrontMatterMdValue = class _DocFrontMatterMdValue {
1826
+ zodSchema;
1827
+ constructor(value, zodSchema) {
1828
+ this.value = zodSchema.parse(value);
1829
+ this.zodSchema = zodSchema;
1830
+ Object.freeze(this.value);
1831
+ Object.freeze(this);
1832
+ }
1833
+ value;
1834
+ toJson() {
1835
+ return this.value;
1836
+ }
1837
+ toYaml() {
1838
+ return stringify2(this.value).trim();
1839
+ }
1840
+ schema(zodSchema) {
1841
+ return new DocSchemaValue(this.value.schema, zodSchema);
1842
+ }
1843
+ /**
1844
+ * Markdownテキストから生成
1845
+ */
1846
+ static from(markdownText, zodSchema) {
1847
+ const openMarkdown = new DocContentMdValue(markdownText);
1848
+ const rawData = openMarkdown.frontMatter.data || {};
1849
+ return new _DocFrontMatterMdValue(rawData, zodSchema);
1850
+ }
1851
+ /**
1852
+ * 空のFrontMatterを生成
1853
+ */
1854
+ static empty(zodSchema) {
1855
+ return new _DocFrontMatterMdValue({}, zodSchema);
1856
+ }
1857
+ /**
1858
+ * データから直接生成
1859
+ */
1860
+ static fromData(data, zodSchema) {
1861
+ return new _DocFrontMatterMdValue(data, zodSchema);
1862
+ }
1863
+ /**
1864
+ * スキーマフィールドに基づいてデフォルト値を生成する
1865
+ */
1866
+ static generateDefaultValueFromSchemaField(fieldDef) {
1867
+ if (fieldDef.type === "text") {
1868
+ return fieldDef.default ?? "";
1869
+ }
1870
+ if (fieldDef.type === "boolean") {
1871
+ return fieldDef.default ?? false;
1872
+ }
1873
+ if (fieldDef.type === "number") {
1874
+ return fieldDef.default ?? 0;
1875
+ }
1876
+ if (fieldDef.type === "multi-text" || fieldDef.type === "multi-number" || fieldDef.type === "multi-boolean" || fieldDef.type === "multi-relation") {
1877
+ return fieldDef.default ?? [];
1878
+ }
1879
+ if (fieldDef.type === "relation") {
1880
+ return fieldDef.default ?? null;
1881
+ }
1882
+ return null;
1489
1883
  }
1490
1884
  /**
1491
- * フィールド値の検証
1885
+ * 単一のプロパティを更新した新しいインスタンスを返す
1492
1886
  */
1493
- validateFieldValue(key, value, fieldDef) {
1887
+ withProperty(key, value) {
1888
+ const currentData = {
1889
+ ...this.toJson(),
1890
+ [key]: value
1891
+ };
1494
1892
  try {
1495
- const factory = new DocSchemaFieldFactory();
1496
- const schemaField = factory.fromSchemaEntry(key, fieldDef);
1497
- return schemaField.validate(value);
1893
+ const validated = this.zodSchema.parse(currentData);
1894
+ return new _DocFrontMatterMdValue(validated, this.zodSchema);
1498
1895
  } catch {
1499
- return false;
1896
+ return this;
1500
1897
  }
1501
1898
  }
1502
1899
  /**
@@ -1615,7 +2012,7 @@ var DocFrontMatterMdValue = class _DocFrontMatterMdValue {
1615
2012
  * プロパティが存在するかチェック
1616
2013
  */
1617
2014
  has(key) {
1618
- return key in this.value && this.value[key] !== void 0;
2015
+ return key in this.value && this.value[key] !== void 0 && this.value[key] !== null;
1619
2016
  }
1620
2017
  /**
1621
2018
  * すべてのキーを取得
@@ -1623,15 +2020,35 @@ var DocFrontMatterMdValue = class _DocFrontMatterMdValue {
1623
2020
  get keys() {
1624
2021
  return Object.keys(this.value);
1625
2022
  }
1626
- };
1627
-
1628
- // lib/values/doc-file-content-md-value.ts
1629
- var DocFileContentMdValue = class _DocFileContentMdValue {
1630
- constructor(value) {
1631
- this.value = value;
1632
- zDocFileContentMd.parse(value);
1633
- Object.freeze(this);
1634
- }
2023
+ field(key) {
2024
+ try {
2025
+ const parsed = this.zodSchema.parse(this.value);
2026
+ return parsed[key] ?? null;
2027
+ } catch {
2028
+ return this.value[key] ?? null;
2029
+ }
2030
+ }
2031
+ /**
2032
+ * 型安全な値の取得
2033
+ */
2034
+ get typedValue() {
2035
+ try {
2036
+ return this.zodSchema.parse(this.value);
2037
+ } catch {
2038
+ return null;
2039
+ }
2040
+ }
2041
+ };
2042
+
2043
+ // lib/values/doc-file-content-md-value.ts
2044
+ var DocFileContentMdValue = class _DocFileContentMdValue {
2045
+ constructor(value, zodSchema) {
2046
+ this.value = value;
2047
+ zDocFileContentMd.parse(value);
2048
+ this.zodSchema = zodSchema;
2049
+ Object.freeze(this);
2050
+ }
2051
+ zodSchema;
1635
2052
  /**
1636
2053
  * コンテンツ本文(frontMatterを含まない)
1637
2054
  */
@@ -1651,10 +2068,14 @@ var DocFileContentMdValue = class _DocFileContentMdValue {
1651
2068
  return this.value.description;
1652
2069
  }
1653
2070
  /**
1654
- * FrontMatter
2071
+ * FrontMatter(コンストラクタのZodスキーマまたは引数のスキーマを使用)
1655
2072
  */
1656
- get frontMatter() {
1657
- return new DocFrontMatterMdValue(this.value.frontMatter);
2073
+ frontMatter(zodSchema) {
2074
+ const schema = zodSchema || this.zodSchema;
2075
+ return new DocFrontMatterMdValue(
2076
+ this.value.frontMatter,
2077
+ schema
2078
+ );
1658
2079
  }
1659
2080
  /**
1660
2081
  * タイトルを更新
@@ -1664,13 +2085,16 @@ var DocFileContentMdValue = class _DocFileContentMdValue {
1664
2085
  const engine = new DocMarkdownSystem();
1665
2086
  const updatedText = engine.updateTitle(fullText, title);
1666
2087
  const updatedBody = engine.extractBody(updatedText);
1667
- return new _DocFileContentMdValue({
1668
- type: "markdown-content",
1669
- body: updatedBody,
1670
- title,
1671
- description: this.description,
1672
- frontMatter: this.value.frontMatter
1673
- });
2088
+ return new _DocFileContentMdValue(
2089
+ {
2090
+ type: "markdown-content",
2091
+ body: updatedBody,
2092
+ title,
2093
+ description: this.description,
2094
+ frontMatter: this.value.frontMatter
2095
+ },
2096
+ this.zodSchema
2097
+ );
1674
2098
  }
1675
2099
  /**
1676
2100
  * 説明を更新
@@ -1684,52 +2108,72 @@ var DocFileContentMdValue = class _DocFileContentMdValue {
1684
2108
  defaultTitle || this.title
1685
2109
  );
1686
2110
  const updatedBody = engine.extractBody(updatedText);
1687
- return new _DocFileContentMdValue({
1688
- type: "markdown-content",
1689
- body: updatedBody,
1690
- title: this.title,
1691
- description,
1692
- frontMatter: this.value.frontMatter
1693
- });
2111
+ return new _DocFileContentMdValue(
2112
+ {
2113
+ type: "markdown-content",
2114
+ body: updatedBody,
2115
+ title: this.title,
2116
+ description,
2117
+ frontMatter: this.value.frontMatter
2118
+ },
2119
+ this.zodSchema
2120
+ );
1694
2121
  }
1695
2122
  /**
1696
2123
  * コンテンツを更新
1697
2124
  */
1698
2125
  withContent(content) {
1699
2126
  const engine = new DocMarkdownSystem();
1700
- return new _DocFileContentMdValue({
1701
- type: "markdown-content",
1702
- body: content,
1703
- title: engine.extractTitle(content) || "",
1704
- description: engine.extractDescription(content) || "",
1705
- frontMatter: this.value.frontMatter
1706
- });
2127
+ return new _DocFileContentMdValue(
2128
+ {
2129
+ type: "markdown-content",
2130
+ body: content,
2131
+ title: engine.extractTitle(content) || "",
2132
+ description: engine.extractDescription(content) || "",
2133
+ frontMatter: this.value.frontMatter
2134
+ },
2135
+ this.zodSchema
2136
+ );
1707
2137
  }
1708
2138
  /**
1709
2139
  * FrontMatterを更新
1710
2140
  */
1711
- withFrontMatter(frontMatter) {
1712
- return new _DocFileContentMdValue({
1713
- type: "markdown-content",
1714
- body: this.body,
1715
- title: this.title,
1716
- description: this.description,
1717
- frontMatter: frontMatter.value
1718
- });
2141
+ withFrontMatter(frontMatter, zodSchema) {
2142
+ return new _DocFileContentMdValue(
2143
+ {
2144
+ type: "markdown-content",
2145
+ body: this.body,
2146
+ title: this.title,
2147
+ description: this.description,
2148
+ frontMatter: frontMatter.value
2149
+ },
2150
+ zodSchema
2151
+ );
2152
+ }
2153
+ /**
2154
+ * FrontMatterの特定のプロパティを更新
2155
+ */
2156
+ withFrontMatterProperty(key, value) {
2157
+ const currentFrontMatter = this.frontMatter();
2158
+ const updatedFrontMatter = currentFrontMatter.withProperty(key, value);
2159
+ return this.withFrontMatter(updatedFrontMatter, this.zodSchema);
1719
2160
  }
1720
2161
  /**
1721
2162
  * Markdownテキストから生成
1722
2163
  */
1723
- static fromMarkdown(markdown) {
2164
+ static fromMarkdown(markdown, zodSchema = zDocFileMdFrontMatter) {
1724
2165
  const engine = new DocMarkdownSystem();
1725
- const frontMatter = DocFrontMatterMdValue.from(markdown);
1726
- return new _DocFileContentMdValue({
1727
- type: "markdown-content",
1728
- body: engine.extractBody(markdown),
1729
- title: engine.extractTitle(markdown) || "",
1730
- description: engine.extractDescription(markdown) || "",
1731
- frontMatter: frontMatter.value
1732
- });
2166
+ const frontMatter = DocFrontMatterMdValue.from(markdown, zodSchema);
2167
+ return new _DocFileContentMdValue(
2168
+ {
2169
+ type: "markdown-content",
2170
+ body: engine.extractBody(markdown),
2171
+ title: engine.extractTitle(markdown) || "",
2172
+ description: engine.extractDescription(markdown) || "",
2173
+ frontMatter: frontMatter.value
2174
+ },
2175
+ zodSchema
2176
+ );
1733
2177
  }
1734
2178
  /**
1735
2179
  * 完全なMarkdownテキストを生成
@@ -1742,7 +2186,7 @@ var DocFileContentMdValue = class _DocFileContentMdValue {
1742
2186
  */
1743
2187
  toText() {
1744
2188
  return `---
1745
- ${this.frontMatter.toYaml()}
2189
+ ${this.frontMatter().toYaml()}
1746
2190
  ---
1747
2191
 
1748
2192
  ${this.body}`;
@@ -1750,14 +2194,17 @@ ${this.body}`;
1750
2194
  /**
1751
2195
  * 空のコンテンツを生成
1752
2196
  */
1753
- static empty(title) {
1754
- return new _DocFileContentMdValue({
1755
- type: "markdown-content",
1756
- body: `# ${title}`,
1757
- title,
1758
- description: "",
1759
- frontMatter: {}
1760
- });
2197
+ static empty(title, zodSchema) {
2198
+ return new _DocFileContentMdValue(
2199
+ {
2200
+ type: "markdown-content",
2201
+ body: `# ${title}`,
2202
+ title,
2203
+ description: "",
2204
+ frontMatter: {}
2205
+ },
2206
+ zodSchema
2207
+ );
1761
2208
  }
1762
2209
  /**
1763
2210
  * JSON形式で出力
@@ -1778,7 +2225,7 @@ var DocFileMdEntity = class _DocFileMdEntity {
1778
2225
  * コンテンツ
1779
2226
  */
1780
2227
  get content() {
1781
- return new DocFileContentMdValue(this.value.content);
2228
+ return new DocFileContentMdValue(this.value.content, zDocFileMdFrontMatter);
1782
2229
  }
1783
2230
  /**
1784
2231
  * パス情報
@@ -1950,410 +2397,162 @@ var DocRelationValue = class _DocRelationValue {
1950
2397
  this.value = value;
1951
2398
  zDocRelation.parse(value);
1952
2399
  Object.freeze(this);
1953
- Object.freeze(this.value);
1954
- }
1955
- /**
1956
- * リレーションパス
1957
- */
1958
- get path() {
1959
- return this.value.path;
1960
- }
1961
- /**
1962
- * リレーション対象ファイル一覧(値オブジェクト)
1963
- */
1964
- get files() {
1965
- return this.value.files.map((file) => new DocRelationFileValue(file));
1966
- }
1967
- /**
1968
- * ファイル数を取得
1969
- */
1970
- get fileCount() {
1971
- return this.files.length;
1972
- }
1973
- /**
1974
- * 空かどうか
1975
- */
1976
- get isEmpty() {
1977
- return this.files.length === 0;
1978
- }
1979
- /**
1980
- * 空のリレーションエンティティを作成
1981
- */
1982
- static empty(path) {
1983
- return new _DocRelationValue({
1984
- path,
1985
- files: []
1986
- });
1987
- }
1988
- /**
1989
- * JSON形式で出力
1990
- */
1991
- toJson() {
1992
- return {
1993
- path: this.path,
1994
- files: this.files.map((file) => file.toJson())
1995
- };
1996
- }
1997
- };
1998
-
1999
- // lib/doc-file-relation-reference.ts
2000
- var DocFileRelationReference = class {
2001
- constructor(props) {
2002
- this.props = props;
2003
- this.pathSystem = props.pathSystem;
2004
- Object.freeze(this);
2005
- }
2006
- pathSystem;
2007
- get fileSystem() {
2008
- return this.props.fileSystem;
2009
- }
2010
- get basePath() {
2011
- return this.fileSystem.getBasePath();
2012
- }
2013
- /**
2014
- * リレーションパス
2015
- */
2016
- get path() {
2017
- return this.props.filePath;
2018
- }
2019
- /**
2020
- * フルパス
2021
- */
2022
- get fullPath() {
2023
- return this.pathSystem.join(
2024
- this.fileSystem.getBasePath(),
2025
- this.props.filePath
2026
- );
2027
- }
2028
- async read() {
2029
- const files = await this.readFiles();
2030
- return new DocRelationValue({
2031
- path: this.path,
2032
- files: files.map((value) => value.toJson())
2033
- });
2034
- }
2035
- /**
2036
- * リレーションファイルの一覧を読み込む
2037
- */
2038
- async readFiles() {
2039
- const exists = await this.fileSystem.exists(this.path);
2040
- if (!exists) {
2041
- return [];
2042
- }
2043
- const filePaths = await this.fileSystem.readDirectoryFilePaths(this.path);
2044
- const files = [];
2045
- for (const filePath of filePaths) {
2046
- const file = await this.readFile(filePath);
2047
- if (file === null) continue;
2048
- files.push(file);
2049
- }
2050
- return files;
2051
- }
2052
- /**
2053
- * 単一のリレーションファイルを読み込む
2054
- */
2055
- async readFile(filePath) {
2056
- if (filePath.includes("index.md")) {
2057
- return null;
2058
- }
2059
- const content = await this.fileSystem.readFile(filePath);
2060
- if (content === null) {
2061
- return null;
2062
- }
2063
- const contentValue = DocFileContentMdValue.fromMarkdown(content);
2064
- const pathValue = DocFilePathValue.fromPathWithSystem(
2065
- filePath,
2066
- this.pathSystem,
2067
- this.basePath
2068
- );
2069
- const fileEntity = new DocFileMdEntity({
2070
- type: "markdown",
2071
- content: contentValue.value,
2072
- path: pathValue.value,
2073
- isArchived: false
2074
- });
2075
- return DocRelationFileValue.from(filePath, fileEntity.value.content.title);
2076
- }
2077
- /**
2078
- * ファイルの存在確認
2079
- */
2080
- async exists(slug) {
2081
- const filePath = `${this.path}/${slug}.md`;
2082
- return this.fileSystem.exists(filePath);
2083
- }
2084
- /**
2085
- * ファイル名の一覧を取得(拡張子なし)
2086
- */
2087
- async readSlugs() {
2088
- const files = await this.readFiles();
2089
- return files.map((file) => file.id);
2090
- }
2091
- /**
2092
- * ファイル数を取得
2093
- */
2094
- async count() {
2095
- const files = await this.readFiles();
2096
- return files.length;
2097
- }
2098
- /**
2099
- * 空かどうかを確認
2100
- */
2101
- async isEmpty() {
2102
- const count = await this.count();
2103
- return count === 0;
2104
- }
2105
- };
2106
-
2107
- // lib/values/doc-front-matter-index-value.ts
2108
- import { parse as parse2, stringify as stringify3 } from "yaml";
2109
-
2110
- // lib/values/doc-schema-value.ts
2111
- var DocSchemaValue = class _DocSchemaValue {
2112
- constructor(value) {
2113
- this.value = value;
2114
- zDocSchema.parse(value);
2115
- Object.freeze(this.value);
2116
- Object.freeze(this);
2117
- }
2118
- /**
2119
- * フィールド名の一覧を取得
2120
- */
2121
- get fieldNames() {
2122
- return Object.keys(this.value);
2123
- }
2124
- /**
2125
- * 指定されたフィールド名のフィールドを取得
2126
- */
2127
- field(name) {
2128
- const fieldDef = this.value[name];
2129
- if (fieldDef === void 0) {
2130
- return null;
2131
- }
2132
- const factory = new DocSchemaFieldFactory();
2133
- return factory.fromSchemaEntry(name, fieldDef);
2134
- }
2135
- get fields() {
2136
- const factory = new DocSchemaFieldFactory();
2137
- return this.fieldNames.map((name) => {
2138
- const fieldValue = this.value[name];
2139
- if (!fieldValue) {
2140
- throw new Error(`Field ${name} not found in schema`);
2141
- }
2142
- return factory.fromSchemaEntry(name, fieldValue);
2143
- });
2144
- }
2145
- get relationFields() {
2146
- return this.fields.filter((t) => {
2147
- return t instanceof DocSchemaFieldRelationSingleValue || t instanceof DocSchemaFieldRelationMultipleValue;
2148
- });
2149
- }
2150
- /**
2151
- * 空のスキーマエンティティを作成
2152
- */
2153
- static empty() {
2154
- return new _DocSchemaValue({});
2155
- }
2156
- /**
2157
- * スキーマからリレーションパスを抽出
2158
- */
2159
- extractRelationPaths() {
2160
- if (!this.value) return /* @__PURE__ */ new Set();
2161
- const uniqueRelationPaths = /* @__PURE__ */ new Set();
2162
- for (const field of Object.values(this.value)) {
2163
- if (this.isRelationField(field) && "path" in field) {
2164
- uniqueRelationPaths.add(field.path);
2165
- }
2166
- }
2167
- return uniqueRelationPaths;
2168
- }
2169
- /**
2170
- * フィールドがリレーション型かどうかを判定
2171
- */
2172
- isRelationField(field) {
2173
- return field.type === "relation" || field.type === "multi-relation";
2174
- }
2175
- /**
2176
- * リレーションオプションを作成
2177
- */
2178
- createRelationOption(filePath, title) {
2179
- const fileName = this.extractFileName(filePath);
2180
- return zDocRelationFile.parse({
2181
- name: fileName,
2182
- label: title || fileName,
2183
- value: fileName,
2184
- path: filePath
2185
- });
2186
- }
2187
- /**
2188
- * ファイルをスキップすべきかどうかを判定
2189
- */
2190
- shouldSkipFile(filePath) {
2191
- return filePath.endsWith("index.md") || filePath.endsWith("README.md");
2192
- }
2193
- /**
2194
- * 値を指定された型に変換する(デフォルト値フォールバック付き)
2195
- */
2196
- convertValue(value, targetType, defaultValue) {
2197
- if (this.isNullOrUndefined(value)) {
2198
- return defaultValue;
2199
- }
2200
- try {
2201
- return this.convertByType(value, targetType, defaultValue);
2202
- } catch {
2203
- return defaultValue;
2204
- }
2205
- }
2206
- /**
2207
- * 型別の変換処理
2208
- */
2209
- convertByType(value, targetType, defaultValue) {
2210
- if (targetType === "text") {
2211
- return this.convertToString(value, defaultValue);
2212
- }
2213
- if (targetType === "number") {
2214
- return this.convertToNumber(value, defaultValue);
2215
- }
2216
- if (targetType === "boolean") {
2217
- return this.convertToBoolean(value, defaultValue);
2218
- }
2219
- if (targetType === "multi-text" || targetType === "array") {
2220
- return this.convertToArray(value, defaultValue);
2221
- }
2222
- if (targetType === "relation") {
2223
- return this.convertToRelation(value, defaultValue);
2224
- }
2225
- if (targetType === "multi-relation") {
2226
- return this.convertToMultiRelation(value, defaultValue);
2227
- }
2228
- return defaultValue;
2400
+ Object.freeze(this.value);
2229
2401
  }
2230
2402
  /**
2231
- * 文字列型への変換
2403
+ * リレーションパス
2232
2404
  */
2233
- convertToString(value, defaultValue) {
2234
- if (typeof value === "string") return value;
2235
- if (typeof value === "number") return String(value);
2236
- if (typeof value === "boolean") return String(value);
2237
- return defaultValue;
2405
+ get path() {
2406
+ return this.value.path;
2238
2407
  }
2239
2408
  /**
2240
- * 数値型への変換
2409
+ * リレーション対象ファイル一覧(値オブジェクト)
2241
2410
  */
2242
- convertToNumber(value, defaultValue) {
2243
- if (typeof value === "number") return value;
2244
- if (typeof value === "string") {
2245
- const parsed = Number(value);
2246
- return Number.isNaN(parsed) ? defaultValue : parsed;
2247
- }
2248
- if (typeof value === "boolean") return value ? 1 : 0;
2249
- return defaultValue;
2411
+ get files() {
2412
+ return this.value.files.map((file) => new DocRelationFileValue(file));
2250
2413
  }
2251
2414
  /**
2252
- * ブール型への変換
2415
+ * ファイル数を取得
2253
2416
  */
2254
- convertToBoolean(value, defaultValue) {
2255
- if (typeof value === "boolean") return value;
2256
- if (typeof value === "string") {
2257
- const lower = value.toLowerCase();
2258
- if (lower === "true" || lower === "1") return true;
2259
- if (lower === "false" || lower === "0") return false;
2260
- return defaultValue;
2261
- }
2262
- if (typeof value === "number") return value !== 0;
2263
- return defaultValue;
2417
+ get fileCount() {
2418
+ return this.files.length;
2264
2419
  }
2265
2420
  /**
2266
- * 配列型への変換
2421
+ * 空かどうか
2267
2422
  */
2268
- convertToArray(value, defaultValue) {
2269
- if (Array.isArray(value)) return value;
2270
- if (typeof value === "string") {
2271
- return value.split(",").map((item) => item.trim());
2272
- }
2273
- return defaultValue;
2423
+ get isEmpty() {
2424
+ return this.files.length === 0;
2274
2425
  }
2275
2426
  /**
2276
- * リレーション型への変換
2427
+ * 空のリレーションエンティティを作成
2277
2428
  */
2278
- convertToRelation(value, defaultValue) {
2279
- if (typeof value === "string") {
2280
- return this.extractFileNameFromPath(value);
2281
- }
2282
- return defaultValue;
2429
+ static empty(path) {
2430
+ return new _DocRelationValue({
2431
+ path,
2432
+ files: []
2433
+ });
2283
2434
  }
2284
2435
  /**
2285
- * マルチリレーション型への変換
2436
+ * JSON形式で出力
2286
2437
  */
2287
- convertToMultiRelation(value, defaultValue) {
2288
- if (Array.isArray(value)) {
2289
- return value.filter((item) => typeof item === "string").map((item) => this.extractFileNameFromPath(item));
2290
- }
2291
- if (typeof value === "string") {
2292
- return value.split(",").map((item) => this.extractFileNameFromPath(item.trim()));
2293
- }
2294
- return defaultValue;
2438
+ toJson() {
2439
+ return {
2440
+ path: this.path,
2441
+ files: this.files.map((file) => file.toJson())
2442
+ };
2443
+ }
2444
+ };
2445
+
2446
+ // lib/doc-file-relation-reference.ts
2447
+ var DocFileRelationReference = class {
2448
+ constructor(props) {
2449
+ this.props = props;
2450
+ this.pathSystem = props.pathSystem;
2451
+ Object.freeze(this);
2452
+ }
2453
+ pathSystem;
2454
+ get fileSystem() {
2455
+ return this.props.fileSystem;
2456
+ }
2457
+ get basePath() {
2458
+ return this.fileSystem.getBasePath();
2295
2459
  }
2296
2460
  /**
2297
- * パスからファイル名を抽出(拡張子除去)
2461
+ * リレーションパス
2298
2462
  */
2299
- extractFileNameFromPath(path) {
2300
- const fileName = path.split("/").pop() || path;
2301
- return fileName.replace(/\.md$/, "");
2463
+ get path() {
2464
+ return this.props.filePath;
2302
2465
  }
2303
2466
  /**
2304
- * スキーマベースでFrontMatterを補完する
2467
+ * フルパス
2305
2468
  */
2306
- getCompleteFrontMatter(rawFrontMatter) {
2307
- const defaultFrontMatter = this.generateDefaultFrontMatter();
2308
- return this.mergePreservingExistingValues(
2309
- defaultFrontMatter,
2310
- rawFrontMatter
2469
+ get fullPath() {
2470
+ return this.pathSystem.join(
2471
+ this.fileSystem.getBasePath(),
2472
+ this.props.filePath
2311
2473
  );
2312
2474
  }
2475
+ async read() {
2476
+ const files = await this.readFiles();
2477
+ return new DocRelationValue({
2478
+ path: this.path,
2479
+ files: files.map((value) => value.toJson())
2480
+ });
2481
+ }
2313
2482
  /**
2314
- * スキーマからデフォルトFrontMatterを生成
2483
+ * リレーションファイルの一覧を読み込む
2315
2484
  */
2316
- generateDefaultFrontMatter() {
2317
- if (!this.value) return {};
2318
- const defaultFrontMatter = {};
2319
- for (const [key, field] of Object.entries(this.value)) {
2320
- defaultFrontMatter[key] = DocFrontMatterMdValue.generateDefaultValueFromSchemaField(field);
2485
+ async readFiles() {
2486
+ const exists = await this.fileSystem.exists(this.path);
2487
+ if (!exists) {
2488
+ return [];
2321
2489
  }
2322
- return zDocFileMdFrontMatter.parse(defaultFrontMatter);
2490
+ const filePaths = await this.fileSystem.readDirectoryFilePaths(this.path);
2491
+ const files = [];
2492
+ for (const filePath of filePaths) {
2493
+ const file = await this.readFile(filePath);
2494
+ if (file === null) continue;
2495
+ files.push(file);
2496
+ }
2497
+ return files;
2323
2498
  }
2324
2499
  /**
2325
- * 既存の値を保持しながらマージ
2500
+ * 単一のリレーションファイルを読み込む
2326
2501
  */
2327
- mergePreservingExistingValues(defaults, existing) {
2328
- const result = { ...defaults };
2329
- if (existing && typeof existing === "object") {
2330
- const validatedData = zDocFileMdFrontMatter.parse(existing);
2331
- for (const [key, value] of Object.entries(validatedData)) {
2332
- if (value !== void 0 && value !== null) {
2333
- result[key] = value;
2334
- }
2335
- }
2502
+ async readFile(filePath) {
2503
+ if (filePath.includes("index.md")) {
2504
+ return null;
2336
2505
  }
2337
- return result;
2506
+ const content = await this.fileSystem.readFile(filePath);
2507
+ if (content === null) {
2508
+ return null;
2509
+ }
2510
+ const contentValue = DocFileContentMdValue.fromMarkdown(content);
2511
+ const pathValue = DocFilePathValue.fromPathWithSystem(
2512
+ filePath,
2513
+ this.pathSystem,
2514
+ this.basePath
2515
+ );
2516
+ const fileEntity = new DocFileMdEntity({
2517
+ type: "markdown",
2518
+ content: contentValue.value,
2519
+ path: pathValue.value,
2520
+ isArchived: false
2521
+ });
2522
+ return DocRelationFileValue.from(filePath, fileEntity.value.content.title);
2338
2523
  }
2339
2524
  /**
2340
- * ファイルパスからファイル名を抽出(拡張子なし)
2525
+ * ファイルの存在確認
2341
2526
  */
2342
- extractFileName(filePath) {
2343
- return filePath.split("/").pop()?.replace(".md", "") || filePath;
2527
+ async exists(slug) {
2528
+ const filePath = `${this.path}/${slug}.md`;
2529
+ return this.fileSystem.exists(filePath);
2344
2530
  }
2345
2531
  /**
2346
- * nullまundefinedのチェック
2532
+ * ファイル名の一覧を取得(拡張子なし)
2347
2533
  */
2348
- isNullOrUndefined(value) {
2349
- return value === null || value === void 0;
2534
+ async readSlugs() {
2535
+ const files = await this.readFiles();
2536
+ return files.map((file) => file.id);
2350
2537
  }
2351
- toJson() {
2352
- return this.value;
2538
+ /**
2539
+ * ファイル数を取得
2540
+ */
2541
+ async count() {
2542
+ const files = await this.readFiles();
2543
+ return files.length;
2544
+ }
2545
+ /**
2546
+ * 空かどうかを確認
2547
+ */
2548
+ async isEmpty() {
2549
+ const count = await this.count();
2550
+ return count === 0;
2353
2551
  }
2354
2552
  };
2355
2553
 
2356
2554
  // lib/values/doc-front-matter-index-value.ts
2555
+ import { parse as parse2, stringify as stringify3 } from "yaml";
2357
2556
  var DocFrontMatterIndexValue = class _DocFrontMatterIndexValue {
2358
2557
  constructor(value) {
2359
2558
  this.value = value;
@@ -2363,14 +2562,14 @@ var DocFrontMatterIndexValue = class _DocFrontMatterIndexValue {
2363
2562
  /**
2364
2563
  * アイコンを取得
2365
2564
  */
2366
- get icon() {
2565
+ icon() {
2367
2566
  return this.value.icon;
2368
2567
  }
2369
2568
  /**
2370
2569
  * スキーマを取得
2371
2570
  */
2372
- get schema() {
2373
- return new DocSchemaValue(this.value.schema);
2571
+ schema(zodSchema) {
2572
+ return new DocSchemaValue(this.value.schema, zodSchema);
2374
2573
  }
2375
2574
  /**
2376
2575
  * iconフィールドを更新
@@ -2744,7 +2943,8 @@ var DocFileIndexReference = class _DocFileIndexReference {
2744
2943
  async read() {
2745
2944
  const content = await this.fileSystem.readFile(this.filePath);
2746
2945
  if (content === null) {
2747
- const dirName = this.filePath.split("/").pop() || "directory";
2946
+ const dirPath = this.pathSystem.dirname(this.filePath);
2947
+ const dirName = this.pathSystem.basename(dirPath);
2748
2948
  const pathValue2 = DocFilePathValue.fromPathWithSystem(
2749
2949
  this.filePath,
2750
2950
  this.pathSystem,
@@ -3123,12 +3323,16 @@ var DocFileMdReference = class _DocFileMdReference {
3123
3323
  if (file instanceof Error) {
3124
3324
  return file;
3125
3325
  }
3126
- const relationValue = file.content.frontMatter.relation(key);
3326
+ const relationValue = file.content.frontMatter().relation(key);
3127
3327
  if (relationValue === null) {
3128
3328
  return null;
3129
3329
  }
3130
- const indexFile = await this.getDirectoryIndex();
3131
- if (!indexFile) {
3330
+ const indexRef = await this.getDirectoryIndex();
3331
+ if (!indexRef) {
3332
+ return null;
3333
+ }
3334
+ const indexFile = await indexRef.read();
3335
+ if (indexFile instanceof Error) {
3132
3336
  return null;
3133
3337
  }
3134
3338
  const schemaData = indexFile.content.frontMatter.value.schema;
@@ -3140,7 +3344,12 @@ var DocFileMdReference = class _DocFileMdReference {
3140
3344
  if (!field || !("path" in field) || !field.path) {
3141
3345
  return null;
3142
3346
  }
3143
- const fullPath = this.pathSystem.join(field.path, `${relationValue}.md`);
3347
+ let resolvedPath = field.path;
3348
+ if (field.path.startsWith("..")) {
3349
+ resolvedPath = this.pathSystem.join(this.directoryPath, field.path);
3350
+ resolvedPath = this.pathSystem.normalize(resolvedPath);
3351
+ }
3352
+ const fullPath = this.pathSystem.join(resolvedPath, `${relationValue}.md`);
3144
3353
  return new _DocFileMdReference({
3145
3354
  path: fullPath,
3146
3355
  fileSystem: this.fileSystem,
@@ -3152,16 +3361,28 @@ var DocFileMdReference = class _DocFileMdReference {
3152
3361
  */
3153
3362
  async getDirectoryIndex() {
3154
3363
  const indexPath = this.pathSystem.join(this.directoryPath, "index.md");
3155
- const indexRef = new _DocFileMdReference({
3156
- path: indexPath,
3157
- fileSystem: this.fileSystem,
3158
- pathSystem: this.pathSystem
3159
- });
3160
- const indexFile = await indexRef.read();
3161
- if (indexFile instanceof Error) {
3162
- return null;
3364
+ const exists = await this.fileSystem.exists(indexPath);
3365
+ if (exists) {
3366
+ return new DocFileIndexReference({
3367
+ path: indexPath,
3368
+ fileSystem: this.fileSystem,
3369
+ pathSystem: this.pathSystem
3370
+ });
3371
+ }
3372
+ const archivedIndexPath = this.pathSystem.join(
3373
+ this.directoryPath,
3374
+ "_",
3375
+ "index.md"
3376
+ );
3377
+ const archivedExists = await this.fileSystem.exists(archivedIndexPath);
3378
+ if (archivedExists) {
3379
+ return new DocFileIndexReference({
3380
+ path: archivedIndexPath,
3381
+ fileSystem: this.fileSystem,
3382
+ pathSystem: this.pathSystem
3383
+ });
3163
3384
  }
3164
- return indexFile;
3385
+ return null;
3165
3386
  }
3166
3387
  /**
3167
3388
  * 複数のリレーションを取得する
@@ -3177,12 +3398,16 @@ var DocFileMdReference = class _DocFileMdReference {
3177
3398
  if (file instanceof Error) {
3178
3399
  return [];
3179
3400
  }
3180
- const relationValues = file.content.frontMatter.multiRelation(key);
3401
+ const relationValues = file.content.frontMatter().multiRelation(key);
3181
3402
  if (relationValues.length === 0) {
3182
3403
  return [];
3183
3404
  }
3184
- const indexFile = await this.getDirectoryIndex();
3185
- if (!indexFile) {
3405
+ const indexRef = await this.getDirectoryIndex();
3406
+ if (!indexRef) {
3407
+ return [];
3408
+ }
3409
+ const indexFile = await indexRef.read();
3410
+ if (indexFile instanceof Error) {
3186
3411
  return [];
3187
3412
  }
3188
3413
  const schemaData = indexFile.content.frontMatter.value.schema;
@@ -3195,7 +3420,12 @@ var DocFileMdReference = class _DocFileMdReference {
3195
3420
  return [];
3196
3421
  }
3197
3422
  return relationValues.map((relationValue) => {
3198
- const fullPath = this.pathSystem.join(field.path, `${relationValue}.md`);
3423
+ let resolvedPath = field.path;
3424
+ if (field.path.startsWith("..")) {
3425
+ resolvedPath = this.pathSystem.join(this.directoryPath, field.path);
3426
+ resolvedPath = this.pathSystem.normalize(resolvedPath);
3427
+ }
3428
+ const fullPath = this.pathSystem.join(resolvedPath, `${relationValue}.md`);
3199
3429
  return new _DocFileMdReference({
3200
3430
  path: fullPath,
3201
3431
  fileSystem: this.fileSystem,
@@ -3203,6 +3433,38 @@ var DocFileMdReference = class _DocFileMdReference {
3203
3433
  });
3204
3434
  });
3205
3435
  }
3436
+ /**
3437
+ * FrontMatterを型安全に読み込む
3438
+ */
3439
+ async readFrontMatter(zodSchema) {
3440
+ const entity = await this.read();
3441
+ if (entity instanceof Error) {
3442
+ return entity;
3443
+ }
3444
+ return entity.content.frontMatter(zodSchema);
3445
+ }
3446
+ /**
3447
+ * FrontMatterを型安全に更新
3448
+ */
3449
+ async updateFrontMatter(key, value, zodSchema) {
3450
+ const entity = await this.read();
3451
+ if (entity instanceof Error) {
3452
+ return entity;
3453
+ }
3454
+ const currentFrontMatter = entity.content.frontMatter(zodSchema);
3455
+ const updatedFrontMatter = currentFrontMatter.withProperty(key, value);
3456
+ const baseContent = new DocFileContentMdValue(
3457
+ entity.content.value,
3458
+ zDocFileMdFrontMatter
3459
+ );
3460
+ const updatedContent = baseContent.withFrontMatter(
3461
+ updatedFrontMatter,
3462
+ zDocFileMdFrontMatter
3463
+ );
3464
+ const updatedEntity = entity.withContent(updatedContent);
3465
+ await this.write(updatedEntity);
3466
+ return void 0;
3467
+ }
3206
3468
  };
3207
3469
 
3208
3470
  // lib/entities/doc-file-unknown-entity.ts
@@ -3546,6 +3808,17 @@ var DocDirectoryReference = class _DocDirectoryReference {
3546
3808
  }
3547
3809
  return files;
3548
3810
  }
3811
+ /**
3812
+ * Markdownファイルの参照とFrontMatterを型安全に取得
3813
+ */
3814
+ async mdFilesWithFrontMatter(zodSchema) {
3815
+ const results = [];
3816
+ for await (const file of this.mdFilesGenerator()) {
3817
+ const frontMatter = await file.readFrontMatter(zodSchema);
3818
+ results.push({ reference: file, frontMatter });
3819
+ }
3820
+ return results;
3821
+ }
3549
3822
  file(fileName) {
3550
3823
  const filePath = this.pathSystem.join(this.relativePath, fileName);
3551
3824
  if (fileName.endsWith(".md")) {
@@ -3587,6 +3860,21 @@ var DocDirectoryReference = class _DocDirectoryReference {
3587
3860
  }
3588
3861
  return entities;
3589
3862
  }
3863
+ /**
3864
+ * Zodスキーマを使って型安全にMarkdownファイルのFrontMatterを読み込む
3865
+ */
3866
+ async readMdFilesWithSchema(zodSchema) {
3867
+ const results = [];
3868
+ for await (const file of this.mdFilesGenerator()) {
3869
+ const entity = await file.read();
3870
+ if (entity instanceof Error) continue;
3871
+ const frontMatter = entity.content.frontMatter(
3872
+ zodSchema
3873
+ );
3874
+ results.push({ entity, frontMatter });
3875
+ }
3876
+ return results;
3877
+ }
3590
3878
  async readUnknownFiles() {
3591
3879
  const entities = [];
3592
3880
  for await (const file of this.filesGenerator()) {
@@ -3898,7 +4186,12 @@ var DocFileTreeSystem = class {
3898
4186
  if (await indexFile.exists()) {
3899
4187
  const entity = await indexFile.read();
3900
4188
  title = entity.value.content.title || fileName;
3901
- icon = entity.value.content.frontMatter.icon || "\u{1F4C1}";
4189
+ const content = entity.content;
4190
+ const frontMatter = content.frontMatter;
4191
+ if (frontMatter instanceof DocFrontMatterIndexValue) {
4192
+ const iconValue = frontMatter.icon();
4193
+ icon = iconValue || "\u{1F4C1}";
4194
+ }
3902
4195
  }
3903
4196
  const children = await this.buildFileTree(filePath);
3904
4197
  return DocTreeDirectoryNodeValue.from({
@@ -3959,7 +4252,12 @@ var DocFileTreeSystem = class {
3959
4252
  if (await indexFile.exists()) {
3960
4253
  const entity = await indexFile.read();
3961
4254
  title = entity.value.content.title || fileName;
3962
- icon = entity.value.content.frontMatter.icon || "\u{1F4C1}";
4255
+ const content = entity.content;
4256
+ const frontMatter = content.frontMatter;
4257
+ if (frontMatter instanceof DocFrontMatterIndexValue) {
4258
+ const iconValue = frontMatter.icon();
4259
+ icon = iconValue || "\u{1F4C1}";
4260
+ }
3963
4261
  }
3964
4262
  const children = await this.buildDirectoryTree(filePath);
3965
4263
  return DocTreeDirectoryNodeValue.from({
@@ -4599,6 +4897,7 @@ export {
4599
4897
  zDocSchema,
4600
4898
  zDocSchemaField,
4601
4899
  zDocSchemaFieldBoolean,
4900
+ zDocSchemaFieldMinimal,
4602
4901
  zDocSchemaFieldMultiBoolean,
4603
4902
  zDocSchemaFieldMultiNumber,
4604
4903
  zDocSchemaFieldMultiRelation,
@@ -4611,6 +4910,7 @@ export {
4611
4910
  zDocSchemaFieldSelectText,
4612
4911
  zDocSchemaFieldText,
4613
4912
  zDocSchemaFieldType,
4913
+ zDocSchemaMinimal,
4614
4914
  zDocTreeDirectoryNode,
4615
4915
  zDocTreeFileNode,
4616
4916
  zDocTreeNode