@luzzle/core 0.0.64 → 0.0.66

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.
@@ -25,6 +25,19 @@ export type PieceSyncResult = {
25
25
  error: true;
26
26
  message: string;
27
27
  };
28
+ export type PieceDiffResult<F extends PieceFrontmatter> = {
29
+ action: 'added';
30
+ file: string;
31
+ markdown: PieceMarkdown<F>;
32
+ } | {
33
+ action: 'updated';
34
+ file: string;
35
+ markdown: PieceMarkdown<F>;
36
+ dbPiece: LuzzleSelectable<'pieces_items'>;
37
+ } | {
38
+ action: 'skipped';
39
+ file: string;
40
+ };
28
41
  declare class Piece<F extends PieceFrontmatter> {
29
42
  private _validator?;
30
43
  private _schema;
@@ -47,16 +60,22 @@ declare class Piece<F extends PieceFrontmatter> {
47
60
  };
48
61
  get(file: string): Promise<PieceMarkdown<F>>;
49
62
  write(markdown: PieceMarkdown<F>): Promise<void>;
50
- prune(db: LuzzleDatabase, files: string[], options?: {
51
- dryRun: boolean;
52
- }): Promise<Readable & AsyncIterable<PiecePruneResult>>;
63
+ private findPrunable;
64
+ diffPrune(db: LuzzleDatabase, files: string[]): Promise<string[]>;
65
+ prune(db: LuzzleDatabase, files: string[]): Promise<Readable & AsyncIterable<PiecePruneResult>>;
66
+ private getChange;
67
+ diffFile(db: LuzzleDatabase, file: string, options?: {
68
+ force?: boolean;
69
+ }): Promise<PieceDiffResult<F>>;
70
+ diff(db: LuzzleDatabase, files: string[], options?: {
71
+ force?: boolean;
72
+ }): Promise<Readable & AsyncIterable<PieceDiffResult<F>>>;
53
73
  sync(db: LuzzleDatabase, files: string[], options?: {
54
- dryRun?: boolean;
55
74
  force?: boolean;
56
75
  }): Promise<Readable & AsyncIterable<PieceSyncResult>>;
57
- syncMarkdownAdd(db: LuzzleDatabase, markdown: PieceMarkdown<F>): Promise<void>;
76
+ syncMarkdownAdd(db: LuzzleDatabase, markdown: PieceMarkdown<F>, hash?: string): Promise<void>;
58
77
  syncMarkdown(db: LuzzleDatabase, markdown: PieceMarkdown<F>): Promise<void>;
59
- syncMarkdownUpdate(db: LuzzleDatabase, markdown: PieceMarkdown<F>, data: LuzzleSelectable<'pieces_items'>): Promise<void>;
78
+ syncMarkdownUpdate(db: LuzzleDatabase, markdown: PieceMarkdown<F>, data: LuzzleSelectable<'pieces_items'>, hash?: string): Promise<void>;
60
79
  toMarkdown(data: LuzzleSelectable<'pieces_items'>): PieceMarkdown<F>;
61
80
  getField(markdown: PieceMarkdown<F>, fieldPath: string): PieceFrontMatterValue | undefined;
62
81
  setFields(markdown: PieceMarkdown<F>, fields: Record<string, unknown>): Promise<PieceMarkdown<F>>;
@@ -99,17 +99,22 @@ class Piece {
99
99
  throw new Error(`Could not write ${markdown.filePath} due to\n\n: ${validated.errors.join('\n')}`);
100
100
  }
101
101
  }
