@griddo/cx 11.13.3 → 11.14.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/build/commands/end-render.js +31 -31
- package/build/commands/end-render.js.map +4 -4
- package/build/commands/notify-indexnow.d.ts +1 -0
- package/build/commands/notify-indexnow.js +161 -0
- package/build/commands/notify-indexnow.js.map +7 -0
- package/build/commands/prepare-assets-directory.js +12 -12
- package/build/commands/prepare-assets-directory.js.map +3 -3
- package/build/commands/prepare-domains-render.js +12 -12
- package/build/commands/prepare-domains-render.js.map +3 -3
- package/build/commands/reset-render.js +23 -23
- package/build/commands/reset-render.js.map +3 -3
- package/build/commands/start-render.js +51 -51
- package/build/commands/start-render.js.map +4 -4
- package/build/commands/upload-search-content.js +12 -12
- package/build/commands/upload-search-content.js.map +3 -3
- package/build/index.js +5 -3
- package/build/services/indexnow-key-file.d.ts +22 -0
- package/build/services/indexnow-notify.d.ts +29 -0
- package/build/services/indexnow-payload.d.ts +54 -0
- package/build/services/render.d.ts +2 -1
- package/build/shared/envs.d.ts +2 -1
- package/cli.mjs +9 -0
- package/exporter/build.sh +1 -0
- package/exporter/commands/end-render.ts +11 -0
- package/exporter/commands/notify-indexnow.ts +19 -0
- package/exporter/services/generate-md.ts +7 -10
- package/exporter/services/indexnow-key-file.ts +58 -0
- package/exporter/services/indexnow-notify.ts +196 -0
- package/exporter/services/indexnow-payload.ts +195 -0
- package/exporter/services/llms.ts +14 -1
- package/exporter/services/render.ts +6 -0
- package/exporter/services/store.ts +24 -0
- package/exporter/shared/envs.ts +2 -0
- package/exporter/ssg-adapters/gatsby/actions/meta.ts +2 -0
- package/exporter/ssg-adapters/gatsby/actions/sync.ts +6 -1
- package/exporter/ssg-adapters/gatsby/shared/sync-render.ts +1 -5
- package/package.json +4 -3
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import fsp from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
|
|
4
|
+
import pLimit from "p-limit";
|
|
5
|
+
|
|
6
|
+
import { GriddoLog } from "../core/GriddoLog";
|
|
7
|
+
import { GRIDDO_API_CONCURRENCY_COUNT, GRIDDO_INDEXNOW_ENABLED } from "../shared/envs";
|
|
8
|
+
import type { GriddoPageObject } from "../shared/types/pages";
|
|
9
|
+
import type { Site } from "../shared/types/sites";
|
|
10
|
+
import { getRenderPathsHydratedWithDomainFromDB } from "./render";
|
|
11
|
+
import { getPage } from "./sites";
|
|
12
|
+
|
|
13
|
+
export interface IndexNowUpsertEntry {
|
|
14
|
+
id: number;
|
|
15
|
+
url: string;
|
|
16
|
+
siteId: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface IndexNowDeleteEntry {
|
|
20
|
+
id: number;
|
|
21
|
+
url: string;
|
|
22
|
+
siteId: number;
|
|
23
|
+
reason: "offlinePending" | "siteUnpublished";
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IndexNowPayloadFile {
|
|
27
|
+
domain: string;
|
|
28
|
+
renderedAt: string;
|
|
29
|
+
upsert: IndexNowUpsertEntry[];
|
|
30
|
+
delete: IndexNowDeleteEntry[];
|
|
31
|
+
skipped: {
|
|
32
|
+
noindex: number;
|
|
33
|
+
noUrlResolved: number;
|
|
34
|
+
paginatedSecondary: number;
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface FlushOptions {
|
|
39
|
+
domain: string;
|
|
40
|
+
sitesToPublish: Site[];
|
|
41
|
+
sitesToUnpublish: Site[];
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface CollectOptions {
|
|
45
|
+
siteId: number;
|
|
46
|
+
pageId: number;
|
|
47
|
+
pageObjects: GriddoPageObject[];
|
|
48
|
+
uploadPending: number[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Collects URLs that should be notified to IndexNow during `createStore`.
|
|
53
|
+
* Intended usage:
|
|
54
|
+
* - `collect()` during the per-page loop (reuses the already-fetched page
|
|
55
|
+
* objects, so upserts cost 0 extra API calls).
|
|
56
|
+
* - `flush()` once per render to resolve delete URLs via `getPage` and write
|
|
57
|
+
* the payload file to `.griddo/cache/<domain>/indexnow-payload.json`.
|
|
58
|
+
*/
|
|
59
|
+
class IndexNowCollector {
|
|
60
|
+
public readonly enabled: boolean;
|
|
61
|
+
private upserts: IndexNowUpsertEntry[] = [];
|
|
62
|
+
private skipped = { noindex: 0, noUrlResolved: 0, paginatedSecondary: 0 };
|
|
63
|
+
|
|
64
|
+
constructor(enabled: boolean = GRIDDO_INDEXNOW_ENABLED) {
|
|
65
|
+
this.enabled = enabled;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
collect(options: CollectOptions): void {
|
|
69
|
+
if (!this.enabled) return;
|
|
70
|
+
const { siteId, pageId, pageObjects, uploadPending } = options;
|
|
71
|
+
if (!uploadPending.includes(pageId)) return;
|
|
72
|
+
|
|
73
|
+
for (const obj of pageObjects) {
|
|
74
|
+
const entry = this.extractCanonical(obj, siteId, pageId);
|
|
75
|
+
if (entry) this.upserts.push(entry);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
async flush(options: FlushOptions): Promise<void> {
|
|
80
|
+
if (!this.enabled) return;
|
|
81
|
+
const { domain, sitesToPublish, sitesToUnpublish } = options;
|
|
82
|
+
|
|
83
|
+
const { __cache } = await getRenderPathsHydratedWithDomainFromDB({ domain });
|
|
84
|
+
const payloadPath = path.join(__cache, "indexnow-payload.json");
|
|
85
|
+
|
|
86
|
+
// Always drop the previous payload first so a failure leaves no stale file.
|
|
87
|
+
await safeUnlink(payloadPath);
|
|
88
|
+
|
|
89
|
+
const deletes: IndexNowDeleteEntry[] = [];
|
|
90
|
+
const limit = pLimit(GRIDDO_API_CONCURRENCY_COUNT);
|
|
91
|
+
const jobs: Promise<void>[] = [];
|
|
92
|
+
|
|
93
|
+
for (const site of sitesToPublish) {
|
|
94
|
+
for (const id of site.pagesStatus.offlinePending) {
|
|
95
|
+
jobs.push(
|
|
96
|
+
limit(async () => {
|
|
97
|
+
const url = await this.resolveUrlForId(id);
|
|
98
|
+
if (url) {
|
|
99
|
+
deletes.push({ id, url, siteId: site.id, reason: "offlinePending" });
|
|
100
|
+
} else {
|
|
101
|
+
this.skipped.noUrlResolved++;
|
|
102
|
+
}
|
|
103
|
+
}),
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
for (const site of sitesToUnpublish) {
|
|
109
|
+
const ids = new Set<number>([
|
|
110
|
+
...site.pagesStatus.active,
|
|
111
|
+
...site.pagesStatus.offlinePending,
|
|
112
|
+
]);
|
|
113
|
+
for (const id of ids) {
|
|
114
|
+
jobs.push(
|
|
115
|
+
limit(async () => {
|
|
116
|
+
const url = await this.resolveUrlForId(id);
|
|
117
|
+
if (url) {
|
|
118
|
+
deletes.push({ id, url, siteId: site.id, reason: "siteUnpublished" });
|
|
119
|
+
} else {
|
|
120
|
+
this.skipped.noUrlResolved++;
|
|
121
|
+
}
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
await Promise.all(jobs);
|
|
128
|
+
|
|
129
|
+
const payload: IndexNowPayloadFile = {
|
|
130
|
+
domain,
|
|
131
|
+
renderedAt: new Date().toISOString(),
|
|
132
|
+
upsert: this.upserts,
|
|
133
|
+
delete: deletes,
|
|
134
|
+
skipped: this.skipped,
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
await fsp.mkdir(__cache, { recursive: true });
|
|
138
|
+
await fsp.writeFile(payloadPath, JSON.stringify(payload, null, 2), "utf-8");
|
|
139
|
+
GriddoLog.verbose(
|
|
140
|
+
`indexnow payload written (upsert:${payload.upsert.length} delete:${payload.delete.length} skipped:${JSON.stringify(payload.skipped)})`,
|
|
141
|
+
);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private extractCanonical(
|
|
145
|
+
obj: GriddoPageObject,
|
|
146
|
+
siteId: number,
|
|
147
|
+
pageId: number,
|
|
148
|
+
): IndexNowUpsertEntry | null {
|
|
149
|
+
const page = obj.context.page as { fullUrl?: string; template?: { pageNumber?: number } };
|
|
150
|
+
const meta = obj.context.pageMetadata;
|
|
151
|
+
|
|
152
|
+
// Skip paginated secondary pages (/2/, /3/...). Page 1 is the canonical entry.
|
|
153
|
+
const pageNumber = page?.template?.pageNumber;
|
|
154
|
+
if (typeof pageNumber === "number" && pageNumber > 1) {
|
|
155
|
+
this.skipped.paginatedSecondary++;
|
|
156
|
+
return null;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
const url = page?.fullUrl;
|
|
160
|
+
if (!url) {
|
|
161
|
+
this.skipped.noUrlResolved++;
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (meta?.index === "noindex") {
|
|
166
|
+
this.skipped.noindex++;
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { id: pageId, url, siteId };
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
private async resolveUrlForId(id: number): Promise<string | null> {
|
|
174
|
+
try {
|
|
175
|
+
const page = await getPage(id, "indexnow-payload");
|
|
176
|
+
const url = (page as { fullUrl?: string } | null)?.fullUrl;
|
|
177
|
+
return url ?? null;
|
|
178
|
+
} catch (err) {
|
|
179
|
+
GriddoLog.warn(
|
|
180
|
+
`indexnow: getPage(${id}) failed: ${(err as Error)?.message ?? String(err)}`,
|
|
181
|
+
);
|
|
182
|
+
return null;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function safeUnlink(p: string): Promise<void> {
|
|
188
|
+
try {
|
|
189
|
+
await fsp.unlink(p);
|
|
190
|
+
} catch {
|
|
191
|
+
// Missing file is fine; nothing to clear.
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export { IndexNowCollector };
|
|
@@ -71,7 +71,20 @@ async function generateLlmsTxt(domain: string): Promise<void> {
|
|
|
71
71
|
const pageLinks = llmsResponse
|
|
72
72
|
.map(({ title, url, socialDescription }) => {
|
|
73
73
|
const description = socialDescription ? `: ${socialDescription}` : "";
|
|
74
|
-
|
|
74
|
+
const strippedUrl = removeTrailingSlash(url);
|
|
75
|
+
// Home: pathname "/" → /index.md evita que quede dominio.com.md
|
|
76
|
+
const isHome = (() => {
|
|
77
|
+
try {
|
|
78
|
+
return new URL(url).pathname === "/";
|
|
79
|
+
} catch {
|
|
80
|
+
return url === "/" || url === "";
|
|
81
|
+
}
|
|
82
|
+
})();
|
|
83
|
+
const href =
|
|
84
|
+
GRIDDO_RENDER_ENABLED_LLM_MD && isHome
|
|
85
|
+
? `${strippedUrl}/index.md`
|
|
86
|
+
: `${strippedUrl}${pageIndexName}`;
|
|
87
|
+
return `- [${title}](${href})${description}`;
|
|
75
88
|
})
|
|
76
89
|
.join("\n");
|
|
77
90
|
|
|
@@ -181,6 +181,11 @@ async function getRenderPathsHydratedWithDomainFromDB(options?: {
|
|
|
181
181
|
};
|
|
182
182
|
}
|
|
183
183
|
|
|
184
|
+
async function getDomainDryRunFromDB(domain: string): Promise<boolean> {
|
|
185
|
+
const db = await readDB();
|
|
186
|
+
return !!db.domains[domain]?.dryRun;
|
|
187
|
+
}
|
|
188
|
+
|
|
184
189
|
async function getRenderMetadataFromDB() {
|
|
185
190
|
const db = await readDB();
|
|
186
191
|
return {
|
|
@@ -228,6 +233,7 @@ async function generateBuildReport(domain: string) {
|
|
|
228
233
|
export {
|
|
229
234
|
assertRenderIsValid,
|
|
230
235
|
generateBuildReport,
|
|
236
|
+
getDomainDryRunFromDB,
|
|
231
237
|
getRenderMetadataFromDB,
|
|
232
238
|
getRenderModeFromDB,
|
|
233
239
|
getRenderPathsHydratedWithDomainFromDB,
|
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
getMultiPageElements,
|
|
36
36
|
getPaginatedPages,
|
|
37
37
|
} from "./pages";
|
|
38
|
+
import { IndexNowCollector } from "./indexnow-payload";
|
|
38
39
|
import { getReferenceFieldData } from "./reference-fields";
|
|
39
40
|
import { getRenderPathsHydratedWithDomainFromDB } from "./render";
|
|
40
41
|
import { getPage } from "./sites";
|
|
@@ -80,6 +81,9 @@ async function createStore(options: {
|
|
|
80
81
|
const createdPages: number[] = [];
|
|
81
82
|
const buildProcessData: BuildProcessData = {};
|
|
82
83
|
|
|
84
|
+
// IndexNow delta collector. No-op when the feature flag is off.
|
|
85
|
+
const indexNowCollector = new IndexNowCollector();
|
|
86
|
+
|
|
83
87
|
// Get sites objects to publish and unpublish from this domain
|
|
84
88
|
const { sitesToPublish, sitesToUnpublish } = await getSitesToRender(domain);
|
|
85
89
|
|
|
@@ -115,6 +119,11 @@ async function createStore(options: {
|
|
|
115
119
|
const { id: siteId, slug: siteSlug, theme, favicon, pagesStatus } = site;
|
|
116
120
|
const siteDirName = siteId.toString();
|
|
117
121
|
|
|
122
|
+
// Snapshot of real pending-to-upload IDs before FROM_SCRATCH merges
|
|
123
|
+
// `active` into `pagesStatus.uploadPending` below. Used by IndexNow so
|
|
124
|
+
// unchanged `active` pages are not reported as upserts on FROM_SCRATCH.
|
|
125
|
+
const uploadPendingForIndexNow = [...pagesStatus.uploadPending];
|
|
126
|
+
|
|
118
127
|
allPagesToRemoveFromBuild.push(...pagesStatus.offlinePending, ...pagesStatus.deleted);
|
|
119
128
|
|
|
120
129
|
const {
|
|
@@ -312,6 +321,13 @@ async function createStore(options: {
|
|
|
312
321
|
// Save build data to store
|
|
313
322
|
await saveSitePagesInStore(siteIdName, griddoPageObjects);
|
|
314
323
|
|
|
324
|
+
indexNowCollector.collect({
|
|
325
|
+
siteId,
|
|
326
|
+
pageId: page.id,
|
|
327
|
+
pageObjects: griddoPageObjects,
|
|
328
|
+
uploadPending: uploadPendingForIndexNow,
|
|
329
|
+
});
|
|
330
|
+
|
|
315
331
|
// Update progress counter and log
|
|
316
332
|
progressCounter.current += 1;
|
|
317
333
|
progress.update(progressCounter.current);
|
|
@@ -355,6 +371,14 @@ async function createStore(options: {
|
|
|
355
371
|
// infor del render normalmente.
|
|
356
372
|
await saveRenderInfoInStore({ buildProcessData, createdPages, sitesToPublish }, domain);
|
|
357
373
|
|
|
374
|
+
try {
|
|
375
|
+
await indexNowCollector.flush({ domain, sitesToPublish, sitesToUnpublish });
|
|
376
|
+
} catch (err) {
|
|
377
|
+
GriddoLog.warn(
|
|
378
|
+
`indexnow: failed to write delta for ${domain}: ${(err as Error)?.message ?? String(err)}`,
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
358
382
|
return {
|
|
359
383
|
pagesToCreate: createdPages,
|
|
360
384
|
pagesToDelete: allPagesToRemoveFromBuild,
|
package/exporter/shared/envs.ts
CHANGED
|
@@ -35,6 +35,7 @@ function safeParseInt(value: string, fallback: number): number {
|
|
|
35
35
|
|
|
36
36
|
// Rendering
|
|
37
37
|
const GRIDDO_AI_EMBEDDINGS = envIsTruthy(env.GRIDDO_AI_EMBEDDINGS);
|
|
38
|
+
const GRIDDO_INDEXNOW_ENABLED = envIsTruthy(env.GRIDDO_INDEXNOW_ENABLED);
|
|
38
39
|
const GRIDDO_API_CONCURRENCY_COUNT = safeParseInt(env.GRIDDO_API_CONCURRENCY_COUNT || env.GRIDDO_RENDER_CONCURRENCY_COUNT || "10", 10);
|
|
39
40
|
const GRIDDO_ASSET_PREFIX = env.GRIDDO_ASSET_PREFIX || env.ASSET_PREFIX || env.GRIDDO_RENDER_ASSET_PREFIX;
|
|
40
41
|
const GRIDDO_BUILD_LOGS = envIsTruthy(env.GRIDDO_BUILD_LOGS || env.GRIDDO_RENDER_BUILD_LOGS);
|
|
@@ -64,6 +65,7 @@ export {
|
|
|
64
65
|
GRIDDO_BOT_USER,
|
|
65
66
|
GRIDDO_BUILD_LOGS,
|
|
66
67
|
GRIDDO_BUILD_LOGS_BUFFER_SIZE,
|
|
68
|
+
GRIDDO_INDEXNOW_ENABLED,
|
|
67
69
|
GRIDDO_PUBLIC_API_URL,
|
|
68
70
|
GRIDDO_REACT_APP_INSTANCE,
|
|
69
71
|
GRIDDO_RENDER_API_FETCH_RETRY_ATTEMPTS,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { RenderContext } from "@shared/context";
|
|
2
2
|
|
|
3
|
+
import { generateIndexNowKeyFile } from "@services/indexnow-key-file";
|
|
3
4
|
import { generateRobots } from "@services/robots";
|
|
4
5
|
import { generateSitemaps } from "@services/sitemaps";
|
|
5
6
|
|
|
@@ -8,4 +9,5 @@ export async function metaAction(context: RenderContext) {
|
|
|
8
9
|
|
|
9
10
|
await generateSitemaps(domain);
|
|
10
11
|
await generateRobots(domain);
|
|
12
|
+
await generateIndexNowKeyFile(domain);
|
|
11
13
|
}
|
|
@@ -5,6 +5,7 @@ import fsp from "node:fs/promises";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
|
|
7
7
|
import { deleteDisposableSiteDirs, deleteEmptyDirectories, mvDirs } from "../../../core/fs";
|
|
8
|
+
import { getIndexNowKeyFilename } from "../../../services/indexnow-key-file";
|
|
8
9
|
import { RENDER_MODE } from "../../../shared/types/render";
|
|
9
10
|
import { extractAssetsFromDist } from "../shared/extract-assets";
|
|
10
11
|
import { SyncRender } from "../shared/sync-render";
|
|
@@ -42,12 +43,16 @@ export async function syncAction(context: RenderContext<SSG>) {
|
|
|
42
43
|
if (renderMode === RENDER_MODE.INCREMENTAL) {
|
|
43
44
|
await deleteDisposableSiteDirs(previousDist);
|
|
44
45
|
|
|
46
|
+
const artifactsToCopyToExports = ["build-report.json", "llms.txt", "robots.txt"];
|
|
47
|
+
const indexNowKeyFile = getIndexNowKeyFilename(domain);
|
|
48
|
+
if (indexNowKeyFile) artifactsToCopyToExports.push(indexNowKeyFile);
|
|
49
|
+
|
|
45
50
|
const syncRender = new SyncRender({
|
|
46
51
|
src: currentDist,
|
|
47
52
|
dst: previousDist,
|
|
48
53
|
pagesToCreate,
|
|
49
54
|
pagesToDelete,
|
|
50
|
-
artifactsToCopyToExports
|
|
55
|
+
artifactsToCopyToExports,
|
|
51
56
|
});
|
|
52
57
|
|
|
53
58
|
await syncRender.execute();
|
|
@@ -144,11 +144,7 @@ class SyncRender {
|
|
|
144
144
|
// ../page-data/about-us/page-data.json // página con slug
|
|
145
145
|
// ../page-data/programs/page-data.json // página con slug
|
|
146
146
|
// ../page-data/index/page-data.json // <---- ¡página root index!
|
|
147
|
-
|
|
148
|
-
// Ojo: `composePath` viene normalizado por `removeTrailingSlash`
|
|
149
|
-
// (ver `scanPages`), así que la home llega como "" y no como "/".
|
|
150
|
-
const normalizedCompose =
|
|
151
|
-
page.composePath === "" || page.composePath === "/" ? "index" : page.composePath;
|
|
147
|
+
const normalizedCompose = page.composePath === "/" ? "index" : page.composePath;
|
|
152
148
|
const jsonTo = path.join(this.bundleDir, "page-data", normalizedCompose, "page-data.json");
|
|
153
149
|
|
|
154
150
|
this.state.htmlToAdd.push({ from: page.htmlPath, to: htmlTo });
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@griddo/cx",
|
|
3
3
|
"description": "Griddo SSG based on Gatsby",
|
|
4
|
-
"version": "11.
|
|
4
|
+
"version": "11.14.0",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Hisco <francis.vega@griddo.io>"
|
|
7
7
|
],
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"start-render": "node ./build/commands/start-render",
|
|
41
41
|
"end-render": "node ./build/commands/end-render",
|
|
42
42
|
"upload-search-content": "node ./build/commands/upload-search-content",
|
|
43
|
+
"notify-indexnow": "node ./build/commands/notify-indexnow",
|
|
43
44
|
"reset-render": "node ./build/commands/reset-render",
|
|
44
45
|
"// ONLY LOCAL SCRIPTS": "",
|
|
45
46
|
"prepare-assets-directory": "node ./build/commands/prepare-assets-directory",
|
|
@@ -61,7 +62,7 @@
|
|
|
61
62
|
},
|
|
62
63
|
"devDependencies": {
|
|
63
64
|
"@biomejs/biome": "2.3.4",
|
|
64
|
-
"@griddo/core": "11.
|
|
65
|
+
"@griddo/core": "11.14.0",
|
|
65
66
|
"@types/node": "20.19.4",
|
|
66
67
|
"@typescript/native-preview": "7.0.0-dev.20260401.1",
|
|
67
68
|
"cheerio": "1.1.2",
|
|
@@ -95,5 +96,5 @@
|
|
|
95
96
|
"publishConfig": {
|
|
96
97
|
"access": "public"
|
|
97
98
|
},
|
|
98
|
-
"gitHead": "
|
|
99
|
+
"gitHead": "d2c96b30e53ce16a9d06df7fd512308ae6fff64c"
|
|
99
100
|
}
|