@interactive-inc/docs-client 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -0,0 +1,448 @@
1
+ # @interactive-inc/docs-client
2
+
3
+ Markdown ベースのドキュメントを型安全に管理する TypeScript ライブラリ。
4
+
5
+ ## Features
6
+
7
+ - Markdown ファイルの読み書きと FrontMatter メタデータ管理
8
+ - Zod スキーマによる型安全なバリデーション
9
+ - ドキュメント間のリレーション
10
+ - 複数ストレージバックエンド(Node.js, GitHub, JSON, Mock)
11
+ - アーカイブ/リストア
12
+ - ファイルツリー生成
13
+ - `.safe` プロキシによるエラーハンドリング
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ vp add @interactive-inc/docs-client
19
+ ```
20
+
21
+ ## Development
22
+
23
+ ```bash
24
+ vp install
25
+ vp lint
26
+ vp fmt
27
+ vp test
28
+ vp run check
29
+ vp run build
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ```typescript
35
+ import { DocClient } from "@interactive-inc/docs-client"
36
+ import { DocFileSystemNode } from "@interactive-inc/docs-client/file-system-node"
37
+
38
+ const fileSystem = new DocFileSystemNode({ basePath: "./docs" })
39
+ const client = new DocClient({ fileSystem })
40
+
41
+ // .safe を使うと throw の代わりに Error を返す
42
+ const entity = await client.safe.mdFile("articles/hello.md").read()
43
+
44
+ if (!(entity instanceof Error)) {
45
+ console.log(entity.content().title)
46
+ console.log(entity.content().body)
47
+ }
48
+
49
+ // try/catch でも使える
50
+ try {
51
+ const entity = await client.mdFile("articles/hello.md").read()
52
+ console.log(entity.content().title)
53
+ } catch (error) {
54
+ console.error(error)
55
+ }
56
+ ```
57
+
58
+ ## Core Concepts
59
+
60
+ ### DocClient
61
+
62
+ メインのエントリポイント。
63
+
64
+ ```typescript
65
+ const client = new DocClient({
66
+ fileSystem,
67
+ config: {
68
+ defaultIndexIcon: "📃",
69
+ indexFileName: "index.md",
70
+ archiveDirectoryName: "_",
71
+ defaultDirectoryName: "Directory",
72
+ indexMetaIncludes: [],
73
+ directoryExcludes: [".vitepress"],
74
+ metaFileName: ".meta.json",
75
+ },
76
+ })
77
+ ```
78
+
79
+ ### References
80
+
81
+ ファイルやディレクトリへの遅延参照。`read()` を呼ぶまで I/O は発生しない。
82
+
83
+ ```typescript
84
+ const fileRef = client.mdFile("articles/hello.md")
85
+
86
+ const dirRef = client.directory("articles")
87
+
88
+ const indexRef = client.indexFile("articles")
89
+ ```
90
+
91
+ ### Entities
92
+
93
+ 読み取り後の不変データオブジェクト。
94
+
95
+ ```typescript
96
+ const entity = await fileRef.read()
97
+
98
+ const title = entity.content().title
99
+ const body = entity.content().body
100
+ const description = entity.content().description
101
+
102
+ const meta = entity.content().meta()
103
+
104
+ // with*() で新しいインスタンスを作成(不変)
105
+ const updated = entity.withTitle("New Title")
106
+ ```
107
+
108
+ ### Error Handling - `.safe` プロキシ
109
+
110
+ Reference や DocClient のメソッドは通常 throw でエラーを伝播する。`.safe` プロキシを使うと、全 async メソッドが `T | Error` を返す。
111
+
112
+ ```typescript
113
+ // throw する(try/catch が必要)
114
+ const entity = await fileRef.read()
115
+
116
+ // Error を返す(instanceof チェック)
117
+ const entity = await fileRef.safe.read()
118
+
119
+ if (entity instanceof Error) {
120
+ console.error(entity.message)
121
+ return
122
+ }
123
+
124
+ console.log(entity.content().title)
125
+ ```
126
+
127
+ ## File Operations
128
+
129
+ ### Reading Files
130
+
131
+ ```typescript
132
+ const fileRef = client.mdFile("articles/hello.md")
133
+ const entity = await fileRef.read()
134
+
135
+ // ディレクトリ内の全ファイルを読み取り
136
+ const dirRef = client.directory("articles")
137
+ const entities = await dirRef.readMdFiles()
138
+
139
+ // 生テキストとして読み取り
140
+ const text = await fileRef.readText()
141
+ ```
142
+
143
+ ### Writing Files
144
+
145
+ ```typescript
146
+ const entity = fileRef.empty()
147
+ const updated = entity.withTitle("Hello World").withDescription("My first article")
148
+ await fileRef.write(updated)
149
+
150
+ // 生テキストで書き込み
151
+ await fileRef.writeText("# Hello\n\nContent here")
152
+
153
+ // デフォルトコンテンツで新規ファイル作成
154
+ await dirRef.createMdFile("new-article.md")
155
+ ```
156
+
157
+ ### Archive and Restore
158
+
159
+ ファイルをアーカイブディレクトリ(デフォルト: `_/`)に移動。
160
+
161
+ ```typescript
162
+ const archivedRef = await fileRef.archive()
163
+
164
+ const restoredRef = await archivedRef.restore()
165
+ ```
166
+
167
+ ### File Information
168
+
169
+ ```typescript
170
+ const exists = await fileRef.exists()
171
+ const size = await fileRef.size()
172
+ const lastModified = await fileRef.lastModified()
173
+ const createdAt = await fileRef.createdAt()
174
+ ```
175
+
176
+ ## Schema Definition
177
+
178
+ FrontMatter メタデータの構造をスキーマで定義する。
179
+
180
+ ### Using .meta.json (Recommended)
181
+
182
+ ディレクトリに `.meta.json` を配置:
183
+
184
+ ```json
185
+ {
186
+ "icon": "📄",
187
+ "schema": {
188
+ "title": {
189
+ "type": "text",
190
+ "required": true,
191
+ "title": "Title",
192
+ "description": "Document title",
193
+ "default": null
194
+ },
195
+ "category": {
196
+ "type": "select-text",
197
+ "required": false,
198
+ "title": "Category",
199
+ "description": null,
200
+ "options": ["tutorial", "reference", "guide"],
201
+ "default": null
202
+ },
203
+ "tags": {
204
+ "type": "multi-text",
205
+ "required": false,
206
+ "title": "Tags",
207
+ "description": null,
208
+ "default": []
209
+ },
210
+ "author": {
211
+ "type": "relation",
212
+ "required": false,
213
+ "title": "Author",
214
+ "description": null,
215
+ "path": "authors",
216
+ "default": null
217
+ }
218
+ }
219
+ }
220
+ ```
221
+
222
+ ### Using index.md FrontMatter (Fallback)
223
+
224
+ `.meta.json` がなければ `index.md` の FrontMatter からスキーマを読み取る:
225
+
226
+ ```markdown
227
+ ---
228
+ icon: 📄
229
+ schema:
230
+ title:
231
+ type: text
232
+ required: true
233
+ category:
234
+ type: select-text
235
+ options:
236
+ - tutorial
237
+ - reference
238
+ ---
239
+
240
+ # Articles
241
+
242
+ Directory description here.
243
+ ```
244
+
245
+ ### Type-Safe Schema in Code
246
+
247
+ `defineSchema` で型安全にスキーマを定義:
248
+
249
+ ```typescript
250
+ import { defineSchema, docCustomSchemaField } from "@interactive-inc/docs-client"
251
+
252
+ const articleSchema = defineSchema({
253
+ title: { type: "text", required: true },
254
+ views: { type: "number", required: false },
255
+ published: { type: "boolean", required: true },
256
+ category: { type: "select-text", required: false },
257
+ tags: { type: "multi-text", required: false },
258
+ author: docCustomSchemaField.relation(false, "authors"),
259
+ relatedArticles: docCustomSchemaField.multiRelation(false, "articles"),
260
+ })
261
+
262
+ // スキーマ付きでディレクトリを取得
263
+ const dirRef = client.directory("articles", articleSchema)
264
+ const fileRef = dirRef.mdFile("hello")
265
+ const entity = await fileRef.read()
266
+
267
+ // 型安全なメタデータアクセス
268
+ const title = entity.content().meta().field("title") // string
269
+ const views = entity.content().meta().field("views") // number | null
270
+ ```
271
+
272
+ ### Schema Field Types
273
+
274
+ | Type | Value Type | Description |
275
+ | --------------------- | ---------- | -------------------------- |
276
+ | `text` | `string` | 単一テキスト |
277
+ | `number` | `number` | 数値 |
278
+ | `boolean` | `boolean` | 真偽値 |
279
+ | `select-text` | `string` | テキスト選択肢から単一選択 |
280
+ | `select-number` | `number` | 数値選択肢から単一選択 |
281
+ | `multi-text` | `string[]` | 複数テキスト |
282
+ | `multi-number` | `number[]` | 複数数値 |
283
+ | `multi-select-text` | `string[]` | テキスト選択肢から複数選択 |
284
+ | `multi-select-number` | `number[]` | 数値選択肢から複数選択 |
285
+ | `relation` | `string` | 他ドキュメントへの参照 |
286
+ | `multi-relation` | `string[]` | 他ドキュメントへの複数参照 |
287
+
288
+ ## Relations
289
+
290
+ ドキュメント間のリレーション。
291
+
292
+ ```typescript
293
+ const pageSchema = defineSchema({
294
+ features: docCustomSchemaField.multiRelation(true, "features"),
295
+ })
296
+
297
+ const dirRef = client.directory("pages", pageSchema)
298
+ const pageRef = dirRef.mdFile("dashboard")
299
+
300
+ // 関連ドキュメントの参照を取得
301
+ const featureRefs = await pageRef.relations("features")
302
+ for (const featureRef of featureRefs) {
303
+ const feature = await featureRef.read()
304
+ console.log(feature.content().title)
305
+ }
306
+ ```
307
+
308
+ ## Directory Operations
309
+
310
+ ```typescript
311
+ const dirRef = client.directory("articles")
312
+
313
+ // 一覧取得
314
+ const fileNames = await dirRef.fileNames()
315
+ const dirNames = await dirRef.directoryNames()
316
+
317
+ // 参照取得
318
+ const files = await dirRef.files()
319
+ const mdFiles = await dirRef.mdFiles()
320
+ const subdirs = await dirRef.directories()
321
+
322
+ // 全ファイル読み取り
323
+ const entities = await dirRef.readMdFiles()
324
+
325
+ // インデックスファイル読み取り
326
+ const indexEntity = await dirRef.readIndexFile()
327
+
328
+ // サブディレクトリ
329
+ const subDirRef = dirRef.directory("tutorials")
330
+
331
+ // 新規ファイル作成
332
+ const newFileRef = await dirRef.createMdFile("new-article.md")
333
+ ```
334
+
335
+ ## File Tree
336
+
337
+ ドキュメントの階層ツリー構造を生成。
338
+
339
+ ```typescript
340
+ const tree = await client.fileTree()
341
+
342
+ const dirTree = await client.directoryTree()
343
+
344
+ // ツリーノードの型
345
+ type DocTreeFileNode = {
346
+ type: "file"
347
+ name: string
348
+ path: string
349
+ icon: string
350
+ title: string
351
+ }
352
+
353
+ type DocTreeDirectoryNode = {
354
+ type: "directory"
355
+ name: string
356
+ path: string
357
+ icon: string
358
+ title: string
359
+ children: DocTreeNode[]
360
+ }
361
+
362
+ type DocTreeNode = DocTreeFileNode | DocTreeDirectoryNode
363
+ ```
364
+
365
+ ## File Systems
366
+
367
+ ### Node.js File System
368
+
369
+ ローカルファイル操作用:
370
+
371
+ ```typescript
372
+ import { DocFileSystemNode } from "@interactive-inc/docs-client/file-system-node"
373
+
374
+ const fileSystem = new DocFileSystemNode({ basePath: "./docs" })
375
+ ```
376
+
377
+ Read/Write を個別に指定する場合:
378
+
379
+ ```typescript
380
+ import {
381
+ DocFileSystemNodeRead,
382
+ DocFileSystemNodeWrite,
383
+ } from "@interactive-inc/docs-client/file-system-node"
384
+ import { DocFileSystem } from "@interactive-inc/docs-client/file-system"
385
+
386
+ const reader = new DocFileSystemNodeRead({ basePath: "./docs" })
387
+ const writer = new DocFileSystemNodeWrite({ basePath: "./docs", reader })
388
+
389
+ const fileSystem = new DocFileSystem({ basePath: "./docs", reader, writer })
390
+ ```
391
+
392
+ ### GitHub File System (Octokit)
393
+
394
+ GitHub リポジトリからの読み取り:
395
+
396
+ ```typescript
397
+ import { DocFileSystemOctokitRead } from "@interactive-inc/docs-client/file-system-octokit"
398
+ import { DocFileSystemNodeWrite } from "@interactive-inc/docs-client/file-system-node"
399
+ import { DocFileSystem } from "@interactive-inc/docs-client/file-system"
400
+
401
+ const reader = new DocFileSystemOctokitRead({
402
+ token: process.env.GITHUB_TOKEN,
403
+ owner: "your-org",
404
+ repo: "your-repo",
405
+ basePath: "docs",
406
+ branch: "main",
407
+ })
408
+
409
+ const writer = new DocFileSystemNodeWrite({
410
+ basePath: "/tmp/docs-output",
411
+ })
412
+
413
+ const fileSystem = new DocFileSystem({
414
+ basePath: "docs",
415
+ reader,
416
+ writer,
417
+ })
418
+ ```
419
+
420
+ ### JSON File System
421
+
422
+ インメモリ操作用:
423
+
424
+ ```typescript
425
+ import { DocFileSystemJson } from "@interactive-inc/docs-client/file-system-json"
426
+
427
+ const fileSystem = new DocFileSystemJson({
428
+ basePath: "docs",
429
+ data: {
430
+ "index.md": "# Home\n\nWelcome!",
431
+ "articles/hello.md": "# Hello\n\nContent here.",
432
+ },
433
+ })
434
+ ```
435
+
436
+ ### Mock File System
437
+
438
+ テスト用:
439
+
440
+ ```typescript
441
+ import { DocFileSystemMock } from "@interactive-inc/docs-client/file-system-mock"
442
+
443
+ const fileSystem = new DocFileSystemMock({ basePath: "docs" })
444
+ ```
445
+
446
+ ## License
447
+
448
+ MIT
@@ -1,9 +1,9 @@
1
- import { D as DocFileSystemInterface, a as DocPathSystem } from './doc-file-system.interface-C5vpYrOL.js';
1
+ import { D as DocFileSystemInterface, a as DocPathSystem } from './doc-file-system.interface-CmQywbuP.js';
2
2
 