102
- async prune(db, files, options) {
102
+ async findPrunable(db, files) {
103
103
  const dbPieces = await selectItems(db, { type: this._pieceName });
104
104
  const diskPiecesSet = new Set(files);
105
- const missingPieces = dbPieces.filter((piece) => !diskPiecesSet.has(piece.file_path));
106
- const missingFiles = missingPieces.map((piece) => piece.file_path);
105
+ return dbPieces
106
+ .filter((piece) => !diskPiecesSet.has(piece.file_path))
107
+ .map((piece) => piece.file_path);
108
+ }
109
+ async diffPrune(db, files) {
110
+ return this.findPrunable(db, files);
111
+ }
112
+ async prune(db, files) {
113
+ const missingFiles = await this.findPrunable(db, files);
107
114
  const stream = Readable.from(missingFiles);
108
115
  return stream.map(async (file) => {
109
116
  try {
110
- if (!options?.dryRun) {
111
- await deleteItem(db, file);
112
- }
117
+ await deleteItem(db, file);
113
118
  return { action: 'pruned', file };
114
119
  }
115
120
  catch (error) {
@@ -117,45 +122,63 @@ class Piece {
117
122
  }
118
123
  }, { concurrency: cpus().length });
119
124
  }
125
+ async getChange(db, file, options) {
126
+ const outdated = await this.isOutdated(file, db);
127
+ if (!options?.force && !outdated) {
128
+ return { result: { action: 'skipped', file } };
129
+ }
130
+ const markdown = await this.get(file);
131
+ const dbPiece = await selectItem(db, markdown.filePath);
132
+ const readStream = this._storage.createReadStream(markdown.filePath);
133
+ const hash = await calculateHashFromFile(readStream);
134
+ if (!dbPiece) {
135
+ return { result: { action: 'added', file, markdown }, hash };
136
+ }
137
+ const cache = await getCache(db, markdown.filePath);
138
+ if (options?.force || cache?.content_hash !== hash) {
139
+ return { result: { action: 'updated', file, markdown, dbPiece }, hash };
140
+ }
141
+ return { result: { action: 'skipped', file }, hash };
142
+ }
143
+ async diffFile(db, file, options) {
144
+ return (await this.getChange(db, file, options)).result;
145
+ }
146
+ async diff(db, files, options) {
147
+ const stream = Readable.from(files);
148
+ return stream.map((file) => this.diffFile(db, file, options), {
149
+ concurrency: cpus().length,
150
+ });
151
+ }
120
152
  async sync(db, files, options) {
121
153
  const stream = Readable.from(files);
122
154
  return stream.map(async (file) => {
123
155
  try {
124
- const markdown = await this.get(file);
125
- const dbPiece = await selectItem(db, markdown.filePath);
126
- if (dbPiece) {
127
- const readStream = this._storage.createReadStream(markdown.filePath);
128
- const newHash = await calculateHashFromFile(readStream);
129
- const cache = await getCache(db, markdown.filePath);
130
- if (options?.force || cache?.content_hash !== newHash) {
131
- if (!options?.dryRun) {
132
- await this.syncMarkdownUpdate(db, markdown, dbPiece);
133
- }
134
- return { action: 'updated', file };
135
- }
136
- if (!options?.dryRun) {
137
- await updateCache(db, markdown.filePath, newHash);
138
- }
139
- return { action: 'skipped', file };
156
+ const { result, hash } = await this.getChange(db, file, options);
157
+ if (result.action === 'added') {
158
+ await this.syncMarkdownAdd(db, result.markdown, hash);
140
159
  }
141
- else {
142
- if (!options?.dryRun) {
143
- await this.syncMarkdownAdd(db, markdown);
144
- }
145
- return { action: 'added', file };
160
+ else if (result.action === 'updated') {
161
+ await this.syncMarkdownUpdate(db, result.markdown, result.dbPiece, hash);
162
+ }
163
+ else if (hash) {
164
+ await updateCache(db, file, hash);
146
165
  }
166
+ return { action: result.action, file };
147
167
  }
148
168
  catch (error) {
149
169
  return { file, error: true, message: `error syncing piece: ${error}` };
150
170
  }
151
171
  }, { concurrency: cpus().length });
152
172
  }
153
- async syncMarkdownAdd(db, markdown) {
173
+ async syncMarkdownAdd(db, markdown, hash) {
154
174
  const createInput = makePieceItemInsertable(this._pieceName, markdown, this._schema);
155
- const readStream = this._storage.createReadStream(markdown.filePath);
156
- const hash = await calculateHashFromFile(readStream);
175
+ let contentHash = hash;
176
+ if (contentHash === undefined) {
177
+ const readStream = this._storage.createReadStream(markdown.filePath);
178
+ contentHash = await calculateHashFromFile(readStream);
179
+ }
157
180
  await insertItem(db, createInput);
158
- await addCache(db, markdown.filePath, hash);
181
+ await addCache(db, markdown.filePath, contentHash);
159
182
  }
160
183
  async syncMarkdown(db, markdown) {
161
184
  const dbPiece = await selectItem(db, markdown.filePath);
@@ -166,12 +189,15 @@ class Piece {
166
189
  await this.syncMarkdownAdd(db, markdown);
167
190
  }
168
191
  }
169
- async syncMarkdownUpdate(db, markdown, data) {
192
+ async syncMarkdownUpdate(db, markdown, data, hash) {
170
193
  const updateInput = makePieceItemUpdatable(markdown, this._schema, data, false);
171
194
  await updateItem(db, markdown.filePath, updateInput);
172
- const readStream = this._storage.createReadStream(markdown.filePath);
173
- const hash = await calculateHashFromFile(readStream);
174
- await updateCache(db, data.file_path, hash);
195
+ let contentHash = hash;
196
+ if (contentHash === undefined) {
197
+ const readStream = this._storage.createReadStream(markdown.filePath);
198
+ contentHash = await calculateHashFromFile(readStream);
199
+ }
200
+ await updateCache(db, data.file_path, contentHash);
175
201
  }
176
202
  toMarkdown(data) {
177
203
  const frontmatter = JSON.parse(data.frontmatter_json);
@@ -1 +1 @@
1
- {"version":3,"file":"Piece.js","sourceRoot":"","sources":["../../../src/pieces/Piece.ts"],"names":[],"mappings":"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","sourcesContent":["import { updateCache, addCache, getCache } from './cache.js'\nimport { cpus } from 'os'\nimport path from 'path'\nimport slugify from '@sindresorhus/slugify'\nimport { Readable } from 'stream'\nimport {\n\tdatabaseValueToPieceFrontmatterValue,\n\tgetPieceFrontmatterSchemaFields,\n\tinitializePieceFrontMatter,\n\tPieceFrontmatter,\n\tPieceFrontmatterSchema,\n\tPieceFrontmatterSchemaField,\n\tPieceFrontMatterValue,\n} from './utils/frontmatter.js'\nimport {\n\tfindFrontmatterField,\n\tsetFrontmatterValue,\n\tunsetFrontmatterValue,\n\tgetFrontmatterValue,\n} from './utils/frontmatter.path.js'\nimport LuzzleStorage from '../storage/abstract.js'\nimport { makePieceMarkdown, makePieceMarkdownString, PieceMarkdown } from './utils/markdown.js'\nimport {\n\tisAttachableStream,\n\tcalculateHashFromFile,\n\tsavePieceFieldAsset,\n\tmakePieceValue,\n} from './utils/piece.js'\nimport { LuzzleDatabase, LuzzleSelectable } from '../database/tables/index.js'\nimport compile from '../lib/ajv.js'\nimport {\n\tgetValidatePieceItemErrors,\n\tmakePieceItemInsertable,\n\tmakePieceItemUpdatable,\n\tvalidatePieceItem,\n} from './item.js'\nimport { extractFullMarkdown } from '../lib/markdown.js'\nimport { deleteItem, insertItem, selectItem, selectItems, updateItem } from './items.js'\nimport { LUZZLE_PIECE_FILE_EXTENSION } from './assets.js'\n\nexport interface InterfacePiece<F extends PieceFrontmatter> {\n\tnew(directory: string, pieceName: string, schemaOverride?: PieceFrontmatterSchema<F>): Piece<F>\n}\n\nexport type PiecePruneResult =\n\t| {\n\t\taction: 'pruned'\n\t\tfile: string\n\t\terror?: false\n\t}\n\t| {\n\t\tfile: string\n\t\terror: true\n\t\tmessage: string\n\t}\n\nexport type PieceSyncResult =\n\t| {\n\t\taction: 'added' | 'updated' | 'skipped'\n\t\tfile: string\n\t\terror?: false\n\t}\n\t| {\n\t\tfile: string\n\t\terror: true\n\t\tmessage: string\n\t}\n\nclass Piece<F extends PieceFrontmatter> {\n\tprivate _validator?: ReturnType<typeof compile<F>>\n\tprivate _schema: PieceFrontmatterSchema<F>\n\tprivate _storage: LuzzleStorage\n\tprivate _pieceName: string\n\tprivate _fields?: Array<PieceFrontmatterSchemaField>\n\n\tconstructor(pieceName: string, storage: LuzzleStorage, schema: PieceFrontmatterSchema<F>) {\n\t\tthis._storage = storage\n\t\tthis._schema = schema\n\t\tthis._pieceName = pieceName\n\n\t\tif (this._pieceName !== this._schema.title) {\n\t\t\tthrow new Error(`${pieceName} does not match the schema title: ${this._schema.title}`)\n\t\t}\n\t}\n\n\tasync create(directory: string, name: string): Promise<PieceMarkdown<F>> {\n\t\tconst slug = slugify(name)\n\t\tconst filename = `${slug}.${this.type}.${LUZZLE_PIECE_FILE_EXTENSION}`\n\t\tconst file = path.join(directory, filename)\n\t\tconst exists = await this._storage.exists(file)\n\n\t\tif (exists) {\n\t\t\tthrow new Error(`file already exists: ${file}`)\n\t\t}\n\n\t\tconst frontmatter = initializePieceFrontMatter(this._schema, true) as F\n\t\treturn makePieceMarkdown(file, this._pieceName, undefined, frontmatter)\n\t}\n\n\tasync delete(file: string) {\n\t\tconst exists = await this._storage.exists(file)\n\n\t\tif (exists) {\n\t\t\tawait this._storage.delete(file)\n\t\t\treturn\n\t\t}\n\n\t\tthrow new Error(`${file} does not exist`)\n\t}\n\n\tget type() {\n\t\treturn this._pieceName\n\t}\n\n\tget schema() {\n\t\treturn this._schema\n\t}\n\n\tprotected get validator(): ReturnType<typeof compile<F>> {\n\t\tthis._validator = this._validator || compile<F>(this._schema)\n\t\treturn this._validator\n\t}\n\n\tget fields() {\n\t\tthis._fields = this._fields || getPieceFrontmatterSchemaFields(this._schema)\n\t\treturn this._fields\n\t}\n\n\tasync isOutdated(file: string, db: LuzzleDatabase): Promise<boolean> {\n\t\tconst fileStat = await this._storage.stat(file).catch(() => null)\n\n\t\tif (fileStat) {\n\t\t\tconst cache = await getCache(db, file)\n\t\t\tconst cachedDate = cache?.date_updated || cache?.date_added\n\n\t\t\treturn !cachedDate || fileStat.last_modified.getTime() > cachedDate\n\t\t}\n\n\t\tthrow new Error(`${file} does not exist`)\n\t}\n\n\tvalidate(markdown: PieceMarkdown<F>): { isValid: true } | { isValid: false; errors: string[] } {\n\t\tconst valid = validatePieceItem(markdown, this.validator)\n\n\t\tif (valid) {\n\t\t\treturn { isValid: true }\n\t\t} else {\n\t\t\tconst errors = getValidatePieceItemErrors(this.validator)\n\t\t\treturn { isValid: false, errors }\n\t\t}\n\t}\n\n\tasync get(file: string): Promise<PieceMarkdown<F>> {\n\t\tconst exists = await this._storage.exists(file)\n\n\t\tif (exists) {\n\t\t\tconst contents = await this._storage.readFile(file, 'text')\n\t\t\tconst data = await extractFullMarkdown(contents)\n\n\t\t\tif (!/^\\//.test(file)) {\n\t\t\t\treturn makePieceMarkdown(file, this._pieceName, data.markdown, data.frontmatter as F)\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(`${file} does not exist`)\n\t}\n\n\tasync write(markdown: PieceMarkdown<F>): Promise<void> {\n\t\tconst validated = this.validate(markdown)\n\n\t\tif (validated.isValid) {\n\t\t\tconst markdownString = makePieceMarkdownString(markdown)\n\t\t\tawait this._storage.writeFile(markdown.filePath, markdownString)\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not write ${markdown.filePath} due to\\n\\n: ${validated.errors.join('\\n')}`\n\t\t\t)\n\t\t}\n\t}\n\n\tasync prune(db: LuzzleDatabase, files: string[], options?: { dryRun: boolean }) {\n\t\tconst dbPieces = await selectItems(db, { type: this._pieceName })\n\t\tconst diskPiecesSet = new Set<string>(files)\n\t\tconst missingPieces = dbPieces.filter((piece) => !diskPiecesSet.has(piece.file_path))\n\t\tconst missingFiles = missingPieces.map((piece) => piece.file_path)\n\t\tconst stream = Readable.from(missingFiles)\n\n\t\treturn stream.map(\n\t\t\tasync (file: string): Promise<PiecePruneResult> => {\n\t\t\t\ttry {\n\t\t\t\t\tif (!options?.dryRun) {\n\t\t\t\t\t\tawait deleteItem(db, file)\n\t\t\t\t\t}\n\t\t\t\t\treturn { action: 'pruned', file }\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { file, error: true, message: `error pruning piece: ${error}` }\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ concurrency: cpus().length }\n\t\t) as Readable & AsyncIterable<PiecePruneResult>\n\t}\n\n\tasync sync(db: LuzzleDatabase, files: string[], options?: { dryRun?: boolean; force?: boolean }) {\n\t\tconst stream = Readable.from(files)\n\n\t\treturn stream.map(\n\t\t\tasync (file: string): Promise<PieceSyncResult> => {\n\t\t\t\ttry {\n\t\t\t\t\tconst markdown = await this.get(file)\n\t\t\t\t\tconst dbPiece = await selectItem(db, markdown.filePath)\n\n\t\t\t\t\tif (dbPiece) {\n\t\t\t\t\t\tconst readStream = this._storage.createReadStream(markdown.filePath)\n\t\t\t\t\t\tconst newHash = await calculateHashFromFile(readStream)\n\t\t\t\t\t\tconst cache = await getCache(db, markdown.filePath)\n\t\t\t\t\t\tif (options?.force || cache?.content_hash !== newHash) {\n\t\t\t\t\t\t\tif (!options?.dryRun) {\n\t\t\t\t\t\t\t\tawait this.syncMarkdownUpdate(db, markdown, dbPiece)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn { action: 'updated', file }\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t\tif (!options?.dryRun) {\n\t\t\t\t\t\t\tawait updateCache(db, markdown.filePath, newHash)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn { action: 'skipped', file }\n\t\t\t\t\t} else {\n\t\t\t\t\t\tif (!options?.dryRun) {\n\t\t\t\t\t\t\tawait this.syncMarkdownAdd(db, markdown)\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn { action: 'added', file }\n\t\t\t\t\t}\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { file, error: true, message: `error syncing piece: ${error}` }\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ concurrency: cpus().length }\n\t\t) as Readable & AsyncIterable<PieceSyncResult>\n\t}\n\n\tasync syncMarkdownAdd(db: LuzzleDatabase, markdown: PieceMarkdown<F>): Promise<void> {\n\t\tconst createInput = makePieceItemInsertable(this._pieceName, markdown, this._schema)\n\t\tconst readStream = this._storage.createReadStream(markdown.filePath)\n\t\tconst hash = await calculateHashFromFile(readStream)\n\n\t\tawait insertItem(db, createInput)\n\t\tawait addCache(db, markdown.filePath, hash)\n\t}\n\n\tasync syncMarkdown(db: LuzzleDatabase, markdown: PieceMarkdown<F>): Promise<void> {\n\t\tconst dbPiece = await selectItem(db, markdown.filePath)\n\n\t\tif (dbPiece) {\n\t\t\tawait this.syncMarkdownUpdate(db, markdown, dbPiece)\n\t\t} else {\n\t\t\tawait this.syncMarkdownAdd(db, markdown)\n\t\t}\n\t}\n\n\tasync syncMarkdownUpdate(\n\t\tdb: LuzzleDatabase,\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tdata: LuzzleSelectable<'pieces_items'>\n\t): Promise<void> {\n\t\tconst updateInput = makePieceItemUpdatable(markdown, this._schema, data, false)\n\t\tawait updateItem(db, markdown.filePath, updateInput)\n\t\tconst readStream = this._storage.createReadStream(markdown.filePath)\n\t\tconst hash = await calculateHashFromFile(readStream)\n\n\t\tawait updateCache(db, data.file_path, hash)\n\t}\n\n\ttoMarkdown(data: LuzzleSelectable<'pieces_items'>): PieceMarkdown<F> {\n\t\tconst frontmatter = JSON.parse(data.frontmatter_json)\n\t\tconst frontmatterJson: Record<string, unknown> = {}\n\t\tconst dataKeys = Object.keys(frontmatter)\n\t\tconst fields = getPieceFrontmatterSchemaFields(this._schema).filter(\n\t\t\t(f): f is PieceFrontmatterSchemaField & { name: string } =>\n\t\t\t\t!!f.name && dataKeys.includes(f.name)\n\t\t)\n\n\t\tfields.forEach((field) => {\n\t\t\tconst name = field.name\n\t\t\tconst value = frontmatter[name]\n\n\t\t\tfrontmatterJson[name] = databaseValueToPieceFrontmatterValue(value, field)\n\t\t})\n\n\t\treturn makePieceMarkdown(data.file_path, data.type, data.note_markdown, frontmatterJson as F)\n\t}\n\n\tgetField(markdown: PieceMarkdown<F>, fieldPath: string): PieceFrontMatterValue | undefined {\n\t\treturn getFrontmatterValue(markdown.frontmatter, fieldPath)\n\t}\n\n\tasync setFields(\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tfields: Record<string, unknown>\n\t): Promise<PieceMarkdown<F>> {\n\t\tlet updatedMarkdown = markdown\n\n\t\tfor (const fieldname in fields) {\n\t\t\tupdatedMarkdown = await this.setField(updatedMarkdown, fieldname, fields[fieldname])\n\t\t}\n\n\t\treturn updatedMarkdown\n\t}\n\n\tasync removeFields(\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tfields: string[]\n\t): Promise<PieceMarkdown<Omit<F, keyof F>>> {\n\t\tlet updatedMarkdown = markdown as unknown as PieceMarkdown<Omit<F, keyof F>>\n\n\t\tfor (const field of fields) {\n\t\t\tupdatedMarkdown = (await this.removeField(\n\t\t\t\tupdatedMarkdown as unknown as PieceMarkdown<F>,\n\t\t\t\tfield\n\t\t\t)) as unknown as PieceMarkdown<Omit<F, keyof F>>\n\t\t}\n\n\t\treturn updatedMarkdown\n\t}\n\n\tasync setField(\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tfieldPath: string,\n\t\tvalue: unknown\n\t): Promise<PieceMarkdown<F>> {\n\t\tconst pieceField = findFrontmatterField(this.fields, fieldPath)\n\n\t\tif (!pieceField) {\n\t\t\tthrow new Error(`${fieldPath} is not a field in ${this._pieceName} ${markdown.filePath}`)\n\t\t}\n\n\t\tconst isArray = pieceField.type === 'array'\n\t\tconst itemField = isArray ? { ...pieceField.items, name: pieceField.name } : pieceField\n\t\tconst values = Array.isArray(value) ? value : [value]\n\n\t\tconst updatedFrontmatter = structuredClone(markdown.frontmatter)\n\n\t\ttry {\n\t\t\tif (isArray) {\n\t\t\t\tconst current = getFrontmatterValue(updatedFrontmatter, fieldPath)\n\t\t\t\tif (!Array.isArray(current)) {\n\t\t\t\t\tsetFrontmatterValue(updatedFrontmatter, fieldPath, [] as unknown as PieceFrontMatterValue)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const one of values) {\n\t\t\t\tconst pieceValue = await makePieceValue(itemField, one)\n\n\t\t\t\tconst val = isAttachableStream(pieceValue)\n\t\t\t\t\t? await savePieceFieldAsset(\n\t\t\t\t\t\tmarkdown.filePath,\n\t\t\t\t\t\titemField as PieceFrontmatterSchemaField,\n\t\t\t\t\t\tpieceValue,\n\t\t\t\t\t\tthis._storage\n\t\t\t\t\t)\n\t\t\t\t\t: (pieceValue as PieceFrontMatterValue)\n\n\t\t\t\tsetFrontmatterValue(updatedFrontmatter, fieldPath, val)\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconst error = e as Error\n\t\t\tconsole.error(`could not set field ${fieldPath}: ${error.message}`)\n\t\t\treturn markdown\n\t\t}\n\n\t\treturn makePieceMarkdown(markdown.filePath, markdown.piece, markdown.note, updatedFrontmatter)\n\t}\n\n\tasync removeField(\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tfieldPath: string,\n\t\tvalue?: number | string | boolean\n\t): Promise<PieceMarkdown<F>> {\n\t\tconst pieceField = findFrontmatterField(this.fields, fieldPath)\n\n\t\tif (!pieceField) {\n\t\t\tthrow new Error(`${fieldPath} is not a field in ${this._pieceName} ${markdown.filePath}`)\n\t\t}\n\n\t\tif (pieceField.nullable !== true) {\n\t\t\tthrow new Error(`${fieldPath} is a required field in ${this._pieceName} ${markdown.filePath}`)\n\t\t}\n\n\t\tconst updatedFrontmatter = structuredClone(markdown.frontmatter)\n\n\t\tif (value === undefined) {\n\t\t\tunsetFrontmatterValue(updatedFrontmatter, fieldPath)\n\t\t} else {\n\t\t\tconst pieceValue = await makePieceValue(pieceField, value)\n\t\t\tconst current = getFrontmatterValue(updatedFrontmatter, fieldPath)\n\n\t\t\tif (Array.isArray(current) && !isAttachableStream(pieceValue)) {\n\t\t\t\tconst index = current.indexOf(pieceValue as PieceFrontMatterValue)\n\t\t\t\tif (index !== -1) {\n\t\t\t\t\tunsetFrontmatterValue(updatedFrontmatter, `${fieldPath}.${index}`)\n\t\t\t\t} else {\n\t\t\t\t\treturn markdown\n\t\t\t\t}\n\t\t\t} else if (current === pieceValue) {\n\t\t\t\tunsetFrontmatterValue(updatedFrontmatter, fieldPath)\n\t\t\t}\n\t\t}\n\n\t\treturn makePieceMarkdown(markdown.filePath, markdown.piece, markdown.note, updatedFrontmatter)\n\t}\n}\n\nexport default Piece\n"]}
1
+ {"version":3,"file":"Piece.js","sourceRoot":"","sources":["../../../src/pieces/Piece.ts"],"names":[],"mappings":"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","sourcesContent":["import { updateCache, addCache, getCache } from './cache.js'\nimport { cpus } from 'os'\nimport path from 'path'\nimport slugify from '@sindresorhus/slugify'\nimport { Readable } from 'stream'\nimport {\n\tdatabaseValueToPieceFrontmatterValue,\n\tgetPieceFrontmatterSchemaFields,\n\tinitializePieceFrontMatter,\n\tPieceFrontmatter,\n\tPieceFrontmatterSchema,\n\tPieceFrontmatterSchemaField,\n\tPieceFrontMatterValue,\n} from './utils/frontmatter.js'\nimport {\n\tfindFrontmatterField,\n\tsetFrontmatterValue,\n\tunsetFrontmatterValue,\n\tgetFrontmatterValue,\n} from './utils/frontmatter.path.js'\nimport LuzzleStorage from '../storage/abstract.js'\nimport { makePieceMarkdown, makePieceMarkdownString, PieceMarkdown } from './utils/markdown.js'\nimport {\n\tisAttachableStream,\n\tcalculateHashFromFile,\n\tsavePieceFieldAsset,\n\tmakePieceValue,\n} from './utils/piece.js'\nimport { LuzzleDatabase, LuzzleSelectable } from '../database/tables/index.js'\nimport compile from '../lib/ajv.js'\nimport {\n\tgetValidatePieceItemErrors,\n\tmakePieceItemInsertable,\n\tmakePieceItemUpdatable,\n\tvalidatePieceItem,\n} from './item.js'\nimport { extractFullMarkdown } from '../lib/markdown.js'\nimport { deleteItem, insertItem, selectItem, selectItems, updateItem } from './items.js'\nimport { LUZZLE_PIECE_FILE_EXTENSION } from './assets.js'\n\nexport interface InterfacePiece<F extends PieceFrontmatter> {\n\tnew(directory: string, pieceName: string, schemaOverride?: PieceFrontmatterSchema<F>): Piece<F>\n}\n\nexport type PiecePruneResult =\n\t| {\n\t\taction: 'pruned'\n\t\tfile: string\n\t\terror?: false\n\t}\n\t| {\n\t\tfile: string\n\t\terror: true\n\t\tmessage: string\n\t}\n\nexport type PieceSyncResult =\n\t| {\n\t\taction: 'added' | 'updated' | 'skipped'\n\t\tfile: string\n\t\terror?: false\n\t}\n\t| {\n\t\tfile: string\n\t\terror: true\n\t\tmessage: string\n\t}\n\nexport type PieceDiffResult<F extends PieceFrontmatter> =\n\t| { action: 'added'; file: string; markdown: PieceMarkdown<F> }\n\t| {\n\t\taction: 'updated'\n\t\tfile: string\n\t\tmarkdown: PieceMarkdown<F>\n\t\tdbPiece: LuzzleSelectable<'pieces_items'>\n\t}\n\t| { action: 'skipped'; file: string }\n\nclass Piece<F extends PieceFrontmatter> {\n\tprivate _validator?: ReturnType<typeof compile<F>>\n\tprivate _schema: PieceFrontmatterSchema<F>\n\tprivate _storage: LuzzleStorage\n\tprivate _pieceName: string\n\tprivate _fields?: Array<PieceFrontmatterSchemaField>\n\n\tconstructor(pieceName: string, storage: LuzzleStorage, schema: PieceFrontmatterSchema<F>) {\n\t\tthis._storage = storage\n\t\tthis._schema = schema\n\t\tthis._pieceName = pieceName\n\n\t\tif (this._pieceName !== this._schema.title) {\n\t\t\tthrow new Error(`${pieceName} does not match the schema title: ${this._schema.title}`)\n\t\t}\n\t}\n\n\tasync create(directory: string, name: string): Promise<PieceMarkdown<F>> {\n\t\tconst slug = slugify(name)\n\t\tconst filename = `${slug}.${this.type}.${LUZZLE_PIECE_FILE_EXTENSION}`\n\t\tconst file = path.join(directory, filename)\n\t\tconst exists = await this._storage.exists(file)\n\n\t\tif (exists) {\n\t\t\tthrow new Error(`file already exists: ${file}`)\n\t\t}\n\n\t\tconst frontmatter = initializePieceFrontMatter(this._schema, true) as F\n\t\treturn makePieceMarkdown(file, this._pieceName, undefined, frontmatter)\n\t}\n\n\tasync delete(file: string) {\n\t\tconst exists = await this._storage.exists(file)\n\n\t\tif (exists) {\n\t\t\tawait this._storage.delete(file)\n\t\t\treturn\n\t\t}\n\n\t\tthrow new Error(`${file} does not exist`)\n\t}\n\n\tget type() {\n\t\treturn this._pieceName\n\t}\n\n\tget schema() {\n\t\treturn this._schema\n\t}\n\n\tprotected get validator(): ReturnType<typeof compile<F>> {\n\t\tthis._validator = this._validator || compile<F>(this._schema)\n\t\treturn this._validator\n\t}\n\n\tget fields() {\n\t\tthis._fields = this._fields || getPieceFrontmatterSchemaFields(this._schema)\n\t\treturn this._fields\n\t}\n\n\tasync isOutdated(file: string, db: LuzzleDatabase): Promise<boolean> {\n\t\tconst fileStat = await this._storage.stat(file).catch(() => null)\n\n\t\tif (fileStat) {\n\t\t\tconst cache = await getCache(db, file)\n\t\t\tconst cachedDate = cache?.date_updated || cache?.date_added\n\n\t\t\treturn !cachedDate || fileStat.last_modified.getTime() > cachedDate\n\t\t}\n\n\t\tthrow new Error(`${file} does not exist`)\n\t}\n\n\tvalidate(markdown: PieceMarkdown<F>): { isValid: true } | { isValid: false; errors: string[] } {\n\t\tconst valid = validatePieceItem(markdown, this.validator)\n\n\t\tif (valid) {\n\t\t\treturn { isValid: true }\n\t\t} else {\n\t\t\tconst errors = getValidatePieceItemErrors(this.validator)\n\t\t\treturn { isValid: false, errors }\n\t\t}\n\t}\n\n\tasync get(file: string): Promise<PieceMarkdown<F>> {\n\t\tconst exists = await this._storage.exists(file)\n\n\t\tif (exists) {\n\t\t\tconst contents = await this._storage.readFile(file, 'text')\n\t\t\tconst data = await extractFullMarkdown(contents)\n\n\t\t\tif (!/^\\//.test(file)) {\n\t\t\t\treturn makePieceMarkdown(file, this._pieceName, data.markdown, data.frontmatter as F)\n\t\t\t}\n\t\t}\n\n\t\tthrow new Error(`${file} does not exist`)\n\t}\n\n\tasync write(markdown: PieceMarkdown<F>): Promise<void> {\n\t\tconst validated = this.validate(markdown)\n\n\t\tif (validated.isValid) {\n\t\t\tconst markdownString = makePieceMarkdownString(markdown)\n\t\t\tawait this._storage.writeFile(markdown.filePath, markdownString)\n\t\t} else {\n\t\t\tthrow new Error(\n\t\t\t\t`Could not write ${markdown.filePath} due to\\n\\n: ${validated.errors.join('\\n')}`\n\t\t\t)\n\t\t}\n\t}\n\n\tprivate async findPrunable(db: LuzzleDatabase, files: string[]): Promise<string[]> {\n\t\tconst dbPieces = await selectItems(db, { type: this._pieceName })\n\t\tconst diskPiecesSet = new Set<string>(files)\n\t\treturn dbPieces\n\t\t\t.filter((piece) => !diskPiecesSet.has(piece.file_path))\n\t\t\t.map((piece) => piece.file_path)\n\t}\n\n\tasync diffPrune(db: LuzzleDatabase, files: string[]): Promise<string[]> {\n\t\treturn this.findPrunable(db, files)\n\t}\n\n\tasync prune(db: LuzzleDatabase, files: string[]) {\n\t\tconst missingFiles = await this.findPrunable(db, files)\n\t\tconst stream = Readable.from(missingFiles)\n\n\t\treturn stream.map(\n\t\t\tasync (file: string): Promise<PiecePruneResult> => {\n\t\t\t\ttry {\n\t\t\t\t\tawait deleteItem(db, file)\n\t\t\t\t\treturn { action: 'pruned', file }\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { file, error: true, message: `error pruning piece: ${error}` }\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ concurrency: cpus().length }\n\t\t) as Readable & AsyncIterable<PiecePruneResult>\n\t}\n\n\tprivate async getChange(\n\t\tdb: LuzzleDatabase,\n\t\tfile: string,\n\t\toptions?: { force?: boolean }\n\t): Promise<{ result: PieceDiffResult<F>; hash?: string }> {\n\t\tconst outdated = await this.isOutdated(file, db)\n\n\t\tif (!options?.force && !outdated) {\n\t\t\treturn { result: { action: 'skipped', file } }\n\t\t}\n\n\t\tconst markdown = await this.get(file)\n\t\tconst dbPiece = await selectItem(db, markdown.filePath)\n\t\tconst readStream = this._storage.createReadStream(markdown.filePath)\n\t\tconst hash = await calculateHashFromFile(readStream)\n\n\t\tif (!dbPiece) {\n\t\t\treturn { result: { action: 'added', file, markdown }, hash }\n\t\t}\n\n\t\tconst cache = await getCache(db, markdown.filePath)\n\t\tif (options?.force || cache?.content_hash !== hash) {\n\t\t\treturn { result: { action: 'updated', file, markdown, dbPiece }, hash }\n\t\t}\n\n\t\treturn { result: { action: 'skipped', file }, hash }\n\t}\n\n\tasync diffFile(\n\t\tdb: LuzzleDatabase,\n\t\tfile: string,\n\t\toptions?: { force?: boolean }\n\t): Promise<PieceDiffResult<F>> {\n\t\treturn (await this.getChange(db, file, options)).result\n\t}\n\n\tasync diff(db: LuzzleDatabase, files: string[], options?: { force?: boolean }) {\n\t\tconst stream = Readable.from(files)\n\n\t\treturn stream.map((file: string) => this.diffFile(db, file, options), {\n\t\t\tconcurrency: cpus().length,\n\t\t}) as Readable & AsyncIterable<PieceDiffResult<F>>\n\t}\n\n\tasync sync(db: LuzzleDatabase, files: string[], options?: { force?: boolean }) {\n\t\tconst stream = Readable.from(files)\n\n\t\treturn stream.map(\n\t\t\tasync (file: string): Promise<PieceSyncResult> => {\n\t\t\t\ttry {\n\t\t\t\t\tconst { result, hash } = await this.getChange(db, file, options)\n\n\t\t\t\t\tif (result.action === 'added') {\n\t\t\t\t\t\tawait this.syncMarkdownAdd(db, result.markdown, hash)\n\t\t\t\t\t} else if (result.action === 'updated') {\n\t\t\t\t\t\tawait this.syncMarkdownUpdate(db, result.markdown, result.dbPiece, hash)\n\t\t\t\t\t} else if (hash) {\n\t\t\t\t\t\tawait updateCache(db, file, hash)\n\t\t\t\t\t}\n\n\t\t\t\t\treturn { action: result.action, file }\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { file, error: true, message: `error syncing piece: ${error}` }\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ concurrency: cpus().length }\n\t\t) as Readable & AsyncIterable<PieceSyncResult>\n\t}\n\n\tasync syncMarkdownAdd(\n\t\tdb: LuzzleDatabase,\n\t\tmarkdown: PieceMarkdown<F>,\n\t\thash?: string\n\t): Promise<void> {\n\t\tconst createInput = makePieceItemInsertable(this._pieceName, markdown, this._schema)\n\n\t\tlet contentHash = hash\n\t\tif (contentHash === undefined) {\n\t\t\tconst readStream = this._storage.createReadStream(markdown.filePath)\n\t\t\tcontentHash = await calculateHashFromFile(readStream)\n\t\t}\n\n\t\tawait insertItem(db, createInput)\n\t\tawait addCache(db, markdown.filePath, contentHash)\n\t}\n\n\tasync syncMarkdown(db: LuzzleDatabase, markdown: PieceMarkdown<F>): Promise<void> {\n\t\tconst dbPiece = await selectItem(db, markdown.filePath)\n\n\t\tif (dbPiece) {\n\t\t\tawait this.syncMarkdownUpdate(db, markdown, dbPiece)\n\t\t} else {\n\t\t\tawait this.syncMarkdownAdd(db, markdown)\n\t\t}\n\t}\n\n\tasync syncMarkdownUpdate(\n\t\tdb: LuzzleDatabase,\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tdata: LuzzleSelectable<'pieces_items'>,\n\t\thash?: string\n\t): Promise<void> {\n\t\tconst updateInput = makePieceItemUpdatable(markdown, this._schema, data, false)\n\t\tawait updateItem(db, markdown.filePath, updateInput)\n\n\t\tlet contentHash = hash\n\t\tif (contentHash === undefined) {\n\t\t\tconst readStream = this._storage.createReadStream(markdown.filePath)\n\t\t\tcontentHash = await calculateHashFromFile(readStream)\n\t\t}\n\n\t\tawait updateCache(db, data.file_path, contentHash)\n\t}\n\n\ttoMarkdown(data: LuzzleSelectable<'pieces_items'>): PieceMarkdown<F> {\n\t\tconst frontmatter = JSON.parse(data.frontmatter_json)\n\t\tconst frontmatterJson: Record<string, unknown> = {}\n\t\tconst dataKeys = Object.keys(frontmatter)\n\t\tconst fields = getPieceFrontmatterSchemaFields(this._schema).filter(\n\t\t\t(f): f is PieceFrontmatterSchemaField & { name: string } =>\n\t\t\t\t!!f.name && dataKeys.includes(f.name)\n\t\t)\n\n\t\tfields.forEach((field) => {\n\t\t\tconst name = field.name\n\t\t\tconst value = frontmatter[name]\n\n\t\t\tfrontmatterJson[name] = databaseValueToPieceFrontmatterValue(value, field)\n\t\t})\n\n\t\treturn makePieceMarkdown(data.file_path, data.type, data.note_markdown, frontmatterJson as F)\n\t}\n\n\tgetField(markdown: PieceMarkdown<F>, fieldPath: string): PieceFrontMatterValue | undefined {\n\t\treturn getFrontmatterValue(markdown.frontmatter, fieldPath)\n\t}\n\n\tasync setFields(\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tfields: Record<string, unknown>\n\t): Promise<PieceMarkdown<F>> {\n\t\tlet updatedMarkdown = markdown\n\n\t\tfor (const fieldname in fields) {\n\t\t\tupdatedMarkdown = await this.setField(updatedMarkdown, fieldname, fields[fieldname])\n\t\t}\n\n\t\treturn updatedMarkdown\n\t}\n\n\tasync removeFields(\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tfields: string[]\n\t): Promise<PieceMarkdown<Omit<F, keyof F>>> {\n\t\tlet updatedMarkdown = markdown as unknown as PieceMarkdown<Omit<F, keyof F>>\n\n\t\tfor (const field of fields) {\n\t\t\tupdatedMarkdown = (await this.removeField(\n\t\t\t\tupdatedMarkdown as unknown as PieceMarkdown<F>,\n\t\t\t\tfield\n\t\t\t)) as unknown as PieceMarkdown<Omit<F, keyof F>>\n\t\t}\n\n\t\treturn updatedMarkdown\n\t}\n\n\tasync setField(\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tfieldPath: string,\n\t\tvalue: unknown\n\t): Promise<PieceMarkdown<F>> {\n\t\tconst pieceField = findFrontmatterField(this.fields, fieldPath)\n\n\t\tif (!pieceField) {\n\t\t\tthrow new Error(`${fieldPath} is not a field in ${this._pieceName} ${markdown.filePath}`)\n\t\t}\n\n\t\tconst isArray = pieceField.type === 'array'\n\t\tconst itemField = isArray ? { ...pieceField.items, name: pieceField.name } : pieceField\n\t\tconst values = Array.isArray(value) ? value : [value]\n\n\t\tconst updatedFrontmatter = structuredClone(markdown.frontmatter)\n\n\t\ttry {\n\t\t\tif (isArray) {\n\t\t\t\tconst current = getFrontmatterValue(updatedFrontmatter, fieldPath)\n\t\t\t\tif (!Array.isArray(current)) {\n\t\t\t\t\tsetFrontmatterValue(updatedFrontmatter, fieldPath, [] as unknown as PieceFrontMatterValue)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfor (const one of values) {\n\t\t\t\tconst pieceValue = await makePieceValue(itemField, one)\n\n\t\t\t\tconst val = isAttachableStream(pieceValue)\n\t\t\t\t\t? await savePieceFieldAsset(\n\t\t\t\t\t\tmarkdown.filePath,\n\t\t\t\t\t\titemField as PieceFrontmatterSchemaField,\n\t\t\t\t\t\tpieceValue,\n\t\t\t\t\t\tthis._storage\n\t\t\t\t\t)\n\t\t\t\t\t: (pieceValue as PieceFrontMatterValue)\n\n\t\t\t\tsetFrontmatterValue(updatedFrontmatter, fieldPath, val)\n\t\t\t}\n\t\t} catch (e) {\n\t\t\tconst error = e as Error\n\t\t\tconsole.error(`could not set field ${fieldPath}: ${error.message}`)\n\t\t\treturn markdown\n\t\t}\n\n\t\treturn makePieceMarkdown(markdown.filePath, markdown.piece, markdown.note, updatedFrontmatter)\n\t}\n\n\tasync removeField(\n\t\tmarkdown: PieceMarkdown<F>,\n\t\tfieldPath: string,\n\t\tvalue?: number | string | boolean\n\t): Promise<PieceMarkdown<F>> {\n\t\tconst pieceField = findFrontmatterField(this.fields, fieldPath)\n\n\t\tif (!pieceField) {\n\t\t\tthrow new Error(`${fieldPath} is not a field in ${this._pieceName} ${markdown.filePath}`)\n\t\t}\n\n\t\tif (pieceField.nullable !== true) {\n\t\t\tthrow new Error(`${fieldPath} is a required field in ${this._pieceName} ${markdown.filePath}`)\n\t\t}\n\n\t\tconst updatedFrontmatter = structuredClone(markdown.frontmatter)\n\n\t\tif (value === undefined) {\n\t\t\tunsetFrontmatterValue(updatedFrontmatter, fieldPath)\n\t\t} else {\n\t\t\tconst pieceValue = await makePieceValue(pieceField, value)\n\t\t\tconst current = getFrontmatterValue(updatedFrontmatter, fieldPath)\n\n\t\t\tif (Array.isArray(current) && !isAttachableStream(pieceValue)) {\n\t\t\t\tconst index = current.indexOf(pieceValue as PieceFrontMatterValue)\n\t\t\t\tif (index !== -1) {\n\t\t\t\t\tunsetFrontmatterValue(updatedFrontmatter, `${fieldPath}.${index}`)\n\t\t\t\t} else {\n\t\t\t\t\treturn markdown\n\t\t\t\t}\n\t\t\t} else if (current === pieceValue) {\n\t\t\t\tunsetFrontmatterValue(updatedFrontmatter, fieldPath)\n\t\t\t}\n\t\t}\n\n\t\treturn makePieceMarkdown(markdown.filePath, markdown.piece, markdown.note, updatedFrontmatter)\n\t}\n}\n\nexport default Piece\n"]}
@@ -2,6 +2,8 @@ import Piece from './Piece.js';
2
2
  import LuzzleStorage from '../storage/abstract.js';
3
3
  import { LuzzleDatabase } from '../database/tables/index.js';
4
4
  import { Readable } from 'stream';
5
+ import { JSONSchemaType } from 'ajv';
6
+ import { PieceFrontmatter } from './utils/frontmatter.js';
5
7
  export type PiecesPruneResult = {
6
8
  action: 'pruned';
7
9
  name: string;
@@ -20,21 +22,35 @@ export type PiecesSyncResult = {
20
22
  error: true;
21
23
  message: string;
22
24
  };
25
+ export interface DiffSummary {
26
+ added: string[];
27
+ updated: string[];
28
+ pruned: string[];
29
+ }
30
+ export type SchemaDiff = DiffSummary;
31
+ export interface PiecesDiff {
32
+ schemas: DiffSummary;
33
+ pieces: DiffSummary;
34
+ }
23
35
  declare class Pieces {
24
36
  private _storage;
25
37
  constructor(storage: LuzzleStorage);
26
- getPiece(name: string): Promise<Piece<import("./index.js").PieceFrontmatter>>;
27
- getPieceMarkdown(file: string, type?: string): Promise<import("./index.js").PieceMarkdown<import("./index.js").PieceFrontmatter>>;
38
+ getPiece(name: string): Promise<Piece<PieceFrontmatter>>;
39
+ getPieceMarkdown(file: string, type?: string): Promise<import("./index.js").PieceMarkdown<PieceFrontmatter>>;
28
40
  getPieceAsset(file: string): Promise<Buffer<ArrayBufferLike>>;
29
41
  getSchemaPath(name: string): string;
30
- getSchema(name: string): Promise<import("ajv").JSONSchemaType<import("./index.js").PieceFrontmatter>>;
42
+ getSchema(name: string): Promise<JSONSchemaType<PieceFrontmatter>>;
43
+ private getSchemaChange;
44
+ diffSchemas(db: LuzzleDatabase, options?: {
45
+ force?: boolean;
46
+ }): Promise<SchemaDiff>;
47
+ diff(db: LuzzleDatabase, options?: {
48
+ force?: boolean;
49
+ }): Promise<PiecesDiff>;
31
50
  sync(db: LuzzleDatabase, options?: {
32
- dryRun?: boolean;
33
51
  force?: boolean;
34
52
  }): Promise<Readable & AsyncIterable<PiecesSyncResult>>;
35
- prune(db: LuzzleDatabase, options?: {
36
- dryRun: boolean;
37
- }): Promise<Readable & AsyncIterable<PiecesPruneResult>>;
53
+ prune(db: LuzzleDatabase): Promise<Readable & AsyncIterable<PiecesPruneResult>>;
38
54
  parseFilename(file: string): {
39
55
  file: string;
40
56
  type: string | null;
@@ -43,6 +59,7 @@ declare class Pieces {
43
59
  };
44
60
  getSchemas(): Promise<string[]>;
45
61
  getTypes(): Promise<string[]>;
62
+ isAsset(filePath: string): boolean;
46
63
  getFilesIn(dir: string, options?: {
47
64
  deep?: boolean;
48
65
  }): Promise<{
@@ -34,43 +34,87 @@ class Pieces {
34
34
  const schemaJson = await this._storage.readFile(schemaPath, 'text');
35
35
  return jsonToPieceSchema(schemaJson);
36
36
  }
37
+ async getSchemaChange(db, name, options) {
38
+ const piece = await getPiece(db, name);
39
+ const schemaPath = this.getSchemaPath(name);
40
+ const fileStat = await this._storage.stat(schemaPath).catch(() => null);
41
+ if (!fileStat) {
42
+ return { name, error: true, message: `schema file ${schemaPath} not found` };
43
+ }
44
+ const schema = await this.getSchema(name);
45
+ if (!piece) {
46
+ return { action: 'added', name, schema };
47
+ }
48
+ const pieceDate = piece.date_updated || piece.date_added;
49
+ if (options?.force || fileStat.last_modified > new Date(pieceDate)) {
50
+ return { action: 'updated', name, schema };
51
+ }
52
+ return { action: 'skipped', name, schema };
53
+ }
54
+ async diffSchemas(db, options) {
55
+ const names = await this.getTypes();
56
+ const added = [];
57
+ const updated = [];
58
+ for (const name of names) {
59
+ const plan = await this.getSchemaChange(db, name, options);
60
+ if ('error' in plan) {
61
+ continue;
62
+ }
63
+ if (plan.action === 'added') {
64
+ added.push(name);
65
+ }
66
+ else if (plan.action === 'updated') {
67
+ updated.push(name);
68
+ }
69
+ }
70
+ const dbPieces = await getPieces(db);
71
+ const diskPiecesSet = new Set(names);
72
+ const pruned = dbPieces.filter((piece) => !diskPiecesSet.has(piece.name)).map((piece) => piece.name);
73
+ return { added, updated, pruned };
74
+ }
75
+ async diff(db, options) {
76
+ const schemas = await this.diffSchemas(db, options);
77
+ const files = await this.getFilesIn('.', { deep: true });
78
+ const pieces = { added: [], updated: [], pruned: [] };
79
+ for (const type of files.types) {
80
+ const piece = await this.getPiece(type);
81
+ const onDisk = files.pieces.filter((file) => this.parseFilename(file).type === type);
82
+ const stream = await piece.diff(db, onDisk, options);
83
+ for await (const result of stream) {
84
+ if (result.action === 'added') {
85
+ pieces.added.push(result.file);
86
+ }
87
+ else if (result.action === 'updated') {
88
+ pieces.updated.push(result.file);
89
+ }
90
+ }
91
+ pieces.pruned.push(...(await piece.diffPrune(db, onDisk)));
92
+ }
93
+ return { schemas, pieces };
94
+ }
37
95
  async sync(db, options) {
38
96
  const names = await this.getTypes();
39
97
  const stream = Readable.from(names);
40
98
  return stream.map(async (name) => {
41
- const piece = await getPiece(db, name);
42
- const schemaPath = this.getSchemaPath(name);
43
- const fileStat = await this._storage.stat(schemaPath).catch(() => null);
44
- if (fileStat) {
45
- const schema = await this.getSchema(name);
46
- try {
47
- if (!piece) {
48
- if (!options?.dryRun) {
49
- await addPiece(db, name, schema);
50
- }
51
- return { action: 'added', name };
52
- }
53
- else {
54
- const pieceDate = piece.date_updated || piece.date_added;
55
- if (options?.force || fileStat.last_modified > new Date(pieceDate)) {
56
- if (!options?.dryRun) {
57
- await updatePiece(db, name, schema);
58
- }
59
- return { action: 'updated', name };
60
- }
61
- return { action: 'skipped', name };
62
- }
99
+ const plan = await this.getSchemaChange(db, name, options);
100
+ if ('error' in plan) {
101
+ return plan;
102
+ }
103
+ try {
104
+ if (plan.action === 'added') {
105
+ await addPiece(db, name, plan.schema);
63
106
  }
64
- catch (error) {
65
- return { name, error: true, message: `error syncing piece: ${error}` };
107
+ else if (plan.action === 'updated') {
108
+ await updatePiece(db, name, plan.schema);
66
109
  }
110
+ return { action: plan.action, name };
67
111
  }
68
- else {
69
- return { name, error: true, message: `schema file ${schemaPath} not found` };
112
+ catch (error) {
113
+ return { name, error: true, message: `error syncing piece: ${error}` };
70
114
  }
71
115
  }, { concurrency: cpus().length });
72
116
  }
73
- async prune(db, options) {
117
+ async prune(db) {
74
118
  const names = await this.getTypes();
75
119
  const dbPieces = await getPieces(db);
76
120
  const diskPiecesSet = new Set(names);
@@ -80,9 +124,7 @@ class Pieces {
80
124
  const stream = Readable.from(missingPieces);
81
125
  return stream.map(async (name) => {
82
126
  try {
83
- if (!options?.dryRun) {
84
- await deletePiece(db, name);
85
- }
127
+ await deletePiece(db, name);
86
128
  return { action: 'pruned', name };
87
129
  }
88
130
  catch (error) {
@@ -107,6 +149,10 @@ class Pieces {
107
149
  const schemas = await this.getSchemas();
108
150
  return schemas.map((schema) => path.basename(schema, '.json'));
109
151
  }
152
+ isAsset(filePath) {
153
+ const normalized = path.normalize(filePath);
154
+ return normalized.startsWith(ASSETS_DIRECTORY + '/') || normalized === ASSETS_DIRECTORY;
155
+ }
110
156
  async getFilesIn(dir, options) {
111
157
  const types = await this.getTypes();
112
158
  const readdir = await this._storage.getFilesIn(dir, options);
@@ -117,14 +163,20 @@ class Pieces {
117
163
  types,
118
164
  };
119
165
  return readdir.reduce((files, file) => {
166
+ const fullPath = path.join(dir, file);
120
167
  const extension = path.extname(file);
121
- const isAsset = file.startsWith(ASSETS_DIRECTORY);
122
- const isHidden = file.startsWith('.');
123
168
  const isDirectory = file.endsWith('/');
169
+ const isHidden = file
170
+ .split(/[\\/]/)
171
+ .some((part) => part.startsWith('.') && part !== ASSETS_DIRECTORY && part !== '');
172
+ if (isHidden) {
173
+ return files;
174
+ }
175
+ const isAsset = this.isAsset(fullPath);
124
176
  if (isAsset && !isDirectory) {
125
177
  files.assets.push(file);
126
178
  }
127
- else if (!isHidden) {
179
+ else {
128
180
  if (isDirectory) {
129
181
  files.directories.push(file);
130
182
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Pieces.js","sourceRoot":"","sources":["../../../src/pieces/Pieces.ts"],"names":[],"mappings":"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","sourcesContent":["import Piece from './Piece.js'\nimport path from 'path'\nimport LuzzleStorage from '../storage/abstract.js'\nimport { jsonToPieceSchema } from './json.schema.js'\nimport { LuzzleDatabase } from '../database/tables/index.js'\nimport { addPiece, deletePiece, getPiece, getPieces, updatePiece } from './manager.js'\nimport {\n\tLUZZLE_DIRECTORY,\n\tLUZZLE_SCHEMAS_DIRECTORY,\n\tASSETS_DIRECTORY,\n\tLUZZLE_PIECE_FILE_EXTENSION,\n} from './assets.js'\nimport { Readable } from 'stream'\nimport { cpus } from 'os'\n\nexport type PiecesPruneResult =\n\t| {\n\t\taction: 'pruned'\n\t\tname: string\n\t\terror?: false\n\t}\n\t| {\n\t\tname: string\n\t\terror: true\n\t\tmessage: string\n\t}\n\nexport type PiecesSyncResult =\n\t| {\n\t\taction: 'added' | 'updated' | 'skipped'\n\t\tname: string\n\t\terror?: false\n\t}\n\t| {\n\t\tname: string\n\t\terror: true\n\t\tmessage: string\n\t}\n\nclass Pieces {\n\tprivate _storage: LuzzleStorage\n\n\tconstructor(storage: LuzzleStorage) {\n\t\tthis._storage = storage\n\t}\n\n\tasync getPiece(name: string) {\n\t\tconst schema = await this.getSchema(name)\n\t\treturn new Piece(name, this._storage, schema)\n\t}\n\n\tasync getPieceMarkdown(file: string, type?: string) {\n\t\tconst parts = this.parseFilename(file)\n\t\tconst pieceName = parts.type || type\n\n\t\tif (pieceName) {\n\t\t\tconst piece = await this.getPiece(pieceName)\n\t\t\treturn await piece.get(file)\n\t\t}\n\n\t\tthrow new Error(`invalid piece, can't determine piece type: ${file}`)\n\t}\n\n\tasync getPieceAsset(file: string) {\n\t\treturn (await this._storage.readFile(file)) as Buffer\n\t}\n\n\tgetSchemaPath(name: string) {\n\t\treturn path.join(LUZZLE_DIRECTORY, LUZZLE_SCHEMAS_DIRECTORY, `${name}.json`)\n\t}\n\n\tasync getSchema(name: string) {\n\t\tconst schemaPath = this.getSchemaPath(name)\n\t\tconst schemaJson = await this._storage.readFile(schemaPath, 'text')\n\t\treturn jsonToPieceSchema(schemaJson as string)\n\t}\n\n\tasync sync(db: LuzzleDatabase, options?: { dryRun?: boolean; force?: boolean }) {\n\t\tconst names = await this.getTypes()\n\t\tconst stream = Readable.from(names)\n\n\t\treturn stream.map(\n\t\t\tasync (name: string): Promise<PiecesSyncResult> => {\n\t\t\t\tconst piece = await getPiece(db, name)\n\t\t\t\tconst schemaPath = this.getSchemaPath(name)\n\t\t\t\tconst fileStat = await this._storage.stat(schemaPath).catch(() => null)\n\n\t\t\t\tif (fileStat) {\n\t\t\t\t\tconst schema = await this.getSchema(name)\n\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (!piece) {\n\t\t\t\t\t\t\tif (!options?.dryRun) {\n\t\t\t\t\t\t\t\tawait addPiece(db, name, schema)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn { action: 'added', name }\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tconst pieceDate = piece.date_updated || piece.date_added\n\t\t\t\t\t\t\tif (options?.force || fileStat.last_modified > new Date(pieceDate)) {\n\t\t\t\t\t\t\t\tif (!options?.dryRun) {\n\t\t\t\t\t\t\t\t\tawait updatePiece(db, name, schema)\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\treturn { action: 'updated', name }\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn { action: 'skipped', name }\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (error) {\n\t\t\t\t\t\treturn { name, error: true, message: `error syncing piece: ${error}` }\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\treturn { name, error: true, message: `schema file ${schemaPath} not found` }\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ concurrency: cpus().length }\n\t\t) as Readable & AsyncIterable<PiecesSyncResult>\n\t}\n\n\tasync prune(db: LuzzleDatabase, options?: { dryRun: boolean }) {\n\t\tconst names = await this.getTypes()\n\t\tconst dbPieces = await getPieces(db)\n\t\tconst diskPiecesSet = new Set<string>(names)\n\t\tconst missingPieces = dbPieces\n\t\t\t.filter((piece) => !diskPiecesSet.has(piece.name))\n\t\t\t.map((piece) => piece.name)\n\t\tconst stream = Readable.from(missingPieces)\n\n\t\treturn stream.map(\n\t\t\tasync (name: string): Promise<PiecesPruneResult> => {\n\t\t\t\ttry {\n\t\t\t\t\tif (!options?.dryRun) {\n\t\t\t\t\t\tawait deletePiece(db, name)\n\t\t\t\t\t}\n\t\t\t\t\treturn { action: 'pruned', name }\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { name, error: true, message: `error pruning piece: ${error}` }\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ concurrency: cpus().length }\n\t\t) as Readable & AsyncIterable<PiecesPruneResult>\n\t}\n\n\tparseFilename(file: string) {\n\t\treturn {\n\t\t\tfile,\n\t\t\ttype: file.match(/\\.([^.]+)\\.[^.]+$/)?.[1] ?? null,\n\t\t\tformat: path.extname(file),\n\t\t\tslug: file.replace(/\\.[^.]+\\.[^.]+$/, ''),\n\t\t}\n\t}\n\n\tasync getSchemas() {\n\t\tconst schemaDir = path.join(LUZZLE_DIRECTORY, LUZZLE_SCHEMAS_DIRECTORY)\n\t\tconst readDir = await this._storage.getFilesIn(schemaDir)\n\n\t\treturn readDir.filter((file) => path.extname(file) === `.json`)\n\t}\n\n\tasync getTypes() {\n\t\tconst schemas = await this.getSchemas()\n\n\t\treturn schemas.map((schema) => path.basename(schema, '.json'))\n\t}\n\n\tasync getFilesIn(dir: string, options?: { deep?: boolean }) {\n\t\tconst types = await this.getTypes()\n\t\tconst readdir = await this._storage.getFilesIn(dir, options)\n\t\tconst result: {\n\t\t\ttypes: string[]\n\t\t\tpieces: string[]\n\t\t\tassets: string[]\n\t\t\tdirectories: string[]\n\t\t} = {\n\t\t\tpieces: [],\n\t\t\tassets: [],\n\t\t\tdirectories: [],\n\t\t\ttypes,\n\t\t}\n\n\t\treturn readdir.reduce((files, file) => {\n\t\t\tconst extension = path.extname(file)\n\t\t\tconst isAsset = file.startsWith(ASSETS_DIRECTORY)\n\t\t\tconst isHidden = file.startsWith('.')\n\t\t\tconst isDirectory = file.endsWith('/')\n\n\t\t\tif (isAsset && !isDirectory) {\n\t\t\t\tfiles.assets.push(file)\n\t\t\t} else if (!isHidden) {\n\t\t\t\tif (isDirectory) {\n\t\t\t\t\tfiles.directories.push(file)\n\t\t\t\t} else {\n\t\t\t\t\tconst type = this.parseFilename(file).type\n\t\t\t\t\tconst isMarkdown = extension === `.${LUZZLE_PIECE_FILE_EXTENSION}`\n\n\t\t\t\t\tif (type && types.includes(type) && isMarkdown) {\n\t\t\t\t\t\tfiles.pieces.push(file)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn files\n\t\t}, result)\n\t}\n}\n\nexport default Pieces\n"]}
1
+ {"version":3,"file":"Pieces.js","sourceRoot":"","sources":["../../../src/pieces/Pieces.ts"],"names":[],"mappings":"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","sourcesContent":["import Piece from './Piece.js'\nimport path from 'path'\nimport LuzzleStorage from '../storage/abstract.js'\nimport { jsonToPieceSchema } from './json.schema.js'\nimport { LuzzleDatabase } from '../database/tables/index.js'\nimport { addPiece, deletePiece, getPiece, getPieces, updatePiece } from './manager.js'\nimport {\n\tLUZZLE_DIRECTORY,\n\tLUZZLE_SCHEMAS_DIRECTORY,\n\tASSETS_DIRECTORY,\n\tLUZZLE_PIECE_FILE_EXTENSION,\n} from './assets.js'\nimport { Readable } from 'stream'\nimport { cpus } from 'os'\nimport { JSONSchemaType } from 'ajv'\nimport { PieceFrontmatter } from './utils/frontmatter.js'\n\nexport type PiecesPruneResult =\n\t| {\n\t\taction: 'pruned'\n\t\tname: string\n\t\terror?: false\n\t}\n\t| {\n\t\tname: string\n\t\terror: true\n\t\tmessage: string\n\t}\n\nexport type PiecesSyncResult =\n\t| {\n\t\taction: 'added' | 'updated' | 'skipped'\n\t\tname: string\n\t\terror?: false\n\t}\n\t| {\n\t\tname: string\n\t\terror: true\n\t\tmessage: string\n\t}\n\nexport interface DiffSummary {\n\tadded: string[]\n\tupdated: string[]\n\tpruned: string[]\n}\n\nexport type SchemaDiff = DiffSummary\n\nexport interface PiecesDiff {\n\tschemas: DiffSummary\n\tpieces: DiffSummary\n}\n\ntype SchemaChange =\n\t| { action: 'added' | 'updated' | 'skipped'; name: string; schema: JSONSchemaType<PieceFrontmatter> }\n\t| { name: string; error: true; message: string }\n\nclass Pieces {\n\tprivate _storage: LuzzleStorage\n\n\tconstructor(storage: LuzzleStorage) {\n\t\tthis._storage = storage\n\t}\n\n\tasync getPiece(name: string) {\n\t\tconst schema = await this.getSchema(name)\n\t\treturn new Piece(name, this._storage, schema)\n\t}\n\n\tasync getPieceMarkdown(file: string, type?: string) {\n\t\tconst parts = this.parseFilename(file)\n\t\tconst pieceName = parts.type || type\n\n\t\tif (pieceName) {\n\t\t\tconst piece = await this.getPiece(pieceName)\n\t\t\treturn await piece.get(file)\n\t\t}\n\n\t\tthrow new Error(`invalid piece, can't determine piece type: ${file}`)\n\t}\n\n\tasync getPieceAsset(file: string) {\n\t\treturn (await this._storage.readFile(file)) as Buffer\n\t}\n\n\tgetSchemaPath(name: string) {\n\t\treturn path.join(LUZZLE_DIRECTORY, LUZZLE_SCHEMAS_DIRECTORY, `${name}.json`)\n\t}\n\n\tasync getSchema(name: string) {\n\t\tconst schemaPath = this.getSchemaPath(name)\n\t\tconst schemaJson = await this._storage.readFile(schemaPath, 'text')\n\t\treturn jsonToPieceSchema(schemaJson as string)\n\t}\n\n\tprivate async getSchemaChange(\n\t\tdb: LuzzleDatabase,\n\t\tname: string,\n\t\toptions?: { force?: boolean }\n\t): Promise<SchemaChange> {\n\t\tconst piece = await getPiece(db, name)\n\t\tconst schemaPath = this.getSchemaPath(name)\n\t\tconst fileStat = await this._storage.stat(schemaPath).catch(() => null)\n\n\t\tif (!fileStat) {\n\t\t\treturn { name, error: true, message: `schema file ${schemaPath} not found` }\n\t\t}\n\n\t\tconst schema = await this.getSchema(name)\n\n\t\tif (!piece) {\n\t\t\treturn { action: 'added', name, schema }\n\t\t}\n\n\t\tconst pieceDate = piece.date_updated || piece.date_added\n\t\tif (options?.force || fileStat.last_modified > new Date(pieceDate)) {\n\t\t\treturn { action: 'updated', name, schema }\n\t\t}\n\n\t\treturn { action: 'skipped', name, schema }\n\t}\n\n\tasync diffSchemas(db: LuzzleDatabase, options?: { force?: boolean }): Promise<SchemaDiff> {\n\t\tconst names = await this.getTypes()\n\t\tconst added: string[] = []\n\t\tconst updated: string[] = []\n\n\t\tfor (const name of names) {\n\t\t\tconst plan = await this.getSchemaChange(db, name, options)\n\t\t\tif ('error' in plan) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif (plan.action === 'added') {\n\t\t\t\tadded.push(name)\n\t\t\t} else if (plan.action === 'updated') {\n\t\t\t\tupdated.push(name)\n\t\t\t}\n\t\t}\n\n\t\tconst dbPieces = await getPieces(db)\n\t\tconst diskPiecesSet = new Set<string>(names)\n\t\tconst pruned = dbPieces.filter((piece) => !diskPiecesSet.has(piece.name)).map((piece) => piece.name)\n\n\t\treturn { added, updated, pruned }\n\t}\n\n\tasync diff(db: LuzzleDatabase, options?: { force?: boolean }): Promise<PiecesDiff> {\n\t\tconst schemas = await this.diffSchemas(db, options)\n\t\tconst files = await this.getFilesIn('.', { deep: true })\n\t\tconst pieces: DiffSummary = { added: [], updated: [], pruned: [] }\n\n\t\tfor (const type of files.types) {\n\t\t\tconst piece = await this.getPiece(type)\n\t\t\tconst onDisk = files.pieces.filter((file) => this.parseFilename(file).type === type)\n\n\t\t\tconst stream = await piece.diff(db, onDisk, options)\n\t\t\tfor await (const result of stream) {\n\t\t\t\tif (result.action === 'added') {\n\t\t\t\t\tpieces.added.push(result.file)\n\t\t\t\t} else if (result.action === 'updated') {\n\t\t\t\t\tpieces.updated.push(result.file)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpieces.pruned.push(...(await piece.diffPrune(db, onDisk)))\n\t\t}\n\n\t\treturn { schemas, pieces }\n\t}\n\n\tasync sync(db: LuzzleDatabase, options?: { force?: boolean }) {\n\t\tconst names = await this.getTypes()\n\t\tconst stream = Readable.from(names)\n\n\t\treturn stream.map(\n\t\t\tasync (name: string): Promise<PiecesSyncResult> => {\n\t\t\t\tconst plan = await this.getSchemaChange(db, name, options)\n\n\t\t\t\tif ('error' in plan) {\n\t\t\t\t\treturn plan\n\t\t\t\t}\n\n\t\t\t\ttry {\n\t\t\t\t\tif (plan.action === 'added') {\n\t\t\t\t\t\tawait addPiece(db, name, plan.schema)\n\t\t\t\t\t} else if (plan.action === 'updated') {\n\t\t\t\t\t\tawait updatePiece(db, name, plan.schema)\n\t\t\t\t\t}\n\t\t\t\t\treturn { action: plan.action, name }\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { name, error: true, message: `error syncing piece: ${error}` }\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ concurrency: cpus().length }\n\t\t) as Readable & AsyncIterable<PiecesSyncResult>\n\t}\n\n\tasync prune(db: LuzzleDatabase) {\n\t\tconst names = await this.getTypes()\n\t\tconst dbPieces = await getPieces(db)\n\t\tconst diskPiecesSet = new Set<string>(names)\n\t\tconst missingPieces = dbPieces\n\t\t\t.filter((piece) => !diskPiecesSet.has(piece.name))\n\t\t\t.map((piece) => piece.name)\n\t\tconst stream = Readable.from(missingPieces)\n\n\t\treturn stream.map(\n\t\t\tasync (name: string): Promise<PiecesPruneResult> => {\n\t\t\t\ttry {\n\t\t\t\t\tawait deletePiece(db, name)\n\t\t\t\t\treturn { action: 'pruned', name }\n\t\t\t\t} catch (error) {\n\t\t\t\t\treturn { name, error: true, message: `error pruning piece: ${error}` }\n\t\t\t\t}\n\t\t\t},\n\t\t\t{ concurrency: cpus().length }\n\t\t) as Readable & AsyncIterable<PiecesPruneResult>\n\t}\n\n\tparseFilename(file: string) {\n\t\treturn {\n\t\t\tfile,\n\t\t\ttype: file.match(/\\.([^.]+)\\.[^.]+$/)?.[1] ?? null,\n\t\t\tformat: path.extname(file),\n\t\t\tslug: file.replace(/\\.[^.]+\\.[^.]+$/, ''),\n\t\t}\n\t}\n\n\tasync getSchemas() {\n\t\tconst schemaDir = path.join(LUZZLE_DIRECTORY, LUZZLE_SCHEMAS_DIRECTORY)\n\t\tconst readDir = await this._storage.getFilesIn(schemaDir)\n\n\t\treturn readDir.filter((file) => path.extname(file) === `.json`)\n\t}\n\n\tasync getTypes() {\n\t\tconst schemas = await this.getSchemas()\n\n\t\treturn schemas.map((schema) => path.basename(schema, '.json'))\n\t}\n\n\tisAsset(filePath: string): boolean {\n\t\tconst normalized = path.normalize(filePath)\n\t\treturn normalized.startsWith(ASSETS_DIRECTORY + '/') || normalized === ASSETS_DIRECTORY\n\t}\n\n\tasync getFilesIn(dir: string, options?: { deep?: boolean }) {\n\t\tconst types = await this.getTypes()\n\t\tconst readdir = await this._storage.getFilesIn(dir, options)\n\t\tconst result: {\n\t\t\ttypes: string[]\n\t\t\tpieces: string[]\n\t\t\tassets: string[]\n\t\t\tdirectories: string[]\n\t\t} = {\n\t\t\tpieces: [],\n\t\t\tassets: [],\n\t\t\tdirectories: [],\n\t\t\ttypes,\n\t\t}\n\n\t\treturn readdir.reduce((files, file) => {\n\t\t\tconst fullPath = path.join(dir, file)\n\t\t\tconst extension = path.extname(file)\n\t\t\tconst isDirectory = file.endsWith('/')\n\n\t\t\tconst isHidden = file\n\t\t\t\t.split(/[\\\\/]/)\n\t\t\t\t.some((part) => part.startsWith('.') && part !== ASSETS_DIRECTORY && part !== '')\n\n\t\t\tif (isHidden) {\n\t\t\t\treturn files\n\t\t\t}\n\n\t\t\tconst isAsset = this.isAsset(fullPath)\n\n\t\t\tif (isAsset && !isDirectory) {\n\t\t\t\tfiles.assets.push(file)\n\t\t\t} else {\n\t\t\t\tif (isDirectory) {\n\t\t\t\t\tfiles.directories.push(file)\n\t\t\t\t} else {\n\t\t\t\t\tconst type = this.parseFilename(file).type\n\t\t\t\t\tconst isMarkdown = extension === `.${LUZZLE_PIECE_FILE_EXTENSION}`\n\n\t\t\t\t\tif (type && types.includes(type) && isMarkdown) {\n\t\t\t\t\t\tfiles.pieces.push(file)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn files\n\t\t}, result)\n\t}\n}\n\nexport default Pieces\n"]}
@@ -6,5 +6,5 @@ export * from './json.schema.js';
6
6
  export * from './items.js';
7
7
  export * from './item.js';
8
8
  export * from './manager.js';
9
- export { default as Piece, type PieceSyncResult, type PiecePruneResult } from './Piece.js';
10
- export { default as Pieces, type PiecesSyncResult, type PiecesPruneResult } from './Pieces.js';
9
+ export { default as Piece, type PieceSyncResult, type PiecePruneResult, type PieceDiffResult, } from './Piece.js';
10
+ export { default as Pieces, type PiecesSyncResult, type PiecesPruneResult, type DiffSummary, type SchemaDiff, type PiecesDiff, } from './Pieces.js';
@@ -6,6 +6,6 @@ export * from './json.schema.js';
6
6
  export * from './items.js';
7
7
  export * from './item.js';
8
8
  export * from './manager.js';
9
- export { default as Piece } from './Piece.js';
10
- export { default as Pieces } from './Pieces.js';
9
+ export { default as Piece, } from './Piece.js';
10
+ export { default as Pieces, } from './Pieces.js';
11
11
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/pieces/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,wBAAwB,CAAA;AACtC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,OAAO,EAAE,OAAO,IAAI,KAAK,EAA+C,MAAM,YAAY,CAAA;AAC1F,OAAO,EAAE,OAAO,IAAI,MAAM,EAAiD,MAAM,aAAa,CAAA","sourcesContent":["export * from './utils/markdown.js'\nexport * from './utils/frontmatter.js'\nexport * from './utils/frontmatter.path.js'\nexport * from './utils/piece.js'\nexport * from './json.schema.js'\nexport * from './items.js'\nexport * from './item.js'\nexport * from './manager.js'\nexport { default as Piece, type PieceSyncResult, type PiecePruneResult } from './Piece.js'\nexport { default as Pieces, type PiecesSyncResult, type PiecesPruneResult } from './Pieces.js'\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/pieces/index.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,wBAAwB,CAAA;AACtC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,kBAAkB,CAAA;AAChC,cAAc,kBAAkB,CAAA;AAChC,cAAc,YAAY,CAAA;AAC1B,cAAc,WAAW,CAAA;AACzB,cAAc,cAAc,CAAA;AAC5B,OAAO,EACN,OAAO,IAAI,KAAK,GAIhB,MAAM,YAAY,CAAA;AACnB,OAAO,EACN,OAAO,IAAI,MAAM,GAMjB,MAAM,aAAa,CAAA","sourcesContent":["export * from './utils/markdown.js'\nexport * from './utils/frontmatter.js'\nexport * from './utils/frontmatter.path.js'\nexport * from './utils/piece.js'\nexport * from './json.schema.js'\nexport * from './items.js'\nexport * from './item.js'\nexport * from './manager.js'\nexport {\n\tdefault as Piece,\n\ttype PieceSyncResult,\n\ttype PiecePruneResult,\n\ttype PieceDiffResult,\n} from './Piece.js'\nexport {\n\tdefault as Pieces,\n\ttype PiecesSyncResult,\n\ttype PiecesPruneResult,\n\ttype DiffSummary,\n\ttype SchemaDiff,\n\ttype PiecesDiff,\n} from './Pieces.js'\n"]}