@gscdump/engine 1.4.3 → 1.4.4
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/dist/adapters/node-harness.d.mts +2 -1
- package/dist/adapters/node-harness.mjs +12 -1
- package/dist/entities.d.mts +57 -29
- package/dist/entities.mjs +698 -325
- package/dist/entity-keys.d.mts +18 -2
- package/dist/entity-keys.mjs +38 -3
- package/dist/rollups.d.mts +2 -2
- package/dist/rollups.mjs +71 -20
- package/dist/sitemap-projection.d.mts +20 -0
- package/dist/sitemap-projection.mjs +57 -0
- package/package.json +4 -4
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { DataSource, StorageEngine } from "../storage.mjs";
|
|
2
2
|
import { SearchType } from "gscdump/query";
|
|
3
|
-
import { Row, TableName } from "@gscdump/contracts";
|
|
3
|
+
import { Row, TableName, TenantCtx } from "@gscdump/contracts";
|
|
4
4
|
interface NodeHarnessOptions {
|
|
5
5
|
dataDir: string;
|
|
6
6
|
/** Tenant user id. Defaults to `'local'` for single-user CLI installs. */
|
|
@@ -18,6 +18,7 @@ interface NodeHarness {
|
|
|
18
18
|
dataDir: string;
|
|
19
19
|
userId: string;
|
|
20
20
|
siteIdFor: (siteUrl: string) => string;
|
|
21
|
+
withSitemapMutation: <T>(ctx: TenantCtx, mutate: () => Promise<T>) => Promise<T>;
|
|
21
22
|
runRawSql: (opts: {
|
|
22
23
|
sql: string;
|
|
23
24
|
siteUrl: string;
|
|
@@ -11,9 +11,10 @@ function createNodeHarness(opts) {
|
|
|
11
11
|
const handle = createNodeDuckDBHandle();
|
|
12
12
|
const factory = { getDuckDB: async () => handle };
|
|
13
13
|
const dataSource = createFilesystemDataSource({ rootDir: dataDir });
|
|
14
|
+
const manifestStore = createFilesystemManifestStore({ path: path.join(dataDir, manifestFilename) });
|
|
14
15
|
const engine = createStorageEngine({
|
|
15
16
|
dataSource,
|
|
16
|
-
manifestStore
|
|
17
|
+
manifestStore,
|
|
17
18
|
codec: createDuckDBCodec(factory),
|
|
18
19
|
executor: createDuckDBExecutor(factory)
|
|
19
20
|
});
|
|
@@ -41,6 +42,16 @@ function createNodeHarness(opts) {
|
|
|
41
42
|
dataDir,
|
|
42
43
|
userId,
|
|
43
44
|
siteIdFor: encodeSiteId,
|
|
45
|
+
withSitemapMutation(ctx, mutate) {
|
|
46
|
+
if (ctx.userId !== userId) throw new Error(`Sitemap mutation user scope mismatch: expected ${userId}, got ${ctx.userId}`);
|
|
47
|
+
if (!ctx.siteId) throw new Error("Sitemap mutation requires a site id");
|
|
48
|
+
return manifestStore.withLock({
|
|
49
|
+
userId,
|
|
50
|
+
siteId: ctx.siteId,
|
|
51
|
+
table: "pages",
|
|
52
|
+
partition: "__sitemap_entities__"
|
|
53
|
+
}, mutate);
|
|
54
|
+
},
|
|
44
55
|
runRawSql
|
|
45
56
|
};
|
|
46
57
|
}
|
package/dist/entities.d.mts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { DataSource } from "./storage.mjs";
|
|
2
2
|
import { ScheduleState } from "./schedule.mjs";
|
|
3
|
-
import { emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix } from "./entity-keys.mjs";
|
|
3
|
+
import { SitemapGenerationKey, emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, parseSitemapUrlsDeltaKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsEventKey, sitemapUrlsEventSeedKey, sitemapUrlsEventsPrefix, sitemapUrlsGenerationKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPendingGenerationKey, sitemapUrlsPendingGenerationsPrefix, sitemapUrlsPrefix, sitemapUrlsProjectionManifestKey, sitemapUrlsReconcileGenerationKey } from "./entity-keys.mjs";
|
|
4
|
+
import { SITEMAP_PROJECTION_GRACE_MS, SitemapProjectionFeed, SitemapProjectionFiles, SitemapProjectionManifest, decodeSitemapProjectionManifest, emptySitemapProjectionManifest, parseSitemapProjectionManifest, selectSitemapProjectionFiles, withSitemapProjectionFeed } from "./sitemap-projection.mjs";
|
|
4
5
|
import { QueryDimDeps, QueryDimMeta, QueryDimRecord, QueryDimStore, buildQueryDimRecords, createQueryDimStore } from "./query-dim.mjs";
|
|
5
6
|
import { TenantCtx } from "@gscdump/contracts";
|
|
6
7
|
/**
|
|
@@ -284,9 +285,15 @@ interface SnapshotUrlsResult {
|
|
|
284
285
|
removed: number;
|
|
285
286
|
kept: number;
|
|
286
287
|
contentHash: string;
|
|
287
|
-
/** True when
|
|
288
|
+
/** True when current membership did not change. Initial history seeding may write. */
|
|
288
289
|
unchanged: boolean;
|
|
289
290
|
}
|
|
291
|
+
interface CompleteSitemapGeneration {
|
|
292
|
+
_tag: 'complete';
|
|
293
|
+
id: string;
|
|
294
|
+
/** Unix epoch milliseconds. */
|
|
295
|
+
observedAt: number;
|
|
296
|
+
}
|
|
290
297
|
interface ReconcileResult {
|
|
291
298
|
/** Feedpaths that were absent from the live set and had their live URLs pruned. */
|
|
292
299
|
feedpathsPruned: number;
|
|
@@ -307,12 +314,12 @@ interface CompactUrlsOptions {
|
|
|
307
314
|
maxFeedpaths?: number;
|
|
308
315
|
}
|
|
309
316
|
interface CompactUrlsResult {
|
|
310
|
-
/** Feedpaths whose deltas were folded and
|
|
317
|
+
/** Feedpaths whose active deltas were folded and retired by this call. */
|
|
311
318
|
compactedFeedpaths: number;
|
|
312
319
|
/**
|
|
313
320
|
* Feedpaths that still hold outstanding deltas. Call `compactUrls` again to
|
|
314
|
-
* continue
|
|
315
|
-
*
|
|
321
|
+
* continue. No cursor is needed because the projection watermark excludes a
|
|
322
|
+
* compacted feedpath's grace-retained deltas from the next call.
|
|
316
323
|
*/
|
|
317
324
|
remainingFeedpaths: number;
|
|
318
325
|
}
|
|
@@ -325,6 +332,21 @@ interface DeltaEntry {
|
|
|
325
332
|
lastmod?: string;
|
|
326
333
|
at: number;
|
|
327
334
|
}
|
|
335
|
+
interface SitemapMembershipEvent {
|
|
336
|
+
feedpath: string;
|
|
337
|
+
feedpathHash: string;
|
|
338
|
+
urlHash: string;
|
|
339
|
+
op: 'added' | 'removed';
|
|
340
|
+
loc: string;
|
|
341
|
+
lastmod?: string;
|
|
342
|
+
generationId: string;
|
|
343
|
+
/** Unix epoch milliseconds. */
|
|
344
|
+
observedAt: number;
|
|
345
|
+
/** Stable ordering within one generation. */
|
|
346
|
+
sequence: number;
|
|
347
|
+
/** Whether this row also mutates the disposable current-state projection. */
|
|
348
|
+
projectsState: boolean;
|
|
349
|
+
}
|
|
328
350
|
interface DateRange {
|
|
329
351
|
/** YYYY-MM-DD inclusive. */
|
|
330
352
|
from?: string;
|
|
@@ -334,27 +356,29 @@ interface DateRange {
|
|
|
334
356
|
interface LoadUrlsOptions {
|
|
335
357
|
includeRemoved?: boolean;
|
|
336
358
|
}
|
|
337
|
-
interface
|
|
338
|
-
/**
|
|
339
|
-
* Persist a snapshot run. Updates the index + writes one immutable
|
|
340
|
-
* history doc per record under `history/<feedpathHash>__<capturedAtMs>.json`.
|
|
341
|
-
*/
|
|
342
|
-
writeSnapshot: (ctx: TenantCtx, records: readonly SitemapRecord[]) => Promise<void>;
|
|
359
|
+
interface SitemapReadStore {
|
|
343
360
|
/** Load the full site index (latest record per feedpath). */
|
|
344
361
|
loadIndex: (ctx: TenantCtx) => Promise<SitemapIndex>;
|
|
345
362
|
/** Fetch the latest snapshot for a feedpath, or undefined. */
|
|
346
363
|
getLatest: (ctx: TenantCtx, path: string) => Promise<SitemapRecord | undefined>;
|
|
347
|
-
/**
|
|
348
|
-
* Diff incoming URLs against the prior `urls/index.parquet` partition for
|
|
349
|
-
* `feedpath`; on change, writes a single delta parquet under
|
|
350
|
-
* `urls/deltas/YYYY-MM-DD__{feedpathHash}.parquet`. Skipped (0 PUTs) when
|
|
351
|
-
* `contentHash` matches prior.
|
|
352
|
-
*/
|
|
353
|
-
snapshotUrls: (ctx: TenantCtx, feedpath: string, urls: readonly ParsedUrl[]) => Promise<SnapshotUrlsResult>;
|
|
354
364
|
/** Stream live (and optionally removed) URL rows for a feedpath. */
|
|
355
365
|
loadUrls: (ctx: TenantCtx, feedpath: string, opts?: LoadUrlsOptions) => AsyncIterable<SitemapUrlRecord>;
|
|
356
366
|
/** Stream all delta entries within `[from, to]` (YYYY-MM-DD inclusive). */
|
|
357
367
|
loadDeltas: (ctx: TenantCtx, dateRange?: DateRange) => AsyncIterable<DeltaEntry>;
|
|
368
|
+
/** Stream immutable membership events within `[from, to]`. */
|
|
369
|
+
loadEvents: (ctx: TenantCtx, dateRange?: DateRange) => AsyncIterable<SitemapMembershipEvent>;
|
|
370
|
+
}
|
|
371
|
+
interface SitemapStore extends SitemapReadStore {
|
|
372
|
+
/**
|
|
373
|
+
* Persist a snapshot run. Updates the index + writes one immutable
|
|
374
|
+
* history doc per record under `history/<feedpathHash>__<capturedAtMs>.json`.
|
|
375
|
+
*/
|
|
376
|
+
writeSnapshot: (ctx: TenantCtx, records: readonly SitemapRecord[]) => Promise<void>;
|
|
377
|
+
/**
|
|
378
|
+
* Diff a complete sitemap generation against current state. The immutable
|
|
379
|
+
* membership event lands before its disposable state delta.
|
|
380
|
+
*/
|
|
381
|
+
snapshotUrls: (ctx: TenantCtx, generation: CompleteSitemapGeneration, feedpath: string, urls: readonly ParsedUrl[]) => Promise<SnapshotUrlsResult>;
|
|
358
382
|
/**
|
|
359
383
|
* Fold accumulated deltas into the prior index, one feedpath at a time:
|
|
360
384
|
* rewrites each touched feedpath's `by-feed/<hash>/index.parquet` and deletes
|
|
@@ -362,9 +386,9 @@ interface SitemapStore {
|
|
|
362
386
|
* regardless of total site URL count.
|
|
363
387
|
*
|
|
364
388
|
* Optionally bounded in TIME too (`opts.deadlineMs` / `opts.maxFeedpaths`).
|
|
365
|
-
* A bounded call is safe to stop mid-way: each feedpath
|
|
366
|
-
*
|
|
367
|
-
*
|
|
389
|
+
* A bounded call is safe to stop mid-way: each rewritten feedpath advances
|
|
390
|
+
* the projection watermark, so the remainder is simply what the next call
|
|
391
|
+
* finds. Callers drive this from `remainingFeedpaths`, not a cursor.
|
|
368
392
|
*/
|
|
369
393
|
compactUrls: (ctx: TenantCtx, opts?: CompactUrlsOptions) => Promise<CompactUrlsResult>;
|
|
370
394
|
/**
|
|
@@ -373,22 +397,26 @@ interface SitemapStore {
|
|
|
373
397
|
* prune URLs *inside* a feedpath that was re-observed; a whole feed dropped
|
|
374
398
|
* from the sitemap list (no `snapshotUrls` call) leaves its URLs frozen-live
|
|
375
399
|
* forever. This is the sidecar mirror of the D1 generation sweep: it rewrites
|
|
376
|
-
* each dropped feedpath's `by-feed/<hash>/index.parquet` with `removedAt` set
|
|
377
|
-
*
|
|
378
|
-
*
|
|
379
|
-
*
|
|
400
|
+
* each dropped feedpath's `by-feed/<hash>/index.parquet` with `removedAt` set,
|
|
401
|
+
* advances its projection watermark, and grace-retires outstanding deltas.
|
|
402
|
+
* Bounded per feedpath, so memory stays flat regardless of site size. Live
|
|
403
|
+
* feedpaths are never touched.
|
|
380
404
|
*/
|
|
381
|
-
reconcile: (ctx: TenantCtx, opts: {
|
|
405
|
+
reconcile: (ctx: TenantCtx, generation: CompleteSitemapGeneration, opts: {
|
|
382
406
|
liveFeedpaths: readonly string[];
|
|
383
|
-
at?: number;
|
|
384
407
|
}) => Promise<ReconcileResult>;
|
|
385
408
|
}
|
|
386
|
-
interface
|
|
409
|
+
interface CreateSitemapReadStoreOptions {
|
|
387
410
|
dataSource: DataSource;
|
|
388
411
|
/** Override the feedpath hash (test seam). */
|
|
389
412
|
hash?: (path: string) => string;
|
|
413
|
+
}
|
|
414
|
+
type SitemapMutation = <T>(ctx: TenantCtx, fn: () => Promise<T>) => Promise<T>;
|
|
415
|
+
interface CreateSitemapStoreOptions extends CreateSitemapReadStoreOptions {
|
|
416
|
+
withMutation: SitemapMutation;
|
|
390
417
|
now?: () => number;
|
|
391
418
|
}
|
|
419
|
+
declare function createSitemapReadStore(opts: CreateSitemapReadStoreOptions): SitemapReadStore;
|
|
392
420
|
declare function createSitemapStore(opts: CreateSitemapStoreOptions): SitemapStore;
|
|
393
421
|
interface IndexingMetadataRecord {
|
|
394
422
|
url: string;
|
|
@@ -432,4 +460,4 @@ interface CreateEmptyTypesStoreOptions {
|
|
|
432
460
|
now?: () => number;
|
|
433
461
|
}
|
|
434
462
|
declare function createEmptyTypesStore(opts: CreateEmptyTypesStoreOptions): EmptyTypesStore;
|
|
435
|
-
export { CompactUrlsOptions, CompactUrlsResult, CreateEmptyTypesStoreOptions, CreateIndexingMetadataStoreOptions, CreateInspectionStoreOptions, CreateSitemapStoreOptions, DateRange, DeltaEntry, EmptyTypesDoc, EmptyTypesStore, INSPECTION_HISTORY_MAX_BYTES, IndexingMetadataIndex, IndexingMetadataRecord, IndexingMetadataStore, InspectionEventRow, InspectionHistoryShard, InspectionIndex, InspectionParquetRow, InspectionRecord, InspectionStore, LoadUrlsOptions, ParsedUrl, QueryDimDeps, QueryDimMeta, QueryDimRecord, QueryDimStore, ReconcileResult, SitemapHistoryDoc, SitemapIndex, SitemapRecord, SitemapStore, SitemapUrlRecord, SnapshotUrlsResult, buildQueryDimRecords, createEmptyTypesStore, createIndexingMetadataStore, createInspectionStore, createQueryDimStore, createSitemapStore, emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, queryDimMetaKey, queryDimParquetKey, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPrefix };
|
|
463
|
+
export { CompactUrlsOptions, CompactUrlsResult, CompleteSitemapGeneration, CreateEmptyTypesStoreOptions, CreateIndexingMetadataStoreOptions, CreateInspectionStoreOptions, CreateSitemapReadStoreOptions, CreateSitemapStoreOptions, DateRange, DeltaEntry, EmptyTypesDoc, EmptyTypesStore, INSPECTION_HISTORY_MAX_BYTES, IndexingMetadataIndex, IndexingMetadataRecord, IndexingMetadataStore, InspectionEventRow, InspectionHistoryShard, InspectionIndex, InspectionParquetRow, InspectionRecord, InspectionStore, LoadUrlsOptions, ParsedUrl, QueryDimDeps, QueryDimMeta, QueryDimRecord, QueryDimStore, ReconcileResult, SITEMAP_PROJECTION_GRACE_MS, SitemapGenerationKey, SitemapHistoryDoc, SitemapIndex, SitemapMembershipEvent, SitemapMutation, SitemapProjectionFeed, SitemapProjectionFiles, SitemapProjectionManifest, SitemapReadStore, SitemapRecord, SitemapStore, SitemapUrlRecord, SnapshotUrlsResult, buildQueryDimRecords, createEmptyTypesStore, createIndexingMetadataStore, createInspectionStore, createQueryDimStore, createSitemapReadStore, createSitemapStore, decodeSitemapProjectionManifest, emptySitemapProjectionManifest, emptyTypesKey, hashSortedUrlList, hashUrl, hashUrlList, indexingMetadataIndexKey, inspectionBaseKey, inspectionEventKey, inspectionEventsPrefix, inspectionHistoryPrefix, inspectionHistoryShardKey, inspectionIndexKey, inspectionParquetKey, parseSitemapProjectionManifest, parseSitemapUrlsDeltaKey, queryDimMetaKey, queryDimParquetKey, selectSitemapProjectionFiles, sitemapHistoryKey, sitemapIndexKey, sitemapUrlsDeltaKey, sitemapUrlsEventKey, sitemapUrlsEventSeedKey, sitemapUrlsEventsPrefix, sitemapUrlsGenerationKey, sitemapUrlsIndexKey, sitemapUrlsIndexPrefix, sitemapUrlsPendingGenerationKey, sitemapUrlsPendingGenerationsPrefix, sitemapUrlsPrefix, sitemapUrlsProjectionManifestKey, sitemapUrlsReconcileGenerationKey, withSitemapProjectionFeed };
|