@interactive-inc/docs-client 0.0.9 → 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
  }
@@ -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
-
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
+
1628
2043
  // lib/values/doc-file-content-md-value.ts
1629
2044
  var DocFileContentMdValue = class _DocFileContentMdValue {
1630
- constructor(value) {
2045
+ constructor(value, zodSchema) {
1631
2046
  this.value = value;
1632
2047
  zDocFileContentMd.parse(value);
2048
+ this.zodSchema = zodSchema;
1633
2049
  Object.freeze(this);
1634
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
  * パス情報
@@ -1952,424 +2399,160 @@ var DocRelationValue = class _DocRelationValue {
1952
2399
  Object.freeze(this);
1953
2400
  Object.freeze(this.value);
1954
2401
  }
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
- value;
2113
- constructor(value) {
2114
- this.value = _DocSchemaValue.normalizeSchema(value);
2115
- zDocSchema.parse(this.value);
2116
- Object.freeze(this.value);
2117
- Object.freeze(this);
2118
- }
2119
- /**
2120
- * フィールド名の一覧を取得
2121
- */
2122
- get fieldNames() {
2123
- return Object.keys(this.value);
2124
- }
2125
- /**
2126
- * 指定されたフィールド名のフィールドを取得
2127
- */
2128
- field(name) {
2129
- const fieldDef = this.value[name];
2130
- if (fieldDef === void 0) {
2131
- return null;
2132
- }
2133
- const factory = new DocSchemaFieldFactory();
2134
- return factory.fromUnknown(name, fieldDef);
2135
- }
2136
- get fields() {
2137
- const factory = new DocSchemaFieldFactory();
2138
- return this.fieldNames.map((name) => {
2139
- const fieldValue = this.value[name];
2140
- if (!fieldValue) {
2141
- throw new Error(`Field ${name} not found in schema`);
2142
- }
2143
- return factory.fromUnknown(name, fieldValue);
2144
- });
2145
- }
2146
- get relationFields() {
2147
- return this.fields.filter((t) => {
2148
- return t instanceof DocSchemaFieldRelationSingleValue || t instanceof DocSchemaFieldRelationMultipleValue;
2149
- });
2150
- }
2151
- /**
2152
- * 空のスキーマエンティティを作成
2153
- */
2154
- static empty() {
2155
- return new _DocSchemaValue({});
2156
- }
2157
- /**
2158
- * スキーマからリレーションパスを抽出
2159
- */
2160
- extractRelationPaths() {
2161
- if (!this.value) return /* @__PURE__ */ new Set();
2162
- const uniqueRelationPaths = /* @__PURE__ */ new Set();
2163
- for (const field of Object.values(this.value)) {
2164
- if (this.isRelationField(field) && "path" in field) {
2165
- uniqueRelationPaths.add(field.path);
2166
- }
2167
- }
2168
- return uniqueRelationPaths;
2169
- }
2170
- /**
2171
- * フィールドがリレーション型かどうかを判定
2172
- */
2173
- isRelationField(field) {
2174
- return field.type === "relation" || field.type === "multi-relation";
2175
- }
2176
- /**
2177
- * リレーションオプションを作成
2178
- */
2179
- createRelationOption(filePath, title) {
2180
- const fileName = this.extractFileName(filePath);
2181
- return zDocRelationFile.parse({
2182
- name: fileName,
2183
- label: title || fileName,
2184
- value: fileName,
2185
- path: filePath
2186
- });
2187
- }
2188
- /**
2189
- * ファイルをスキップすべきかどうかを判定
2190
- */
2191
- shouldSkipFile(filePath) {
2192
- return filePath.endsWith("index.md") || filePath.endsWith("README.md");
2193
- }
2194
- /**
2195
- * 値を指定された型に変換する(デフォルト値フォールバック付き)
2196
- */
2197
- convertValue(value, targetType, defaultValue) {
2198
- if (this.isNullOrUndefined(value)) {
2199
- return defaultValue;
2200
- }
2201
- try {
2202
- return this.convertByType(value, targetType, defaultValue);
2203
- } catch {
2204
- return defaultValue;
2205
- }
2206
- }
2207
- /**
2208
- * 型別の変換処理
2209
- */
2210
- convertByType(value, targetType, defaultValue) {
2211
- if (targetType === "text") {
2212
- return this.convertToString(value, defaultValue);
2213
- }
2214
- if (targetType === "number") {
2215
- return this.convertToNumber(value, defaultValue);
2216
- }
2217
- if (targetType === "boolean") {
2218
- return this.convertToBoolean(value, defaultValue);
2219
- }
2220
- if (targetType === "multi-text" || targetType === "array") {
2221
- return this.convertToArray(value, defaultValue);
2222
- }
2223
- if (targetType === "relation") {
2224
- return this.convertToRelation(value, defaultValue);
2225
- }
2226
- if (targetType === "multi-relation") {
2227
- return this.convertToMultiRelation(value, defaultValue);
2228
- }
2229
- return defaultValue;
2230
- }
2231
- /**
2232
- * 文字列型への変換
2233
- */
2234
- convertToString(value, defaultValue) {
2235
- if (typeof value === "string") return value;
2236
- if (typeof value === "number") return String(value);
2237
- if (typeof value === "boolean") return String(value);
2238
- return defaultValue;
2402
+ /**
2403
+ * リレーションパス
2404
+ */
2405
+ get path() {
2406
+ return this.value.path;
2239
2407
  }
2240
2408
  /**
2241
- * 数値型への変換
2409
+ * リレーション対象ファイル一覧(値オブジェクト)
2242
2410
  */
2243
- convertToNumber(value, defaultValue) {
2244
- if (typeof value === "number") return value;
2245
- if (typeof value === "string") {
2246
- const parsed = Number(value);
2247
- return Number.isNaN(parsed) ? defaultValue : parsed;
2248
- }
2249
- if (typeof value === "boolean") return value ? 1 : 0;
2250
- return defaultValue;
2411
+ get files() {
2412
+ return this.value.files.map((file) => new DocRelationFileValue(file));
2251
2413
  }
2252
2414
  /**
2253
- * ブール型への変換
2415
+ * ファイル数を取得
2254
2416
  */
2255
- convertToBoolean(value, defaultValue) {
2256
- if (typeof value === "boolean") return value;
2257
- if (typeof value === "string") {
2258
- const lower = value.toLowerCase();
2259
- if (lower === "true" || lower === "1") return true;
2260
- if (lower === "false" || lower === "0") return false;
2261
- return defaultValue;
2262
- }
2263
- if (typeof value === "number") return value !== 0;
2264
- return defaultValue;
2417
+ get fileCount() {
2418
+ return this.files.length;
2265
2419
  }
2266
2420
  /**
2267
- * 配列型への変換
2421
+ * 空かどうか
2268
2422
  */
2269
- convertToArray(value, defaultValue) {
2270
- if (Array.isArray(value)) return value;
2271
- if (typeof value === "string") {
2272
- return value.split(",").map((item) => item.trim());
2273
- }
2274
- return defaultValue;
2423
+ get isEmpty() {
2424
+ return this.files.length === 0;
2275
2425
  }
2276
2426
  /**
2277
- * リレーション型への変換
2427
+ * 空のリレーションエンティティを作成
2278
2428
  */
2279
- convertToRelation(value, defaultValue) {
2280
- if (typeof value === "string") {
2281
- return this.extractFileNameFromPath(value);
2282
- }
2283
- return defaultValue;
2429
+ static empty(path) {
2430
+ return new _DocRelationValue({
2431
+ path,
2432
+ files: []
2433
+ });
2284
2434
  }
2285
2435
  /**
2286
- * マルチリレーション型への変換
2436
+ * JSON形式で出力
2287
2437
  */
2288
- convertToMultiRelation(value, defaultValue) {
2289
- if (Array.isArray(value)) {
2290
- return value.filter((item) => typeof item === "string").map((item) => this.extractFileNameFromPath(item));
2291
- }
2292
- if (typeof value === "string") {
2293
- return value.split(",").map((item) => this.extractFileNameFromPath(item.trim()));
2294
- }
2295
- 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();
2296
2459
  }
2297
2460
  /**
2298
- * パスからファイル名を抽出(拡張子除去)
2461
+ * リレーションパス
2299
2462
  */
2300
- extractFileNameFromPath(path) {
2301
- const fileName = path.split("/").pop() || path;
2302
- return fileName.replace(/\.md$/, "");
2463
+ get path() {
2464
+ return this.props.filePath;
2303
2465
  }
2304
2466
  /**
2305
- * スキーマベースでFrontMatterを補完する
2467
+ * フルパス
2306
2468
  */
2307
- getCompleteFrontMatter(rawFrontMatter) {
2308
- const defaultFrontMatter = this.generateDefaultFrontMatter();
2309
- return this.mergePreservingExistingValues(
2310
- defaultFrontMatter,
2311
- rawFrontMatter
2469
+ get fullPath() {
2470
+ return this.pathSystem.join(
2471
+ this.fileSystem.getBasePath(),
2472
+ this.props.filePath
2312
2473
  );
2313
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
+ }
2314
2482
  /**
2315
- * スキーマからデフォルトFrontMatterを生成
2483
+ * リレーションファイルの一覧を読み込む
2316
2484
  */
2317
- generateDefaultFrontMatter() {
2318
- if (!this.value) return {};
2319
- const defaultFrontMatter = {};
2320
- for (const [key, field] of Object.entries(this.value)) {
2321
- defaultFrontMatter[key] = DocFrontMatterMdValue.generateDefaultValueFromSchemaField(field);
2485
+ async readFiles() {
2486
+ const exists = await this.fileSystem.exists(this.path);
2487
+ if (!exists) {
2488
+ return [];
2322
2489
  }
2323
- 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;
2324
2498
  }
2325
2499
  /**
2326
- * 既存の値を保持しながらマージ
2500
+ * 単一のリレーションファイルを読み込む
2327
2501
  */
2328
- mergePreservingExistingValues(defaults, existing) {
2329
- const result = { ...defaults };
2330
- if (existing && typeof existing === "object") {
2331
- const validatedData = zDocFileMdFrontMatter.parse(existing);
2332
- for (const [key, value] of Object.entries(validatedData)) {
2333
- if (value !== void 0 && value !== null) {
2334
- result[key] = value;
2335
- }
2336
- }
2502
+ async readFile(filePath) {
2503
+ if (filePath.includes("index.md")) {
2504
+ return null;
2337
2505
  }
2338
- 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);
2339
2523
  }
2340
2524
  /**
2341
- * ファイルパスからファイル名を抽出(拡張子なし)
2525
+ * ファイルの存在確認
2342
2526
  */
2343
- extractFileName(filePath) {
2344
- return filePath.split("/").pop()?.replace(".md", "") || filePath;
2527
+ async exists(slug) {
2528
+ const filePath = `${this.path}/${slug}.md`;
2529
+ return this.fileSystem.exists(filePath);
2345
2530
  }
2346
2531
  /**
2347
- * nullまundefinedのチェック
2532
+ * ファイル名の一覧を取得(拡張子なし)
2348
2533
  */
2349
- isNullOrUndefined(value) {
2350
- return value === null || value === void 0;
2534
+ async readSlugs() {
2535
+ const files = await this.readFiles();
2536
+ return files.map((file) => file.id);
2351
2537
  }
2352
- toJson() {
2353
- return this.value;
2538
+ /**
2539
+ * ファイル数を取得
2540
+ */
2541
+ async count() {
2542
+ const files = await this.readFiles();
2543
+ return files.length;
2354
2544
  }
2355
2545
  /**
2356
- * スキーマを正規化
2546
+ * 空かどうかを確認
2357
2547
  */
2358
- static normalizeSchema(schema) {
2359
- const normalized = {};
2360
- const factory = new DocSchemaFieldFactory();
2361
- for (const [key, field] of Object.entries(schema)) {
2362
- try {
2363
- const normalizedField = factory.fromUnknown(key, field);
2364
- normalized[key] = normalizedField.toJson();
2365
- } catch {
2366
- }
2367
- }
2368
- return normalized;
2548
+ async isEmpty() {
2549
+ const count = await this.count();
2550
+ return count === 0;
2369
2551
  }
2370
2552
  };
2371
2553
 
2372
2554
  // lib/values/doc-front-matter-index-value.ts
2555
+ import { parse as parse2, stringify as stringify3 } from "yaml";
2373
2556
  var DocFrontMatterIndexValue = class _DocFrontMatterIndexValue {
2374
2557
  constructor(value) {
2375
2558
  this.value = value;
@@ -2379,14 +2562,14 @@ var DocFrontMatterIndexValue = class _DocFrontMatterIndexValue {
2379
2562
  /**
2380
2563
  * アイコンを取得
2381
2564
  */
2382
- get icon() {
2565
+ icon() {
2383
2566
  return this.value.icon;
2384
2567
  }
2385
2568
  /**
2386
2569
  * スキーマを取得
2387
2570
  */
2388
- get schema() {
2389
- return new DocSchemaValue(this.value.schema);
2571
+ schema(zodSchema) {
2572
+ return new DocSchemaValue(this.value.schema, zodSchema);
2390
2573
  }
2391
2574
  /**
2392
2575
  * iconフィールドを更新
@@ -2760,7 +2943,8 @@ var DocFileIndexReference = class _DocFileIndexReference {
2760
2943
  async read() {
2761
2944
  const content = await this.fileSystem.readFile(this.filePath);
2762
2945
  if (content === null) {
2763
- const dirName = this.filePath.split("/").pop() || "directory";
2946
+ const dirPath = this.pathSystem.dirname(this.filePath);
2947
+ const dirName = this.pathSystem.basename(dirPath);
2764
2948
  const pathValue2 = DocFilePathValue.fromPathWithSystem(
2765
2949
  this.filePath,
2766
2950
  this.pathSystem,
@@ -3139,12 +3323,16 @@ var DocFileMdReference = class _DocFileMdReference {
3139
3323
  if (file instanceof Error) {
3140
3324
  return file;
3141
3325
  }
3142
- const relationValue = file.content.frontMatter.relation(key);
3326
+ const relationValue = file.content.frontMatter().relation(key);
3143
3327
  if (relationValue === null) {
3144
3328
  return null;
3145
3329
  }
3146
- const indexFile = await this.getDirectoryIndex();
3147
- 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) {
3148
3336
  return null;
3149
3337
  }
3150
3338
  const schemaData = indexFile.content.frontMatter.value.schema;
@@ -3156,7 +3344,12 @@ var DocFileMdReference = class _DocFileMdReference {
3156
3344
  if (!field || !("path" in field) || !field.path) {
3157
3345
  return null;
3158
3346
  }
3159
- 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`);
3160
3353
  return new _DocFileMdReference({
3161
3354
  path: fullPath,
3162
3355
  fileSystem: this.fileSystem,
@@ -3168,16 +3361,28 @@ var DocFileMdReference = class _DocFileMdReference {
3168
3361
  */
3169
3362
  async getDirectoryIndex() {
3170
3363
  const indexPath = this.pathSystem.join(this.directoryPath, "index.md");
3171
- const indexRef = new _DocFileMdReference({
3172
- path: indexPath,
3173
- fileSystem: this.fileSystem,
3174
- pathSystem: this.pathSystem
3175
- });
3176
- const indexFile = await indexRef.read();
3177
- if (indexFile instanceof Error) {
3178
- 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
+ });
3179
3384
  }
3180
- return indexFile;
3385
+ return null;
3181
3386
  }
3182
3387
  /**
3183
3388
  * 複数のリレーションを取得する
@@ -3193,12 +3398,16 @@ var DocFileMdReference = class _DocFileMdReference {
3193
3398
  if (file instanceof Error) {
3194
3399
  return [];
3195
3400
  }
3196
- const relationValues = file.content.frontMatter.multiRelation(key);
3401
+ const relationValues = file.content.frontMatter().multiRelation(key);
3197
3402
  if (relationValues.length === 0) {
3198
3403
  return [];
3199
3404
  }
3200
- const indexFile = await this.getDirectoryIndex();
3201
- 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) {
3202
3411
  return [];
3203
3412
  }
3204
3413
  const schemaData = indexFile.content.frontMatter.value.schema;
@@ -3211,7 +3420,12 @@ var DocFileMdReference = class _DocFileMdReference {
3211
3420
  return [];
3212
3421
  }
3213
3422
  return relationValues.map((relationValue) => {
3214
- 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`);
3215
3429
  return new _DocFileMdReference({
3216
3430
  path: fullPath,
3217
3431
  fileSystem: this.fileSystem,
@@ -3219,6 +3433,38 @@ var DocFileMdReference = class _DocFileMdReference {
3219
3433
  });
3220
3434
  });
3221
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
+ }
3222
3468
  };
3223
3469
 
3224
3470
  // lib/entities/doc-file-unknown-entity.ts
@@ -3562,6 +3808,17 @@ var DocDirectoryReference = class _DocDirectoryReference {
3562
3808
  }
3563
3809
  return files;
3564
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
+ }
3565
3822
  file(fileName) {
3566
3823
  const filePath = this.pathSystem.join(this.relativePath, fileName);
3567
3824
  if (fileName.endsWith(".md")) {
@@ -3603,6 +3860,21 @@ var DocDirectoryReference = class _DocDirectoryReference {
3603
3860
  }
3604
3861
  return entities;
3605
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
+ }
3606
3878
  async readUnknownFiles() {
3607
3879
  const entities = [];
3608
3880
  for await (const file of this.filesGenerator()) {
@@ -3914,7 +4186,12 @@ var DocFileTreeSystem = class {
3914
4186
  if (await indexFile.exists()) {
3915
4187
  const entity = await indexFile.read();
3916
4188
  title = entity.value.content.title || fileName;
3917
- 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
+ }
3918
4195
  }
3919
4196
  const children = await this.buildFileTree(filePath);
3920
4197
  return DocTreeDirectoryNodeValue.from({
@@ -3975,7 +4252,12 @@ var DocFileTreeSystem = class {
3975
4252
  if (await indexFile.exists()) {
3976
4253
  const entity = await indexFile.read();
3977
4254
  title = entity.value.content.title || fileName;
3978
- 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
+ }
3979
4261
  }
3980
4262
  const children = await this.buildDirectoryTree(filePath);
3981
4263
  return DocTreeDirectoryNodeValue.from({
@@ -4615,6 +4897,7 @@ export {
4615
4897
  zDocSchema,
4616
4898
  zDocSchemaField,
4617
4899
  zDocSchemaFieldBoolean,
4900
+ zDocSchemaFieldMinimal,
4618
4901
  zDocSchemaFieldMultiBoolean,
4619
4902
  zDocSchemaFieldMultiNumber,
4620
4903
  zDocSchemaFieldMultiRelation,
@@ -4627,6 +4910,7 @@ export {
4627
4910
  zDocSchemaFieldSelectText,
4628
4911
  zDocSchemaFieldText,
4629
4912
  zDocSchemaFieldType,
4913
+ zDocSchemaMinimal,
4630
4914
  zDocTreeDirectoryNode,
4631
4915
  zDocTreeFileNode,
4632
4916
  zDocTreeNode