3
3
  /**
4
4
  * Read-only file system interface for document operations
5
5
  */
6
- interface DocFileSystemReadInterface {
6
+ type DocFileSystemReadInterface = {
7
7
  /**
8
8
  * Read file content at the specified path
9
9
  */
@@ -68,12 +68,12 @@ interface DocFileSystemReadInterface {
68
68
  * Get file creation time
69
69
  */
70
70
  getFileCreatedTime(relativePath: string): Promise<Date | Error>;
71
- }
71
+ };
72
72
 
73
73
  /**
74
74
  * Write-only file system interface for document operations
75
75
  */
76
- interface DocFileSystemWriteInterface {
76
+ type DocFileSystemWriteInterface = {
77
77
  /**
78
78
  * Write content to file at the specified path (creates directory if needed)
79
79
  */
@@ -98,7 +98,7 @@ interface DocFileSystemWriteInterface {
98
98
  * Move file
99
99
  */
100
100
  moveFile(sourcePath: string, destinationPath: string): Promise<Error | null>;
101
- }
101
+ };
102
102
 
103
103
  type Props = {
104
104
  basePath: string;
@@ -3,6 +3,7 @@
3
3
  */
4
4
  declare class DocPathSystem {
5
5
  readonly separator = "/";
6
+ constructor();
6
7
  /**
7
8
  * Join paths
8
9
  */
@@ -41,7 +42,7 @@ declare class DocPathSystem {
41
42
  * Interface for DocFileSystem class
42
43
  * Defines all public methods available in DocFileSystem
43
44
  */
44
- interface DocFileSystemInterface {
45
+ type DocFileSystemInterface = {
45
46
  /**
46
47
  * Read file content at the specified path
47
48
  */
@@ -130,6 +131,6 @@ interface DocFileSystemInterface {
130
131
  * Get file creation time
131
132
  */
132
133
  getFileCreatedTime(relativePath: string): Promise<Date | Error>;
133
- }
134
+ };
134
135
 
135
136
  export { type DocFileSystemInterface as D, DocPathSystem as a };
@@ -1,6 +1,6 @@
1
1
  import { z } from 'zod';
2
- import { a as DocPathSystem } from './doc-file-system.interface-C5vpYrOL.js';
3
- import { D as DocFileSystem, a as DocFileSystemReadInterface, b as DocFileSystemWriteInterface } from './doc-file-system-DEmPPJPE.js';
2
+ import { a as DocPathSystem } from './doc-file-system.interface-CmQywbuP.js';
3
+ import { D as DocFileSystem, a as DocFileSystemReadInterface, b as DocFileSystemWriteInterface } from './doc-file-system-z2sEMkq0.js';
4
4
 
5
5
  declare const zJsonDocumentData$2: z.ZodRecord<z.ZodString, z.ZodString>;
6
6
  type JsonDocumentData$2 = z.infer<typeof zJsonDocumentData$2>;
@@ -49,8 +49,52 @@ declare class DocFileSystemJson extends DocFileSystem {
49
49
 
50
50
  declare const zJsonDocumentData$1: z.ZodRecord<z.ZodString, z.ZodString>;
51
51
  type JsonDocumentData$1 = z.infer<typeof zJsonDocumentData$1>;
52
+ type Props$2 = {
53
+ data?: JsonDocumentData$1 | unknown;
54
+ };
55
+ /**
56
+ * JSON ファイルシステムの可変データストア。reader と writer が同一インスタンスを共有し、
57
+ * writer の変更が reader に即時反映される単一の真実の源となる
58
+ */
59
+ declare class DocFileSystemJsonStore {
60
+ private documents;
61
+ constructor(props?: Props$2);
62
+ /**
63
+ * 指定パスのドキュメント内容を取得する
64
+ */
65
+ get(path: string): string | undefined;
66
+ /**
67
+ * 指定パスにドキュメント内容を書き込む
68
+ */
69
+ set(path: string, content: string): void;
70
+ /**
71
+ * 指定パスを削除する。存在すれば true を返す
72
+ */
73
+ delete(path: string): boolean;
74
+ /**
75
+ * 指定パスが存在するかを返す
76
+ */
77
+ has(path: string): boolean;
78
+ /**
79
+ * 全ドキュメントのパス一覧を返す
80
+ */
81
+ keys(): string[];
82
+ /**
83
+ * データ全体を入れ替える
84
+ */
85
+ replace(data: JsonDocumentData$1 | unknown): Error | null;
86
+ /**
87
+ * 全データを削除する
88
+ */
89
+ clear(): void;
90
+ /**
91
+ * 現在のデータの読み取り専用コピーを返す
92
+ */
93
+ toData(): JsonDocumentData$1;
94
+ }
95
+
52
96
  type Props$1 = {
53
- data: JsonDocumentData$1 | unknown;
97
+ store: DocFileSystemJsonStore;
54
98
  basePath?: string;
55
99
  pathSystem?: DocPathSystem;
56
100
  };
@@ -58,7 +102,7 @@ type Props$1 = {
58
102
  * JSON-based read-only file system implementation
59
103
  */
60
104
  declare class DocFileSystemJsonRead implements DocFileSystemReadInterface {
61
- private readonly data;
105
+ private readonly store;
62
106
  private readonly basePath;
63
107
  private readonly pathSystem;
64
108
  constructor(props: Props$1);
@@ -133,7 +177,7 @@ declare class DocFileSystemJsonRead implements DocFileSystemReadInterface {
133
177
  /**
134
178
  * Get the raw JSON data
135
179
  */
136
- getRawData(): JsonDocumentData$1;
180
+ getRawData(): Record<string, string>;
137
181
  /**
138
182
  * Normalize path by removing leading/trailing slashes
139
183
  */
@@ -151,8 +195,7 @@ declare class DocFileSystemJsonRead implements DocFileSystemReadInterface {
151
195
  declare const zJsonDocumentData: z.ZodRecord<z.ZodString, z.ZodString>;
152
196
  type JsonDocumentData = z.infer<typeof zJsonDocumentData>;
153
197
  type Props = {
154
- data?: JsonDocumentData | unknown;
155
- basePath?: string;
198
+ store: DocFileSystemJsonStore;
156
199
  pathSystem?: DocPathSystem;
157
200
  reader?: DocFileSystemReadInterface;
158
201
  onDataChange?: (data: JsonDocumentData) => void;
@@ -161,12 +204,11 @@ type Props = {
161
204
  * JSON-based write-only file system implementation
162
205
  */
163
206
  declare class DocFileSystemJsonWrite implements DocFileSystemWriteInterface {
164
- private data;
165
- private readonly basePath;
207
+ private readonly store;
166
208
  private readonly pathSystem;
167
209
  private readonly reader?;
168
210
  private readonly onDataChange?;
169
- constructor(props?: Props);
211
+ constructor(props: Props);
170
212
  /**
171
213
  * Write content to JSON data
172
214
  */