@tsdoctor/snapshot 0.4.1 → 0.5.1
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/SnapshotService.js +50 -31
- package/index.d.ts +20 -0
- package/package.json +4 -4
package/SnapshotService.js
CHANGED
|
@@ -44,6 +44,26 @@ var SnapshotService = class SnapshotService extends Context.Service()("@tsdoctor
|
|
|
44
44
|
*/
|
|
45
45
|
static layer = (dbPath) => make(dbPath);
|
|
46
46
|
/**
|
|
47
|
+
* The live {@link SnapshotService} implementation over an in-memory
|
|
48
|
+
* (`:memory:`) SQLite database.
|
|
49
|
+
*
|
|
50
|
+
* @remarks
|
|
51
|
+
* The same SQL, migrations and upsert semantics as
|
|
52
|
+
* {@link SnapshotService.layer}, backed by `@effected/store`'s
|
|
53
|
+
* `Store.layerTest` instead of a file — so a test exercises the real queries
|
|
54
|
+
* without a temp directory. What it cannot cover is file-backed behaviour:
|
|
55
|
+
* WAL mode, `checkpointOnClose`, and persistence across a reopen. Unlike
|
|
56
|
+
* {@link SnapshotService.layerTest}, nothing is stubbed.
|
|
57
|
+
*
|
|
58
|
+
* This is a layer FACTORY: each call mints a fresh layer and so a fresh,
|
|
59
|
+
* empty database. Bind the result to a `const` and provide it once when the
|
|
60
|
+
* steps of one test must see each other's rows.
|
|
61
|
+
*
|
|
62
|
+
* @returns A layer providing {@link SnapshotService}
|
|
63
|
+
* @public
|
|
64
|
+
*/
|
|
65
|
+
static layerMemory = () => makeMemory();
|
|
66
|
+
/**
|
|
47
67
|
* An in-memory double: no SQLite file, no migrations, no WAL.
|
|
48
68
|
*
|
|
49
69
|
* @remarks
|
|
@@ -113,20 +133,21 @@ const migrations = [{
|
|
|
113
133
|
yield* sql`CREATE INDEX IF NOT EXISTS idx_file_path ON file_snapshots(file_path)`;
|
|
114
134
|
})
|
|
115
135
|
}];
|
|
116
|
-
const make = (dbPath) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
136
|
+
const make = (dbPath) => Layer.provide(serviceImpl(), Store.layerSqlite({
|
|
137
|
+
filename: dbPath,
|
|
138
|
+
migrations,
|
|
139
|
+
checkpointOnClose: true
|
|
140
|
+
}));
|
|
141
|
+
const makeMemory = () => Layer.provide(serviceImpl(), Store.layerTest({ migrations }));
|
|
142
|
+
/** The SQL implementation, over whichever `Store` the caller provides. */
|
|
143
|
+
const serviceImpl = () => Layer.effect(SnapshotService, Effect.gen(function* () {
|
|
144
|
+
const sql = (yield* Store).client;
|
|
145
|
+
return {
|
|
146
|
+
hashContent,
|
|
147
|
+
getSnapshot: (outputDir, filePath) => sql`SELECT * FROM file_snapshots WHERE output_dir = ${outputDir} AND file_path = ${filePath}`.pipe(Effect.map((rows) => rows.length > 0 ? Option.some(toFileSnapshot(rows[0])) : Option.none()), Effect.mapError(toSnapshotDbError)),
|
|
148
|
+
getAllForDirectory: (outputDir) => sql`SELECT * FROM file_snapshots WHERE output_dir = ${outputDir}`.pipe(Effect.map((rows) => rows.map(toFileSnapshot)), Effect.mapError(toSnapshotDbError)),
|
|
149
|
+
getFilePaths: (outputDir) => sql`SELECT file_path FROM file_snapshots WHERE output_dir = ${outputDir}`.pipe(Effect.map((rows) => rows.map((r) => r.file_path)), Effect.mapError(toSnapshotDbError)),
|
|
150
|
+
upsert: (snapshot) => sql`INSERT INTO file_snapshots
|
|
130
151
|
(output_dir, file_path, published_time, modified_time,
|
|
131
152
|
content_hash, frontmatter_hash, build_time)
|
|
132
153
|
VALUES (${snapshot.outputDir}, ${snapshot.filePath},
|
|
@@ -143,7 +164,7 @@ const make = (dbPath) => {
|
|
|
143
164
|
OR modified_time != ${snapshot.modifiedTime}
|
|
144
165
|
OR content_hash != ${snapshot.contentHash}
|
|
145
166
|
OR frontmatter_hash != ${snapshot.frontmatterHash}`.pipe(Effect.as(true), Effect.mapError(toSnapshotDbError)),
|
|
146
|
-
|
|
167
|
+
batchUpsert: (snapshots) => (snapshots.length === 0 ? Effect.succeed(0) : sql.withTransaction(Effect.forEach(snapshots, (s) => sql`INSERT INTO file_snapshots
|
|
147
168
|
(output_dir, file_path, published_time, modified_time,
|
|
148
169
|
content_hash, frontmatter_hash, build_time)
|
|
149
170
|
VALUES (${s.outputDir}, ${s.filePath},
|
|
@@ -160,23 +181,21 @@ const make = (dbPath) => {
|
|
|
160
181
|
OR modified_time != ${s.modifiedTime}
|
|
161
182
|
OR content_hash != ${s.contentHash}
|
|
162
183
|
OR frontmatter_hash != ${s.frontmatterHash}`, { concurrency: 1 })).pipe(Effect.map(() => snapshots.length))).pipe(Effect.mapError(toSnapshotDbError)),
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
184
|
+
deleteSnapshot: (outputDir, filePath) => sql`DELETE FROM file_snapshots WHERE output_dir = ${outputDir} AND file_path = ${filePath}`.pipe(Effect.asVoid, Effect.mapError(toSnapshotDbError)),
|
|
185
|
+
cleanupStale: (outputDir, currentFiles) => Effect.gen(function* () {
|
|
186
|
+
const rows = yield* sql`SELECT file_path FROM file_snapshots WHERE output_dir = ${outputDir}`;
|
|
187
|
+
const staleFiles = [];
|
|
188
|
+
for (const row of rows) {
|
|
189
|
+
const fp = row.file_path;
|
|
190
|
+
if (!currentFiles.has(fp)) {
|
|
191
|
+
yield* sql`DELETE FROM file_snapshots WHERE output_dir = ${outputDir} AND file_path = ${fp}`;
|
|
192
|
+
staleFiles.push(fp);
|
|
173
193
|
}
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
};
|
|
194
|
+
}
|
|
195
|
+
return staleFiles;
|
|
196
|
+
}).pipe(Effect.mapError(toSnapshotDbError))
|
|
197
|
+
};
|
|
198
|
+
}));
|
|
180
199
|
|
|
181
200
|
//#endregion
|
|
182
201
|
export { SnapshotDbError, SnapshotService };
|
package/index.d.ts
CHANGED
|
@@ -171,6 +171,26 @@ export declare class SnapshotService extends SnapshotService_base {
|
|
|
171
171
|
* @public
|
|
172
172
|
*/
|
|
173
173
|
static readonly layer: (dbPath: string) => Layer.Layer<SnapshotService, StoreError | StoreMigrationError>;
|
|
174
|
+
/**
|
|
175
|
+
* The live {@link SnapshotService} implementation over an in-memory
|
|
176
|
+
* (`:memory:`) SQLite database.
|
|
177
|
+
*
|
|
178
|
+
* @remarks
|
|
179
|
+
* The same SQL, migrations and upsert semantics as
|
|
180
|
+
* {@link SnapshotService.layer}, backed by `@effected/store`'s
|
|
181
|
+
* `Store.layerTest` instead of a file — so a test exercises the real queries
|
|
182
|
+
* without a temp directory. What it cannot cover is file-backed behaviour:
|
|
183
|
+
* WAL mode, `checkpointOnClose`, and persistence across a reopen. Unlike
|
|
184
|
+
* {@link SnapshotService.layerTest}, nothing is stubbed.
|
|
185
|
+
*
|
|
186
|
+
* This is a layer FACTORY: each call mints a fresh layer and so a fresh,
|
|
187
|
+
* empty database. Bind the result to a `const` and provide it once when the
|
|
188
|
+
* steps of one test must see each other's rows.
|
|
189
|
+
*
|
|
190
|
+
* @returns A layer providing {@link SnapshotService}
|
|
191
|
+
* @public
|
|
192
|
+
*/
|
|
193
|
+
static readonly layerMemory: () => Layer.Layer<SnapshotService, StoreError | StoreMigrationError>;
|
|
174
194
|
/**
|
|
175
195
|
* An in-memory double: no SQLite file, no migrations, no WAL.
|
|
176
196
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsdoctor/snapshot",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Incremental-build snapshot tracking for static documentation pipelines: a schema-versioned SQLite store of per-file content hashes and timestamps, built on @effected/store, plus the pure SHA-256 content-hashing helpers that feed it.",
|
|
6
6
|
"keywords": [
|
|
@@ -37,11 +37,11 @@
|
|
|
37
37
|
"./package.json": "./package.json"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"@effected/jsonc": "^0.
|
|
40
|
+
"@effected/jsonc": "^0.15.0"
|
|
41
41
|
},
|
|
42
42
|
"peerDependencies": {
|
|
43
|
-
"@effected/store": "^0.
|
|
44
|
-
"effect": "4.0.0
|
|
43
|
+
"@effected/store": "^0.12.0",
|
|
44
|
+
"effect": "^4.0.0"
|
|
45
45
|
},
|
|
46
46
|
"engines": {
|
|
47
47
|
"node": ">=24.11.0"
|