From 2dac41a06e68a43eab871c5d8f1f30766d6c9197 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sat, 18 Jul 2026 23:30:56 -0400 Subject: [PATCH 01/87] Persist functional WordPress state in Cloudflare --- packages/runtime-cloudflare/README.md | 24 +- .../markdown-database-integration-runtime.zip | Bin 68589 -> 69079 bytes packages/runtime-cloudflare/src/worker.ts | 275 ++++++++---------- .../build-cloudflare-mdi-runtime-bundle.mjs | 25 +- scripts/cloudflare-local-gate.mjs | 134 +++++++-- tests/cloudflare-runtime.test.ts | 34 ++- 6 files changed, 301 insertions(+), 191 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 8952e10e..e827dcfd 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -1,19 +1,21 @@ # Cloudflare Runtime Gate -This additive integration is the first acceptance gate for [wp-codebox#1838](https://github.com/Automattic/wp-codebox/issues/1838). It compiles the current PHP 8.5 Asyncify Wasm asset for workerd, boots WordPress through Playground with the current SQLite integration release, executes PHP, and returns a Codebox runtime-command-result envelope containing the stable `wp-codebox/cloudflare-runtime-health/v1` health payload. +This candidate integration for [wp-codebox#1838](https://github.com/Automattic/wp-codebox/issues/1838) runs WordPress through PHP-WASM and Playground in Cloudflare Workers. -The Worker owns Cloudflare transport, PHP-WASM execution, and the caller-owned disposable SQLite cache. [MDI PR #126](https://github.com/Automattic/markdown-database-integration/pull/126), pinned at `94b9f875ffb8402d5e8eb726893a12324e20f45c`, supplies the constrained public primary runtime: normal MDI driver SQL writes are explicitly flushed to deterministic relative Markdown/JSON paths. R2 stores immutable canonical revisions and the current pointer; the Durable Object owns only the persisted lease, base-revision validation, and CAS pointer promotion. SQLite is never uploaded. This preserves cold reconstruction from canonical R2 files and concurrent mutation serialization without expanding MDI's storage-only boundary. +## Runtime Architecture -Existing evidence covers full WordPress initialization and canonical R2 revision behavior. This update changes the source relationship from ad hoc writes to MDI's public constrained runtime, adds source-level bundle and mutation guards, and supports local packaging verification. It does not claim a new remote deployment. +All normal WordPress, health, and synthetic mutation traffic is forwarded to the named `WordPressStateCoordinator` Durable Object. The object serializes requests and owns one runtime promise. A failed boot clears that promise so the next request can retry; a successful runtime remains available for subsequent requests. + +On cold start, the Durable Object reads the current R2 Markdown pointer and rebuilds PHP-WASM's disposable SQLite index from canonical MDI Markdown and JSON files. A missing or incomplete revision is bootstrapped from the packaged SQLite seed, then exported through MDI's public existing-cache storage attachment. The bootstrap updates `siteurl` and `home` through WordPress APIs using the request origin and sets the seeded admin password from `WORDPRESS_ADMIN_PASSWORD`; only WordPress's password hash is canonical. + +After each mutating HTTP request, the runtime invokes MDI's explicit request-boundary flush, collects canonical files, stores immutable content-addressed R2 objects and a revision manifest, then advances the current pointer while still serialized by the Durable Object. It does not persist SQLite. Existing manifests are reused when canonical file hashes have not changed. + +The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c7406f1f1ea6dc3` from [MDI PR #127](https://github.com/Automattic/markdown-database-integration/pull/127). The bundle generator, worker provenance, and source-contract test use the same revision. ## Verification -1. Install dependencies with `npm ci`. -2. Run `npm run build`; expect the existing Codebox packages to compile. -3. Run `npm run test:cloudflare-runtime`; expect the deterministic health response to contain the Codebox command-result schema and boot/execution evidence. -4. Run `npm run cloudflare:dry-run`; expect Wrangler to compile the Worker and report the Worker bundle size. This creates no Cloudflare resources. -5. Run `npm run cloudflare:local-gate`; expect two HTTP 200 health envelopes from local workerd and automatic server cleanup. This validates packaging and the real Worker boot path. -6. After configuring a Cloudflare account and an explicit deployment target, run `npm exec -- wrangler deploy --config packages/runtime-cloudflare/wrangler.jsonc`; request the deployed URL and expect `marker: "wp-codebox-cloudflare-runtime-health"`, WordPress and PHP versions, and completed initialization/execution evidence. -7. Remove the deployed Worker with `npm exec -- wrangler delete --config packages/runtime-cloudflare/wrangler.jsonc` when the remote gate is no longer needed. +1. Run `npm run test:cloudflare-runtime` for routing, canonical-state, filtered archive, source contract, and TypeScript coverage. +2. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. +3. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It injects a test admin password and verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin assets, PHP diagnostics, and the same login/post checks after a cold restart using the persisted R2/DO state. -The remote request in step 6 is the memory and boot acceptance gate. Cloudflare Workers enforces the 128 MB isolate limit remotely; a local workerd result does not establish that limit. +This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip index 80f0b63dd6f34f24727389b49ff5dd8cd0ba9246..030f12e7d9e66490914b0ed1269d53d9fd4590e7 100644 GIT binary patch delta 24065 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 diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 9e3d089e..8d61b342 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -15,7 +15,7 @@ import wordpressInstallSeed from "../assets/wordpress-install-seed.sqlite" const PHP_VERSION = "8.5.8" const WORDPRESS_ARCHIVE_URL = "https://wordpress.org/latest.zip" const SQLITE_INTEGRATION_ARCHIVE_URL = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip" -const MARKDOWN_DATABASE_INTEGRATION_REVISION = "94b9f875ffb8402d5e8eb726893a12324e20f45c" +const MARKDOWN_DATABASE_INTEGRATION_REVISION = "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3" const SITE_URL = "https://wp-codebox-runtime.invalid" const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite" const MARKDOWN_ROOT = "/wordpress/wp-content/markdown" @@ -24,11 +24,10 @@ const MARKDOWN_RESOLVED_INDEX_PATH = "/tmp/markdown-index-8133b4cf3c66.sqlite" const R2_MARKDOWN_POINTER_KEY = "sites/default/markdown/current.json" const R2_MARKDOWN_REVISION_PREFIX = "sites/default/markdown/revisions" const R2_MARKDOWN_OBJECT_PREFIX = "sites/default/markdown/objects" -const MUTATION_LEASE_VERSION = "mdi-canonical-v1" const SERIALIZED_MARKDOWN_MUTATION_CODE = `flush(); echo json_encode(['revisionValue' => $value, 'previousPostFound' => !empty($previous_rows), 'postId' => $post_id, 'wordpressVersion' => $wp_version, 'canonicalChanges' => $changes]);` -let bootPromise: Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> | undefined - interface Env { WORDPRESS_STATE: DurableObjectNamespace WORDPRESS_STATE_BUCKET: R2Bucket + WORDPRESS_ADMIN_PASSWORD?: string } export default { async fetch(request: Request, env: Env): Promise { const route = routeWorkerRequest(request) - if (route.kind === "r2-state" || route.kind === "r2-mutate") { - const expectedMethod = route.kind === "r2-mutate" ? "POST" : "GET" - if (request.method !== expectedMethod) { - return new Response(`WordPress state ${route.kind === "r2-mutate" ? "mutation" : "read"} requires ${expectedMethod}.`, { status: 405 }) - } - const coordinator = env.WORDPRESS_STATE.getByName("default") - return route.kind === "r2-state" - ? coordinator.fetch(new Request("https://coordinator/state")) - : runSerializedMarkdownMutation(env, coordinator) - } if (route.kind === "probe") return runBootProbe(route.phase) - - const runtime = await (bootPromise ??= bootWordPressRuntime( - "do-not-attempt-installing", - true, - false, - new Uint8Array(wordpressInstallSeed), - undefined, - undefined, - new URL(request.url).origin, - )) - if (route.kind === "wordpress") return toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) - const phpVersion = (await runtime.php.run({ code: " = Promise.resolve() + private runtimePromise: Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string; pointer: MarkdownPointer }> | undefined constructor( private readonly state: DurableObjectState, @@ -160,8 +119,9 @@ export class WordPressStateCoordinator implements DurableObject { } private async handleRequest(request: Request): Promise { - const action = new URL(request.url).pathname - if (request.method === "GET" && action === "/state") { + const route = routeWorkerRequest(request) + if (route.kind === "r2-state") { + if (request.method !== "GET") return new Response("WordPress state read requires GET.", { status: 405 }) const pointer = await readMarkdownPointer(this.env.WORDPRESS_STATE_BUCKET) return Response.json({ schema: "wp-codebox/cloudflare-wordpress-state/v1", @@ -169,95 +129,77 @@ export class WordPressStateCoordinator implements DurableObject { pointer, }) } - if (request.method !== "POST") return new Response("Method not allowed.", { status: 405 }) + if (route.kind === "r2-mutate") { + if (request.method !== "POST") return new Response("WordPress state mutation requires POST.", { status: 405 }) + return this.runSyntheticMutation(new URL(request.url).origin) + } + const runtime = await this.getRuntime(new URL(request.url).origin) + if (route.kind === "health") return this.health(runtime) + const response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) + if (!["GET", "HEAD", "OPTIONS"].includes(request.method)) await this.persistRuntime(runtime) + return response + } - if (action === "/begin") { - const existing = await this.state.storage.get("mutation-lease") - if (existing?.version === MUTATION_LEASE_VERSION && existing.expiresAt > Date.now()) { - return Response.json({ retryAfterMs: 1_000 }, { status: 409 }) - } - const pointer = await readMarkdownPointer(this.env.WORDPRESS_STATE_BUCKET) - const lease: MutationLease = { - token: crypto.randomUUID(), - version: MUTATION_LEASE_VERSION, - baseRevision: pointer?.revision ?? null, - expiresAt: Date.now() + 2 * 60_000, - } - await this.state.storage.put("mutation-lease", lease) - return Response.json({ ...lease, pointer } satisfies AcquiredLease) + private async getRuntime(origin: string) { + const runtime = this.runtimePromise ?? this.bootRuntime(origin) + this.runtimePromise = runtime + try { + return await runtime + } catch (error) { + if (this.runtimePromise === runtime) this.runtimePromise = undefined + throw error } - if (action === "/commit") { - const body = await request.json<{ token: string; pointer: MarkdownPointer }>() - const lease = await this.state.storage.get("mutation-lease") - if (!lease || lease.token !== body.token) return new Response("Mutation lease is invalid.", { status: 409 }) - const current = await readMarkdownPointer(this.env.WORDPRESS_STATE_BUCKET) - if ((current?.revision ?? null) !== lease.baseRevision) return new Response("Canonical revision changed during mutation.", { status: 409 }) - await this.env.WORDPRESS_STATE_BUCKET.put(R2_MARKDOWN_POINTER_KEY, JSON.stringify(body.pointer), { - httpMetadata: { contentType: "application/json" }, - }) - await this.state.storage.delete("mutation-lease") - return Response.json(body.pointer) + } + + private async bootRuntime(origin: string) { + const pointer = await readMarkdownPointer(this.env.WORDPRESS_STATE_BUCKET) + if (pointer && await isCompleteMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, pointer)) { + return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin), pointer } } - if (action === "/abort") { - const body = await request.json<{ token: string }>() - const lease = await this.state.storage.get("mutation-lease") - if (lease?.token === body.token) { - await this.state.storage.delete("mutation-lease") - } - return new Response(null, { status: 204 }) + if (!this.env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") + const seed = await bootWordPressRuntime("do-not-attempt-installing", true, true, new Uint8Array(wordpressInstallSeed), undefined, undefined, origin) + let seedExited = false + try { + await materializeMarkdownDatabaseIntegration(seed.php) + const passwordFile = "/tmp/wordpress-admin-password" + seed.php.writeFile(passwordFile, new TextEncoder().encode(this.env.WORDPRESS_ADMIN_PASSWORD)) + const output = (await seed.php.run({ code: canonicalSeedExportCode(passwordFile, origin) })).text.trim() + const changes = JSON.parse(output) as MarkdownChanges + validateMarkdownChanges(changes) + const files = collectRuntimeFiles(seed.php, MARKDOWN_ROOT) + const nextPointer = await persistMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, files) + await this.env.WORDPRESS_STATE_BUCKET.put(R2_MARKDOWN_POINTER_KEY, JSON.stringify(nextPointer), { httpMetadata: { contentType: "application/json" } }) + seed.php.exit() + seedExited = true + return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, files, new Uint8Array(markdownPrimaryBootstrapIndex), origin), pointer: nextPointer } + } catch (error) { + if (!seedExited) seed.php.exit() + throw error } - return new Response("Unknown coordinator action.", { status: 404 }) } -} -async function runSerializedMarkdownMutation(env: Env, coordinator: DurableObjectStub): Promise { - const lease = await acquireMutationLease(coordinator) - try { - const markdownFiles = lease.pointer - ? await readMarkdownRevision(env.WORDPRESS_STATE_BUCKET, lease.pointer) - : initialMarkdownFiles() - const runtime = await bootWordPressRuntime( - "do-not-attempt-installing", - true, - true, - undefined, - markdownFiles, - new Uint8Array(markdownPrimaryBootstrapIndex), - ) - let canonicalFiles: RuntimeFile[] - let mutation: { revisionValue: number; previousPostFound: boolean; postId: number; wordpressVersion: string; canonicalChanges: MarkdownChanges } - try { - const mutationOutput = (await runtime.php.run({ - code: SERIALIZED_MARKDOWN_MUTATION_CODE, - })).text.trim() - mutation = JSON.parse(mutationOutput) as typeof mutation - validateMarkdownChanges(mutation.canonicalChanges) - canonicalFiles = collectRuntimeFiles(runtime.php, MARKDOWN_ROOT) - } finally { - runtime.php.exit() + private async persistRuntime(runtime: { php: PHP; pointer: MarkdownPointer }): Promise { + const output = (await runtime.php.run({ code: "flush_canonical_writes(); echo 'flushed';" })).text.trim() + if (output !== "flushed") throw new Error("MDI did not confirm its canonical flush.") + const next = await persistMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT), runtime.pointer) + if (next.revision !== runtime.pointer.revision) { + await this.env.WORDPRESS_STATE_BUCKET.put(R2_MARKDOWN_POINTER_KEY, JSON.stringify(next), { httpMetadata: { contentType: "application/json" } }) + runtime.pointer = next } + } + + private async runSyntheticMutation(origin: string): Promise { + const runtime = await this.getRuntime(origin) + const mutationOutput = (await runtime.php.run({ code: SERIALIZED_MARKDOWN_MUTATION_CODE })).text.trim() + const mutation = JSON.parse(mutationOutput) as { revisionValue: number; previousPostFound: boolean; postId: number; wordpressVersion: string; canonicalChanges: MarkdownChanges } + validateMarkdownChanges(mutation.canonicalChanges) + await this.persistRuntime(runtime) + return Response.json({ schema: "wp-codebox/cloudflare-wordpress-mutation/v1", source: "durable-object-primary-runtime", ...mutation, canonicalFiles: collectRuntimeFiles(runtime.php, MARKDOWN_ROOT).length, markdownDatabaseIntegrationRevision: MARKDOWN_DATABASE_INTEGRATION_REVISION, sqlitePersisted: false, pointer: runtime.pointer }) + } - const nextPointer = await persistMarkdownRevision(env.WORDPRESS_STATE_BUCKET, canonicalFiles) - const commit = await coordinator.fetch(new Request("https://coordinator/commit", { - method: "POST", - body: JSON.stringify({ token: lease.token, pointer: nextPointer }), - })) - if (!commit.ok) throw new Error(`Unable to promote canonical Markdown revision: ${commit.status} ${await commit.text()}`) - return Response.json({ - schema: "wp-codebox/cloudflare-wordpress-mutation/v1", - source: lease.pointer ? "r2-markdown-revision" : "packaged-markdown-seed", - ...mutation, - canonicalFiles: canonicalFiles.length, - markdownDatabaseIntegrationRevision: MARKDOWN_DATABASE_INTEGRATION_REVISION, - sqlitePersisted: false, - pointer: nextPointer, - }) - } catch (error) { - await coordinator.fetch(new Request("https://coordinator/abort", { - method: "POST", - body: JSON.stringify({ token: lease.token }), - })) - throw error + private async health(runtime: { php: PHP; wordpressVersion: string }): Promise { + const phpVersion = (await runtime.php.run({ code: " 0 && !path.startsWith("/") && !path.split("/").includes("..") } -async function acquireMutationLease(coordinator: DurableObjectStub): Promise { - for (let attempt = 0; attempt < 300; attempt++) { - const response = await coordinator.fetch(new Request("https://coordinator/begin", { method: "POST" })) - if (response.ok) return response.json() - if (response.status !== 409) throw new Error(`Unable to acquire mutation lease: ${response.status} ${await response.text()}`) - await new Promise((resolve) => setTimeout(resolve, 1_000)) - } - throw new Error("Timed out waiting for the WordPress mutation lease.") -} - async function readMarkdownPointer(bucket: R2Bucket): Promise { const object = await bucket.get(R2_MARKDOWN_POINTER_KEY) return object ? object.json() : null @@ -310,7 +242,7 @@ async function readMarkdownRevision(bucket: R2Bucket, pointer: MarkdownPointer): return files } -async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[]): Promise { +async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], current?: MarkdownPointer): Promise { const manifestFiles: MarkdownManifestFile[] = [] for (const file of files) { const sha256 = await sha256Hex(file.bytes) @@ -318,6 +250,10 @@ async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[]): if (!await bucket.head(objectKey)) await bucket.put(objectKey, file.bytes) manifestFiles.push({ path: file.path, objectKey, sha256, size: file.bytes.byteLength }) } + if (current) { + const currentManifest = await readMarkdownManifest(bucket, current) + if (currentManifest && JSON.stringify(currentManifest.files) === JSON.stringify(manifestFiles)) return current + } const revision = crypto.randomUUID() const manifestKey = `${R2_MARKDOWN_REVISION_PREFIX}/${revision}.json` @@ -330,6 +266,55 @@ async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[]): return pointer } +async function readMarkdownManifest(bucket: R2Bucket, pointer: MarkdownPointer): Promise { + const object = await bucket.get(pointer.manifestKey) + return object ? object.json() : null +} + +async function isCompleteMarkdownRevision(bucket: R2Bucket, pointer: MarkdownPointer): Promise { + const manifest = await readMarkdownManifest(bucket, pointer) + if (!manifest || manifest.revision !== pointer.revision) return false + const paths = new Set(manifest.files.map((file) => file.path)) + if (!["_options/siteurl.json", "_options/home.json", "_tables/users.json", "_tables/usermeta.json", "_tables/terms.json", "_tables/term_taxonomy.json", "_tables/termmeta.json", "_tables/postmeta.json", "_tables/term_relationships.json", "_tables/comments.json", "_tables/commentmeta.json", "_tables/links.json"].every((path) => paths.has(path)) || !manifest.files.some((file) => file.path.endsWith(".md"))) return false + const users = manifest.files.find((file) => file.path === "_tables/users.json") + const usermeta = manifest.files.find((file) => file.path === "_tables/usermeta.json") + if (!users || !usermeta) return false + const [usersObject, usermetaObject] = await Promise.all([bucket.get(users.objectKey), bucket.get(usermeta.objectKey)]) + if (!usersObject || !usermetaObject) return false + const [usersRows, usermetaRows] = await Promise.all([ + usersObject.json>(), + usermetaObject.json>(), + ]) + const admin = usersRows.find((row) => row.user_login === "admin") + return Boolean(admin?.ID && usermetaRows.some((row) => row.user_id === admin.ID)) +} + +function canonicalSeedExportCode(passwordFile: string, origin: string): string { + return `ID); +$password = null; +update_option('siteurl', ${JSON.stringify(origin)}); +update_option('home', ${JSON.stringify(origin)}); +foreach (['class-wp-markdown-frontmatter-profiles.php', 'class-wp-markdown-storage.php', 'class-wp-markdown-driver.php', 'class-wp-markdown-search.php', 'class-wp-markdown-write-engine.php', 'class-wp-markdown-loader.php', 'class-wp-markdown-primary-storage-runtime.php'] as $file) require_once '/wordpress/wp-content/plugins/markdown-database-integration/inc/' . $file; +$connection = new WP_SQLite_Connection(['pdo' => $GLOBALS['@pdo'], 'path' => FQDB]); +$runtime = WP_Markdown_Primary_Storage_Runtime::bootstrap_existing_cache(['content_root' => '${MARKDOWN_ROOT}', 'state_root' => '${MARKDOWN_ROOT}'], $connection, defined('DB_NAME') && '' !== DB_NAME ? DB_NAME : 'database_name_here', array_filter(array_map('trim', explode(',', MARKDOWN_DB_EXCLUDED_TYPES))), $wpdb->prefix); +$driver = $runtime->get_driver(); +$prefix = $wpdb->prefix; +foreach ($driver->query("SELECT ID FROM " . $prefix . "posts") as $row) $driver->query("UPDATE " . $prefix . "posts SET post_modified = post_modified WHERE ID = " . (int) $row->ID); +foreach (['options' => 'option_id', 'users' => 'ID', 'usermeta' => 'umeta_id', 'postmeta' => 'meta_id', 'terms' => 'term_id', 'term_taxonomy' => 'term_taxonomy_id', 'termmeta' => 'meta_id', 'term_relationships' => 'object_id', 'comments' => 'comment_ID', 'commentmeta' => 'meta_id', 'links' => 'link_id'] as $table => $column) { + try { $driver->query("UPDATE " . $prefix . $table . " SET " . $column . " = " . $column); } catch (Throwable $ignored) {} +} +echo json_encode($runtime->flush());` +} + async function runBootProbe(phase: string): Promise { if (phase === "wordpress-archive" || phase === "sqlite-archive") { const archive = phase === "wordpress-archive" @@ -640,7 +625,7 @@ async function materializeWordPressServerFiles(php: PHP): Promise<{ materialized const { done, value: entry } = await reader.read() if (done) break const path = entry instanceof File ? entry.name : decoder.decode(entry.path) - if (!path.startsWith("wordpress/") || path.endsWith("/")) continue + if (!isWordPressServerFile(path) || path.endsWith("/")) continue const destination = `/${path}` const bytes = entry instanceof File ? new Uint8Array(await entry.arrayBuffer()) : entry.bytes @@ -653,12 +638,8 @@ async function materializeWordPressServerFiles(php: PHP): Promise<{ materialized } function isWordPressServerFile(path: string): boolean { - if (path.startsWith("wordpress/wp-admin/")) { - return path === "wordpress/wp-admin/includes/plugin.php" - || path === "wordpress/wp-admin/includes/class-wp-site-health.php" - } - return /\.(?:php|json|crt|html)$/.test(path) - || path.endsWith("/style.css") + if (path.startsWith("wordpress/wp-admin/")) return true + return /\.(?:php|json|crt|html|css|js|mjs|woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/.test(path) } function createPhpRuntime() { diff --git a/scripts/build-cloudflare-mdi-runtime-bundle.mjs b/scripts/build-cloudflare-mdi-runtime-bundle.mjs index bc4ef407..e25e54e3 100644 --- a/scripts/build-cloudflare-mdi-runtime-bundle.mjs +++ b/scripts/build-cloudflare-mdi-runtime-bundle.mjs @@ -1,11 +1,10 @@ -import { writeFile } from "node:fs/promises" +import { readFile, writeFile } from "node:fs/promises" import { decodeZip, encodeZip } from "@php-wasm/stream-compression" -const revision = "94b9f875ffb8402d5e8eb726893a12324e20f45c" +const revision = "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3" const archiveUrl = `https://codeload.github.com/Automattic/markdown-database-integration/zip/${revision}` +const sourceDirectory = process.env.MDI_RUNTIME_SOURCE const output = new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip", import.meta.url) -const response = await fetch(archiveUrl) -if (!response.ok || !response.body) throw new Error(`Unable to fetch Markdown Database Integration: ${response.status}.`) const runtimePaths = new Set([ "db.php", @@ -19,11 +18,19 @@ const runtimePaths = new Set([ "inc/class-wp-markdown-write-engine.php", ]) const runtimeFiles = [] -for await (const entry of decodeZip(response.body)) { - const separator = entry.name.indexOf("/") - const relative = separator === -1 ? "" : entry.name.slice(separator + 1) - if (!runtimePaths.has(relative)) continue - runtimeFiles.push(new File([await entry.arrayBuffer()], relative, { lastModified: 0 })) +if (sourceDirectory) { + for (const relative of runtimePaths) { + runtimeFiles.push(new File([await readFile(`${sourceDirectory}/${relative}`)], relative, { lastModified: 0 })) + } +} else { + const response = await fetch(archiveUrl) + if (!response.ok || !response.body) throw new Error(`Unable to fetch Markdown Database Integration: ${response.status}.`) + for await (const entry of decodeZip(response.body)) { + const separator = entry.name.indexOf("/") + const relative = separator === -1 ? "" : entry.name.slice(separator + 1) + if (!runtimePaths.has(relative)) continue + runtimeFiles.push(new File([await entry.arrayBuffer()], relative, { lastModified: 0 })) + } } if (runtimeFiles.length !== runtimePaths.size) throw new Error(`Expected ${runtimePaths.size} MDI runtime files, received ${runtimeFiles.length}.`) diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 8f50d19e..df6e48ad 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -1,25 +1,57 @@ import { spawn } from "node:child_process" +import { mkdtemp, rm } from "node:fs/promises" +import { tmpdir } from "node:os" +import { join } from "node:path" const port = 8792 -const url = `http://127.0.0.1:${port}/` -const child = spawn("npm", ["exec", "--", "wrangler", "dev", "--config", "packages/runtime-cloudflare/wrangler.jsonc", "--port", String(port)], { - cwd: process.cwd(), - stdio: ["ignore", "pipe", "pipe"], -}) - +const origin = `http://127.0.0.1:${port}` +const password = "cloudflare-runtime-test-password" +const stateDirectory = await mkdtemp(join(tmpdir(), "wp-codebox-cloudflare-gate-")) +const cookies = new Map() +let child let output = "" -child.stdout.on("data", (chunk) => { output += chunk }) -child.stderr.on("data", (chunk) => { output += chunk }) try { - await waitForServer() - await assertWordPressPage(url, "front page") + await startWorker() + const adminHtml = await login() + const post = await createPost(adminHtml) + const frontPage = await assertWordPressPage(`${origin}/${post.slug}/`, "published post") + assertIncludes(frontPage, post.title, "published post") await assertHealthResponse() - await assertWordPressPage(`${url}wp-admin/`, "WordPress admin route") - console.log("Cloudflare local runtime gate passed: front page, explicit health envelope, and admin route returned.") + await assertLinkedAssets(frontPage, "front-end") + await assertLinkedAssets(adminHtml, "admin") + await stopWorker() + + cookies.clear() + await startWorker() + const restartedAdmin = await login() + const restartedPost = await assertWordPressPage(`${origin}/${post.slug}/`, "post after cold restart") + assertIncludes(restartedPost, post.title, "post after cold restart") + await assertLinkedAssets(restartedAdmin, "admin after cold restart") + console.log("Cloudflare local runtime gate passed: login, authenticated REST post creation, frontend/admin assets, and cold-restart persistence.") } finally { + await stopWorker() + await rm(stateDirectory, { recursive: true, force: true }) +} + +async function startWorker() { + output = "" + child = spawn("npm", ["exec", "--", "wrangler", "dev", "--config", "packages/runtime-cloudflare/wrangler.jsonc", "--port", String(port), "--persist-to", stateDirectory, "--var", `WORDPRESS_ADMIN_PASSWORD:${password}`], { + cwd: process.cwd(), + // The host PAC resolves these public archive hosts through an unavailable local proxy. + env: { ...process.env, NO_PROXY: "wordpress.org,github.com,codeload.github.com", no_proxy: "wordpress.org,github.com,codeload.github.com" }, + stdio: ["ignore", "pipe", "pipe"], + }) + child.stdout.on("data", (chunk) => { output += chunk }) + child.stderr.on("data", (chunk) => { output += chunk }) + await waitForServer() +} + +async function stopWorker() { + if (!child || child.exitCode !== null) return child.kill("SIGTERM") await new Promise((resolve) => child.once("exit", resolve)) + child = undefined } async function waitForServer() { @@ -32,19 +64,81 @@ async function waitForServer() { throw new Error(`workerd did not start within 30 seconds:\n${output}`) } +async function assertLoginForm() { + const html = await assertWordPressPage(`${origin}/wp-login.php`, "login form") + if (!/]+id=["']loginform["']/i.test(html)) throw new Error("wp-login.php did not return the login form.") +} + +async function login() { + await assertLoginForm() + const form = new URLSearchParams({ log: "admin", pwd: password, redirect_to: `${origin}/wp-admin/`, testcookie: "1", "wp-submit": "Log In" }) + const response = await request(`${origin}/wp-login.php`, { method: "POST", headers: { "content-type": "application/x-www-form-urlencoded" }, body: form, redirect: "manual" }) + if (![301, 302].includes(response.status)) throw new Error(`Expected login redirect, received ${response.status}: ${await response.text()}`) + const location = response.headers.get("location") + if (!location?.includes("/wp-admin/")) throw new Error(`Login did not redirect to wp-admin: ${location}`) + return assertWordPressPage(new URL(location, origin), "wp-admin") +} + +async function createPost(adminHtml) { + const nonce = adminHtml.match(/"nonce":"([^"]+)"/)?.[1] + if (!nonce) throw new Error("wp-admin did not expose a REST nonce.") + const title = `Cloudflare durable post ${Date.now()}` + const response = await request(`${origin}/wp-json/wp/v2/posts`, { + method: "POST", + headers: { "content-type": "application/json", "x-wp-nonce": nonce }, + body: JSON.stringify({ title, content: "Persisted through the authenticated Cloudflare runtime.", status: "publish" }), + }) + const body = await response.text() + assertNoPhpDiagnostics(body, "REST post creation") + if (response.status !== 201) throw new Error(`Expected REST post creation, received ${response.status}: ${body}`) + const post = JSON.parse(body) + if (typeof post.slug !== "string" || post.title?.rendered !== title) throw new Error(`Unexpected REST post response: ${body}`) + return { slug: post.slug, title } +} + async function assertHealthResponse() { - const response = await fetch(`${url}?phase=health`) - if (response.status !== 200) throw new Error(`Expected HTTP 200, received ${response.status}: ${await response.text()}`) + const response = await request(`${origin}/?phase=health`) const body = await response.json() - if (body.schema !== "wp-codebox/cloudflare-runtime-health/v1" || body.marker !== "wp-codebox-cloudflare-runtime-health" || body.phpVersion !== "8.5.8" || typeof body.wordpressVersion !== "string" || body.execution?.schema !== "wp-codebox/runtime-command-result/v1" || body.execution?.status !== "ok") { - throw new Error(`Unexpected Cloudflare runtime health envelope: ${JSON.stringify(body)}`) - } + if (response.status !== 200 || body.schema !== "wp-codebox/cloudflare-runtime-health/v1" || body.marker !== "wp-codebox-cloudflare-runtime-health" || body.phpVersion !== "8.5.8" || typeof body.wordpressVersion !== "string" || body.execution?.status !== "ok") throw new Error(`Unexpected Cloudflare runtime health envelope: ${JSON.stringify(body)}`) } async function assertWordPressPage(target, label) { - const response = await fetch(target) + const response = await request(target) const body = await response.text() - if (response.status !== 200 || !response.headers.get("content-type")?.includes("text/html") || !/]/i.test(body)) { - throw new Error(`Expected an HTML ${label}, received ${response.status}: ${body}`) + assertNoPhpDiagnostics(body, label) + if (response.status !== 200 || !response.headers.get("content-type")?.includes("text/html") || !/]/i.test(body)) throw new Error(`Expected an HTML ${label}, received ${response.status}: ${body}`) + return body +} + +async function assertLinkedAssets(html, label) { + const links = [...html.matchAll(/<(?:link|script)\b[^>]*?\b(?:href|src)=["']([^"']+\.(?:css|js)(?:\?[^"']*)?)["']/gi)].slice(0, 2) + if (!links.length) throw new Error(`Expected ${label} HTML to link CSS or JavaScript assets.`) + for (const match of links) { + const response = await request(new URL(match[1], origin)) + const body = await response.text() + assertNoPhpDiagnostics(body, `${label} asset ${match[1]}`) + if (!response.ok || !body.length) throw new Error(`Missing ${label} asset ${match[1]}: ${response.status}`) + } +} + +async function request(target, options = {}) { + const headers = new Headers(options.headers) + if (cookies.size) headers.set("cookie", [...cookies].map(([name, value]) => `${name}=${value}`).join("; ")) + const response = await fetch(target, { ...options, headers }) + const setCookies = response.headers.getSetCookie?.() ?? (response.headers.get("set-cookie") ? [response.headers.get("set-cookie")] : []) + for (const cookie of setCookies) { + const [pair] = cookie.split(";", 1) + const separator = pair.indexOf("=") + if (separator > 0) cookies.set(pair.slice(0, separator), pair.slice(separator + 1)) } + return response +} + +function assertIncludes(html, expected, label) { + assertNoPhpDiagnostics(html, label) + if (!html.includes(expected)) throw new Error(`${label} did not contain ${JSON.stringify(expected)}.`) +} + +function assertNoPhpDiagnostics(body, label) { + if (/(?:Warning|Fatal error|Parse error|Deprecated)<\/b>:/i.test(body)) throw new Error(`PHP diagnostics in ${label}: ${body}`) } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 9a8232d7..31976c71 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -90,7 +90,7 @@ test("Cloudflare runtime packages the disposable WordPress install seed", async }) test("Cloudflare runtime pins and bundles the public constrained MDI runtime", async () => { - const revision = "94b9f875ffb8402d5e8eb726893a12324e20f45c" + const revision = "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3" const generator = await readFile(new URL("../scripts/build-cloudflare-mdi-runtime-bundle.mjs", import.meta.url), "utf8") const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const runtime = await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip", import.meta.url)) @@ -112,14 +112,40 @@ test("Cloudflare runtime pins and bundles the public constrained MDI runtime", a ]) }) -test("serialized Cloudflare mutations use the public MDI runtime and its flush paths", async () => { +test("Cloudflare routes WordPress and health through the serialized Durable Object runtime", async () => { + const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const materializer = worker.slice(worker.indexOf("async function materializeWordPressServerFiles"), worker.indexOf("function isWordPressServerFile")) + const predicate = worker.slice(worker.indexOf("function isWordPressServerFile"), worker.indexOf("function createPhpRuntime")) + + assert.match(worker, /return env\.WORDPRESS_STATE\.getByName\("default"\)\.fetch\(request\)/) + assert.match(worker, /private runtimePromise/) + assert.match(worker, /private async getRuntime\(origin: string\)/) + assert.match(worker, /if \(this\.runtimePromise === runtime\) this\.runtimePromise = undefined/) + assert.match(worker, /route\.kind === "health"\) return this\.health\(runtime\)/) + assert.match(worker, /runSyntheticMutation\(new URL\(request\.url\)\.origin\)/) + assert.match(worker, /private async runSyntheticMutation\(origin: string\)/) + assert.match(worker, /bootWordPressRuntime\("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision/) + assert.match(worker, /maxPhpInstances: 1/) + assert.match(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL,\s*\(entry: \{ path: Uint8Array \}\) => isWordPressServerFile\(decoder\.decode\(entry\.path\)\)\)/) + assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL\)(?!\s*,)/) + assert.match(predicate, /php\|json\|crt\|html\|css\|js\|mjs\|woff2\?\|ttf\|otf\|eot\|svg\|png\|jpe\?g\|gif\|webp\|avif\|ico/) + assert.match(predicate, /path\.startsWith\("wordpress\/wp-admin\/"\)\) return true/) +}) + +test("serialized Cloudflare mutations use MDI flush paths and complete canonical state", async () => { const source = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const mutation = source.slice(source.indexOf("const SERIALIZED_MARKDOWN_MUTATION_CODE"), source.indexOf("let bootPromise")) + const mutation = source.slice(source.indexOf("const SERIALIZED_MARKDOWN_MUTATION_CODE"), source.indexOf("interface Env")) assert.match(mutation, /WP_Markdown_Primary_Storage_Runtime::bootstrap/) assert.match(mutation, /new WP_SQLite_Connection\(\['pdo' => \$GLOBALS\['@pdo'\], 'path' => FQDB\]\)/) assert.match(mutation, /\$runtime->get_driver\(\)/) assert.match(mutation, /\$runtime->flush\(\)/) - assert.doesNotMatch(mutation, /\$wpdb->dbh|\{\$\{prefix\}|WP_Markdown_Storage|write_post|file_put_contents|wp_codebox_mdi_revision\.json/) + assert.doesNotMatch(mutation, /write_post|file_put_contents|wp_codebox_mdi_revision\.json/) assert.match(source, /validateMarkdownChanges\(mutation\.canonicalChanges\)/) + assert.match(source, /flush_canonical_writes\(\)/) + assert.match(source, /bootstrap_existing_cache/) + assert.match(source, /update_option\('siteurl'/) + assert.match(source, /_tables\/termmeta\.json/) + assert.match(source, /_tables\/users\.json/) + assert.match(source, /WORDPRESS_ADMIN_PASSWORD is required/) }) From 7159cf0e72db3b674ca745cc0e0e604df1b6cd6f Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 00:21:44 -0400 Subject: [PATCH 02/87] Prebuild canonical Cloudflare WordPress state --- package.json | 1 + packages/runtime-cloudflare/README.md | 9 +- ...n-database-integration-canonical-seed.json | 673 ++++++++++++++++++ ...wn-database-integration-canonical-seed.zip | Bin 0 -> 48499 bytes packages/runtime-cloudflare/src/worker.ts | 60 +- packages/runtime-cloudflare/wrangler.jsonc | 2 +- .../build-cloudflare-canonical-mdi-seed.php | 109 +++ tests/cloudflare-runtime.test.ts | 36 +- 8 files changed, 856 insertions(+), 34 deletions(-) create mode 100644 packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json create mode 100644 packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip create mode 100644 scripts/build-cloudflare-canonical-mdi-seed.php diff --git a/package.json b/package.json index 98719f99..c7db659d 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "generate:browser-fanout-aggregation-runtime": "tsx scripts/generate-browser-fanout-aggregation-runtime.ts", "generate:fanout-aggregation-contract": "tsx scripts/generate-fanout-aggregation-contract-fixture.ts", "generate:cloudflare-mdi-runtime-bundle": "node scripts/build-cloudflare-mdi-runtime-bundle.mjs", + "generate:cloudflare-canonical-mdi-seed": "npm run generate:cloudflare-mdi-runtime-bundle && php scripts/build-cloudflare-canonical-mdi-seed.php", "smoke": "tsx scripts/run-smoke.ts", "test:redaction": "tsx tests/redaction.test.ts", "test:cloudflare-runtime": "tsx tests/cloudflare-runtime.test.ts && node ./node_modules/typescript/bin/tsc -p packages/runtime-cloudflare --noEmit", diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index e827dcfd..bf1e0b40 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -6,7 +6,7 @@ This candidate integration for [wp-codebox#1838](https://github.com/Automattic/w All normal WordPress, health, and synthetic mutation traffic is forwarded to the named `WordPressStateCoordinator` Durable Object. The object serializes requests and owns one runtime promise. A failed boot clears that promise so the next request can retry; a successful runtime remains available for subsequent requests. -On cold start, the Durable Object reads the current R2 Markdown pointer and rebuilds PHP-WASM's disposable SQLite index from canonical MDI Markdown and JSON files. A missing or incomplete revision is bootstrapped from the packaged SQLite seed, then exported through MDI's public existing-cache storage attachment. The bootstrap updates `siteurl` and `home` through WordPress APIs using the request origin and sets the seeded admin password from `WORDPRESS_ADMIN_PASSWORD`; only WordPress's password hash is canonical. +On cold start, the Durable Object reads the current R2 Markdown pointer and rebuilds PHP-WASM's disposable SQLite index from canonical MDI Markdown and JSON files. A missing or incomplete revision materializes the packaged canonical MDI seed and boots one PHP-WASM primary runtime. The build-time PHP CLI generator creates that archive from `wordpress-install-seed.sqlite` through MDI's public `bootstrap_existing_cache()` API, validates its pinned MDI revision and input digest, and never packages SQLite. The runtime updates `siteurl` and `home` through WordPress APIs using the request origin and sets the admin password from `WORDPRESS_ADMIN_PASSWORD`; only WordPress's password hash is canonical. After each mutating HTTP request, the runtime invokes MDI's explicit request-boundary flush, collects canonical files, stores immutable content-addressed R2 objects and a revision manifest, then advances the current pointer while still serialized by the Durable Object. It does not persist SQLite. Existing manifests are reused when canonical file hashes have not changed. @@ -14,8 +14,9 @@ The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c ## Verification -1. Run `npm run test:cloudflare-runtime` for routing, canonical-state, filtered archive, source contract, and TypeScript coverage. -2. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. -3. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It injects a test admin password and verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin assets, PHP diagnostics, and the same login/post checks after a cold restart using the persisted R2/DO state. +1. Run `npm run generate:cloudflare-canonical-mdi-seed` to regenerate the deterministic canonical MDI archive and provenance manifest. +2. Run `npm run test:cloudflare-runtime` for routing, canonical-state, generator reproducibility, filtered archive, source contract, and TypeScript coverage. +3. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. +4. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It injects a test admin password and verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin assets, PHP diagnostics, and the same login/post checks after a cold restart using the persisted R2/DO state. This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json new file mode 100644 index 00000000..5c8d3657 --- /dev/null +++ b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json @@ -0,0 +1,673 @@ +{ + "schema": "wp-codebox/cloudflare-canonical-mdi-seed/v1", + "markdownDatabaseIntegrationRevision": "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3", + "wordpressInstallSeedSha256": "7212b89f1c01f98a23b89e9a2dd2b214306a85c10adb0355b9c7a6005b4fe822", + "archiveSha256": "d69b700c671ebee7f9eb01fa80d58c66072cf3966560d01cc188bd26b4818895", + "files": [ + { + "path": "_options/active_plugins.json", + "sha256": "84f7623dc86cb6a24106cef17f4783e9757cef9e67a9f9e810880b0be2ccfedb", + "size": 112 + }, + { + "path": "_options/admin_email.json", + "sha256": "3e8c0fc65ef749dc7b3ad8a5e1e081feea895ec2d640bae77b1e93739802fb22", + "size": 121 + }, + { + "path": "_options/admin_email_lifespan.json", + "sha256": "dfe263e469ce03384d7f9a5d5ee9c39a00ce9aeb27c8e23864cc4a55b8c9205f", + "size": 122 + }, + { + "path": "_options/auto_plugin_theme_update_emails.json", + "sha256": "fa121974c309fc507b60431b2ac0620b72bc2ca097d6c3ecc58612101820164c", + "size": 130 + }, + { + "path": "_options/auto_update_core_dev.json", + "sha256": "ca17ac00a45db6600f256ffda899759c7b28bd27b97c821957fd97d773293972", + "size": 119 + }, + { + "path": "_options/auto_update_core_major.json", + "sha256": "91522b117f1f9d82d01f2bb0b8b2af7004c9e40d6b70764fe9b5b6a4df0686e3", + "size": 121 + }, + { + "path": "_options/auto_update_core_minor.json", + "sha256": "1df4a26373a11e6ea154e74448c79e998cb92a04218aa5bb30ba3121795dc38e", + "size": 121 + }, + { + "path": "_options/avatar_default.json", + "sha256": "a646a1b9250cd44137ba1d212f1988f8691345ca9b0b8a2d069fde77758183ce", + "size": 113 + }, + { + "path": "_options/avatar_rating.json", + "sha256": "0809528307b143405f0a88bbcca5fedc018d9e2acfdb9bc9a30177f54a3355f5", + "size": 106 + }, + { + "path": "_options/blog_charset.json", + "sha256": "5b90a1c381c47a74feccccdb87c039f6286bc83abb5d9a0bbccad84621503ce8", + "size": 109 + }, + { + "path": "_options/blog_public.json", + "sha256": "7058ed5728f6a6cc83871ca37d427b1d90d9811e336e1af79c6e8cbf2bb76ed4", + "size": 104 + }, + { + "path": "_options/blogdescription.json", + "sha256": "759154f66c8b9bed0810ce36349fc7a2ed8a0da3acf6c59a2e6e01f863d89c5b", + "size": 106 + }, + { + "path": "_options/blogname.json", + "sha256": "2d121fcce756b914f84be99b8a0a75a1003e6a21f46e4c9d1dcf10502aa18de2", + "size": 119 + }, + { + "path": "_options/category_base.json", + "sha256": "e776661b42daf03cb607978d2893dd04e7c2a84c37605f2e0b734d849b7bd311", + "size": 105 + }, + { + "path": "_options/close_comments_days_old.json", + "sha256": "fa22450262fdf9ed63965d4f8f3b7089da80affccd0d2ffe4163adfafd45fad2", + "size": 117 + }, + { + "path": "_options/close_comments_for_old_posts.json", + "sha256": "8845ab30d066d479ef7f885bb3614d183a9e37fb4ef671a2130bea5330e54fd0", + "size": 121 + }, + { + "path": "_options/comment_max_links.json", + "sha256": "ac32c6babef6c35a0ba6250a5e8f6aa26ef022cc610eab3d8c4f4dbdc5415598", + "size": 110 + }, + { + "path": "_options/comment_moderation.json", + "sha256": "cb91c908690bad4fd53b0315f0289dc5d5f2172a9a982749a5a649633225b187", + "size": 111 + }, + { + "path": "_options/comment_order.json", + "sha256": "aef741e2d655aef8e58cb30de476b0907db86ee94e1fa1b1e35571a8eaeab088", + "size": 108 + }, + { + "path": "_options/comment_previously_approved.json", + "sha256": "1df49c4dbac9c0fc878d13a47db6fad2ef4751233711301e9d5bf5e6a1f9615b", + "size": 120 + }, + { + "path": "_options/comment_registration.json", + "sha256": "2bdcfad07e83fbb8a24360b77d45a7396d07c1e181d85876f6163e72d4e1fa07", + "size": 113 + }, + { + "path": "_options/comments_notify.json", + "sha256": "7c48f80892ad18b47ec30dd81c082d2ef3dd9638a196bc0d3693a2409dbfad08", + "size": 108 + }, + { + "path": "_options/comments_per_page.json", + "sha256": "77d9d1ecf32a75afbe6d22abec5ecf2a0165daf08c26e7250fb977ca2b810abf", + "size": 111 + }, + { + "path": "_options/date_format.json", + "sha256": "225acb7b5145a429f3986a9edff8ea6caab7a86bd2694d68bbaaf0e9a9de0fb7", + "size": 109 + }, + { + "path": "_options/db_version.json", + "sha256": "e6e8488e2d652760fffd3b83306adffcb307ff95db8ebaa06043c0f823e816f7", + "size": 107 + }, + { + "path": "_options/default_category.json", + "sha256": "77c338c7594f53d0004511b56fdba06d2512c833b591d088fd118805eb309294", + "size": 109 + }, + { + "path": "_options/default_comment_status.json", + "sha256": "f0710a0728a4f6755c288b1bf95eda4aca4e81b7251a995b7b9de5610e9a4492", + "size": 118 + }, + { + "path": "_options/default_comments_page.json", + "sha256": "0089107a3f280197fa8f9856a82f3ecbc548acd20a6d326498a9ad6e8107e7f7", + "size": 119 + }, + { + "path": "_options/default_email_category.json", + "sha256": "8cb37eed3a7459d850180930f59b1a7d9e0aa7a535da27ee78974510db3bed16", + "size": 115 + }, + { + "path": "_options/default_link_category.json", + "sha256": "d14edfff2d30009384b26ba04ef7472b4dd8561ca4af035667b285260c902cea", + "size": 114 + }, + { + "path": "_options/default_ping_status.json", + "sha256": "425e4ad0dd59c17459d9887f04899ae78d90056975d9c403cb3d6efeacbe39c8", + "size": 115 + }, + { + "path": "_options/default_pingback_flag.json", + "sha256": "f7a53109e13e80512ef9afe8e488284a3b64d0a02b5887dea8d2238043b82dde", + "size": 114 + }, + { + "path": "_options/default_post_format.json", + "sha256": "2798b064f77d20c8ce901741b5ea3725dba8707da457be3b1d3f18652e0fec8c", + "size": 112 + }, + { + "path": "_options/default_role.json", + "sha256": "c62ab3f9c4fcc9d0c606ac7c9237188e78b9a25419a1c0aad435772f0d77ca73", + "size": 114 + }, + { + "path": "_options/disallowed_keys.json", + "sha256": "b6c1c8a4cdfc3e45d96b662c1dcce2d144b9ab1326b6d4b1eb44247b219a0a6e", + "size": 108 + }, + { + "path": "_options/finished_splitting_shared_terms.json", + "sha256": "6522a73a1d62194b4701c45020270b3093f517f135a2fad3ee51eccd0326d859", + "size": 124 + }, + { + "path": "_options/fresh_site.json", + "sha256": "1a9141f58fcd724358183b9ab5cb8d9d5c46264af89c4409548baf6e2d6adb21", + "size": 106 + }, + { + "path": "_options/gmt_offset.json", + "sha256": "8b8819575c68fead5d35d50538be944928a8d03780b9b287521d32a70ab64787", + "size": 103 + }, + { + "path": "_options/hack_file.json", + "sha256": "86483dceca4c1db0576c7da3cd9d68f42ef843982a86a65183a630c41ae468ed", + "size": 102 + }, + { + "path": "_options/home.json", + "sha256": "787527e6e0af4882bcbaddbd363f6ae75e3ed088cbff5284cee5b64e21255531", + "size": 129 + }, + { + "path": "_options/html_type.json", + "sha256": "d7e80733a07ca37dbbdb9276c69ae263d00e65ab9b564ad62a348bde76c0a462", + "size": 110 + }, + { + "path": "_options/image_default_align.json", + "sha256": "8f361d6b96a03e2d39dd855093e5e6960b57812b397c9f5d00c04f3ed2effa17", + "size": 111 + }, + { + "path": "_options/image_default_link_type.json", + "sha256": "616fa770086485cf01c1dfdfe3e192daf3957d0b966447353a13ae38bff6cf0a", + "size": 119 + }, + { + "path": "_options/image_default_size.json", + "sha256": "41bfbf0a39f500f49ee729856edbee68b4a631845b082196db32c65102fbf437", + "size": 110 + }, + { + "path": "_options/initial_db_version.json", + "sha256": "5ccf972a368e39ad7788bada4fe624fb4aeda14b0521b952035400abff2816bb", + "size": 116 + }, + { + "path": "_options/large_size_h.json", + "sha256": "b0a49f2caf5e7db1077c1e2f38588df98398ff1b68f23778cb51f52ac973d118", + "size": 108 + }, + { + "path": "_options/large_size_w.json", + "sha256": "cedf9b85e15af4034ff98a8fcec18ebcbfab155873e20e46f7120347b26f4bb5", + "size": 108 + }, + { + "path": "_options/link_manager_enabled.json", + "sha256": "d41d67c8ad4c55d75d83406a06d94a945e352101c7d82264eb63e1bb0bfcf522", + "size": 113 + }, + { + "path": "_options/links_updated_date_format.json", + "sha256": "56557527a25e8ed5a8ea38472ecb12853efa8c3a94781c39392f0dd88d2a529b", + "size": 129 + }, + { + "path": "_options/mailserver_login.json", + "sha256": "7df9ef92bbfc1a63932ac7d9dbee4dcb3d78ccf7f048e5bb0c3a42b0f7387bef", + "size": 125 + }, + { + "path": "_options/mailserver_pass.json", + "sha256": "d9ef05301ce28ef7814c3a8b2134f812c54e84aac535801892149dcfd37f94dc", + "size": 107 + }, + { + "path": "_options/mailserver_port.json", + "sha256": "a7a7acbdf22ca209b3d8a10c1735497d0e9614913386b4924d4536552d0d3710", + "size": 110 + }, + { + "path": "_options/mailserver_url.json", + "sha256": "6296a308daa4a21d74dd0b72251f55a6d9c5fd8eb4120b2ad8ecb16f72917c0b", + "size": 122 + }, + { + "path": "_options/medium_large_size_h.json", + "sha256": "e7c1d41f7ad0f0be61591807d025b2d462aebaf9ee3d036df819e952b62783e7", + "size": 112 + }, + { + "path": "_options/medium_large_size_w.json", + "sha256": "7ee7a64d7ff9bacf9057d30d9cdf0708d14093410de7ec894c1da18a5a44239a", + "size": 114 + }, + { + "path": "_options/medium_size_h.json", + "sha256": "955e26ba1067f56ae17363304ff452f06b179e5b469b9d226948a0803faf459c", + "size": 108 + }, + { + "path": "_options/medium_size_w.json", + "sha256": "b8e09c4cab146d31b96426b6a75b4e62d1051cdf3fd0308820f214c557e910aa", + "size": 108 + }, + { + "path": "_options/moderation_keys.json", + "sha256": "9dace2230ae3d9e384d086f9057306abd7ab2f1aaef2b73765412a4b90f51465", + "size": 108 + }, + { + "path": "_options/moderation_notify.json", + "sha256": "ac04d3bd2b98f352666b61418c74e6284e5a80ed390cf7174397fc1f0dc776be", + "size": 110 + }, + { + "path": "_options/page_comments.json", + "sha256": "8606785e458036411e6d54e0b5cef6462aab2bd03563bf23e83793a252fb4885", + "size": 106 + }, + { + "path": "_options/page_for_posts.json", + "sha256": "f3ff3a8b005a98457d3aecf694b0c5cc365a574e096784853a28295621d2a3db", + "size": 107 + }, + { + "path": "_options/page_on_front.json", + "sha256": "aff01d58fb09d4c284db6550484612b378ef54a95b2843faba230e57f3ab6da3", + "size": 106 + }, + { + "path": "_options/permalink_structure.json", + "sha256": "bf6281020411182117dfad8692ff8ac95b61deeb0d83ca37febc8215b83e0367", + "size": 147 + }, + { + "path": "_options/ping_sites.json", + "sha256": "95be92fe1144a4321f5e3c5df763503310b886950e8af29abd40064373bac1f3", + "size": 129 + }, + { + "path": "_options/posts_per_page.json", + "sha256": "125ee4ccc001325bf3ee58fbfeff5ee52e1c29e8d51b1dfd9012b790b0ac29d6", + "size": 108 + }, + { + "path": "_options/posts_per_rss.json", + "sha256": "7dda3ac6019d100e3a5bc5ad52b232aded5f338ea3194123c50f6685d1f796cf", + "size": 107 + }, + { + "path": "_options/recently_edited.json", + "sha256": "00c88af938f966e12a90d8bfef9b3f8f6cf12cc4bb7164616014a3536fb987a0", + "size": 108 + }, + { + "path": "_options/require_name_email.json", + "sha256": "5b646ce53da9f76ebd26dd28295016a278d7047c8ce8b5ecea5619ee754ad6bd", + "size": 111 + }, + { + "path": "_options/rewrite_rules.json", + "sha256": "2fbd47a28f55fe14816bd0210bdbafb30833bb83a63feed7a397a372bedab3b6", + "size": 105 + }, + { + "path": "_options/rss_use_excerpt.json", + "sha256": "6d7413bac992817e82047f9217d6495038b6fdc5b783dc09326437a489f49d35", + "size": 108 + }, + { + "path": "_options/show_avatars.json", + "sha256": "307d790f64b7921dcd03097366755b01ad4b24d18e9cf752d7e154c623797a9e", + "size": 105 + }, + { + "path": "_options/show_comments_cookies_opt_in.json", + "sha256": "3c6b3bd92fac9402edceeddc98ba873cd52af9750f5f8033a1ea12525035ec72", + "size": 121 + }, + { + "path": "_options/show_on_front.json", + "sha256": "34d9c066eb8dd1f36c14e67e5b34ba4cfb041ebdc9d9ba1be2b78869879bb002", + "size": 110 + }, + { + "path": "_options/sidebars_widgets.json", + "sha256": "a8039e2a770d738e6cb1c82c8d254751f05c20b5ff96158f192dbfab03f3bca4", + "size": 327 + }, + { + "path": "_options/site_icon.json", + "sha256": "55a93bb571dbf736f5ab6bbbf72ad599f390b7af8863e66da65f1dfde649e950", + "size": 102 + }, + { + "path": "_options/siteurl.json", + "sha256": "23d3c018db48d8d92a5f63a554abce9c9a37e2306af826f728dbee52ecd79a8c", + "size": 132 + }, + { + "path": "_options/start_of_week.json", + "sha256": "194b39a6b949e6141ba613d87105d1fb3ed44ca52a6a55f34d7bd1199de40ec2", + "size": 105 + }, + { + "path": "_options/sticky_posts.json", + "sha256": "58be52e291a2ab9a6abb582e2ad523a3b63646660a60c25c7110d51f7ab18c5c", + "size": 110 + }, + { + "path": "_options/stylesheet.json", + "sha256": "ad0c99868d70a03393b6551f378d8fb3f784713125beab43823ab6a1a87c1ae5", + "size": 118 + }, + { + "path": "_options/tag_base.json", + "sha256": "f8b410c18313b01ae2ab8505efcfc43273ae675284790add48dd8bef1827bc23", + "size": 100 + }, + { + "path": "_options/template.json", + "sha256": "4bb5f2dcc89d2ca9281434c74b47e9049f65dadc53d94754487be6ea32430e1d", + "size": 116 + }, + { + "path": "_options/thread_comments.json", + "sha256": "10416951abb4fb35cafc0b90a178a67f669dafb8334bea3a23ee862eb9243e89", + "size": 108 + }, + { + "path": "_options/thread_comments_depth.json", + "sha256": "b093c8b7802638c89102df0c3726fac5419ae14d8fe42a81b2ea44861f253812", + "size": 114 + }, + { + "path": "_options/thumbnail_crop.json", + "sha256": "8b19055800744b5b6e9443a851d8332b536c7f2d6c701533ae4de1b5821e39d4", + "size": 107 + }, + { + "path": "_options/thumbnail_size_h.json", + "sha256": "771845078b327ecefcac2e7868f06993169dd6d7076010a69c9e62135dc1714a", + "size": 111 + }, + { + "path": "_options/thumbnail_size_w.json", + "sha256": "9b2bff86022546608383cc567173404857b80499f850636bb09115a01d4f7fd5", + "size": 111 + }, + { + "path": "_options/time_format.json", + "sha256": "da1ee66de5a9b8b3db5faade6962f9cd6df92c87dc2421e555b99741d8c299c7", + "size": 108 + }, + { + "path": "_options/timezone_string.json", + "sha256": "c118479443b84fc55b3194f050fc16135d68382601acb441f23a2424f591fdd2", + "size": 107 + }, + { + "path": "_options/uninstall_plugins.json", + "sha256": "33ad0795e50835929266282132a7342d4ba858ae7e3531db4d6b1c374b41e903", + "size": 116 + }, + { + "path": "_options/upload_path.json", + "sha256": "ebc17ee3ee1783f8752defa217383cb55b6da2f50129fae5d4fbbedcce3ef327", + "size": 103 + }, + { + "path": "_options/upload_url_path.json", + "sha256": "73e100232174541fe0648008c4675a183cd243da3f6a62b160639f5a55bf5ee5", + "size": 107 + }, + { + "path": "_options/uploads_use_yearmonth_folders.json", + "sha256": "00d94c46b28b1f5285ae044172d096614a83bb29bfe62cef0e77a8da7106fe91", + "size": 122 + }, + { + "path": "_options/use_balanceTags.json", + "sha256": "3b877f85030eed378f9c4f72952fac8b2bc393bb39cfb80a3be2a8ad9e7ba09f", + "size": 107 + }, + { + "path": "_options/use_smilies.json", + "sha256": "02842bbacf762cb6da985249d99621b890e98bf3f834ab72ba5c8065b19324f1", + "size": 103 + }, + { + "path": "_options/use_trackback.json", + "sha256": "04dadd1da7b6da72c0642d8e95e689703ba342771eddf10ae9d3ae81d9a57b89", + "size": 106 + }, + { + "path": "_options/user_count.json", + "sha256": "1b2df19c13d13df8aa360a707276f77a61399820d1f0af6358f2eda7237b3d51", + "size": 105 + }, + { + "path": "_options/users_can_register.json", + "sha256": "c84ca74aa31a4c9c0d3627f01f99821827fe5aca7b9feba8423e9b16e7987844", + "size": 110 + }, + { + "path": "_options/widget_archives.json", + "sha256": "7200c1c7380dbaee12b7c8436abf525a64f8745b125c0db65ee0baf26b42a7a9", + "size": 142 + }, + { + "path": "_options/widget_block.json", + "sha256": "7e71a999a47d4b8f791ee9d41979605663dcc835e00556b7826f6456a8659c59", + "size": 1033 + }, + { + "path": "_options/widget_calendar.json", + "sha256": "bbc6acdba7e0fb0093b330b40a3436ea24bf8b327c3f1325c64c265d01e3bae7", + "size": 142 + }, + { + "path": "_options/widget_categories.json", + "sha256": "03af5dae8453aa02310e1c2819cadccdaaa852f255f660263199c732fcbb3d0f", + "size": 115 + }, + { + "path": "_options/widget_custom_html.json", + "sha256": "9a4ab35bff2d636e49d752fe9ada4ba558c1f04ee86366ffd10fb97e61c048fe", + "size": 145 + }, + { + "path": "_options/widget_media_audio.json", + "sha256": "bcb2001e9bb043499d3a0264c9c7c2c14e1665a9a6730af1b12a23c2702dfdc7", + "size": 145 + }, + { + "path": "_options/widget_media_gallery.json", + "sha256": "a648f6979972cd36cc2dc9dc55c6c31f18b0352d95a821e67a96be2260c6ee37", + "size": 147 + }, + { + "path": "_options/widget_media_image.json", + "sha256": "6a5c88ab15a0490b9e09d99791b2bc780f4bcf02478ac5d414db8d36b78629b6", + "size": 145 + }, + { + "path": "_options/widget_media_video.json", + "sha256": "712b8c9eefe0da8a5b30296c93d5f28fb077db03eb23729b1c3cc8ae21b96cb7", + "size": 145 + }, + { + "path": "_options/widget_meta.json", + "sha256": "c788c270d1e70a6717252e17702989e8f9dae0f4d25eb1077f2755fd5c1913d9", + "size": 138 + }, + { + "path": "_options/widget_nav_menu.json", + "sha256": "dfc3adb04a939a700dd5cf790374968f6165334c84aa9bf25dc480908415f64c", + "size": 142 + }, + { + "path": "_options/widget_pages.json", + "sha256": "0374b26868c3926f4576ee9ff8a3727390b5e2eb44e7a5f9df060319258a0243", + "size": 139 + }, + { + "path": "_options/widget_recent-comments.json", + "sha256": "f26c453716ed317c240b7e75fbd8df1d84f51bd24da96c5760d8e5b9d0ee929a", + "size": 149 + }, + { + "path": "_options/widget_recent-posts.json", + "sha256": "4ff8e3a64d75e2d269f22f560d9fb3f3c9d2d2065cdc01c98474f7199c9ac2b1", + "size": 146 + }, + { + "path": "_options/widget_rss.json", + "sha256": "447fe26ace5c658ddde6305edc1508e62da8d63e108c8318431a431b121c57ed", + "size": 108 + }, + { + "path": "_options/widget_search.json", + "sha256": "a0e602b6bd631db1fc2959e11bc70ec56526c73b762264b06c514623c3367768", + "size": 140 + }, + { + "path": "_options/widget_tag_cloud.json", + "sha256": "cd66f951d63141a6cf2b2fe5cf590bf0c163635f4df2c850c31c06420662fec3", + "size": 143 + }, + { + "path": "_options/widget_text.json", + "sha256": "304a22a3c6178981b9a94f54811277701829a148f022ec60bc7deb2de88dbaab", + "size": 109 + }, + { + "path": "_options/wp_attachment_pages_enabled.json", + "sha256": "63945d6ce74619978fe316e6fc6a920c0baf76b30601296d7fa23ee18fa12945", + "size": 120 + }, + { + "path": "_options/wp_force_deactivated_plugins.json", + "sha256": "f2fa84cefc7711870930ac46816d41a54923c6a317d74c3ad6ff2afee6f51c87", + "size": 126 + }, + { + "path": "_options/wp_notes_notify.json", + "sha256": "d80340b73a05544487f331630ae1808592c549769cfbad04cbc29e898a6bb366", + "size": 109 + }, + { + "path": "_options/wp_page_for_privacy_policy.json", + "sha256": "69ce2b256b40c14860cf9e06ce3a2551056f429c7e33d36a892ad94e7ddedce9", + "size": 119 + }, + { + "path": "_options/wp_user_roles.json", + "sha256": "5716712b2f6035bfc1eef064704964f3a6a7f507307e3ae4546f99e888d1ff1a", + "size": 3503 + }, + { + "path": "_tables/commentmeta.json", + "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "size": 2 + }, + { + "path": "_tables/comments.json", + "sha256": "137760295dc50e80ccb11d98a5771a8019ead13c3546f2b5b3db0aa6742a98a6", + "size": 788 + }, + { + "path": "_tables/links.json", + "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "size": 2 + }, + { + "path": "_tables/postmeta.json", + "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "size": 2 + }, + { + "path": "_tables/term_relationships.json", + "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "size": 2 + }, + { + "path": "_tables/term_taxonomy.json", + "sha256": "436a974aecbde99424fde9f5fc67f86d95d8af3e8123ef4212da91a7f8e2823a", + "size": 167 + }, + { + "path": "_tables/termmeta.json", + "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", + "size": 2 + }, + { + "path": "_tables/terms.json", + "sha256": "bf86799a66152064b3039356cd1b4aa6dc10a8b1e20856ed083e98683a3cc270", + "size": 127 + }, + { + "path": "_tables/usermeta.json", + "sha256": "b1af5af0b34981f2b5fada28b1ff648b63d1f191b2158947ca627a27baccd542", + "size": 1875 + }, + { + "path": "_tables/users.json", + "sha256": "a59e1935b9636e9e7fe2fa2a9699e3d75fa055f91bf05a3a298cb97f14c463b7", + "size": 431 + }, + { + "path": "page/privacy-policy.md", + "sha256": "a2a87d2d22e084f5c8feb45329881f24f22dc83e0882b1b2cfe8eb5b05decc01", + "size": 6118 + }, + { + "path": "page/sample-page.md", + "sha256": "066c9dcb78ab3c93c719161e4d819bd136c12dda7b3dacf3497839a3ab927609", + "size": 1923 + }, + { + "path": "post/hello-world.md", + "sha256": "3accc9d34069ca26be508b15ce84de25f4740b521b8acf12c87e97003c77fc11", + "size": 700 + } + ] +} diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip new file mode 100644 index 0000000000000000000000000000000000000000..c744ae19dae12c95635e8314eb4b66642fee774b GIT binary patch literal 48499 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 +} + export class WordPressStateCoordinator implements DurableObject { private tail: Promise = Promise.resolve() private runtimePromise: Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string; pointer: MarkdownPointer }> | undefined @@ -157,23 +166,18 @@ export class WordPressStateCoordinator implements DurableObject { return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin), pointer } } if (!this.env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") - const seed = await bootWordPressRuntime("do-not-attempt-installing", true, true, new Uint8Array(wordpressInstallSeed), undefined, undefined, origin) - let seedExited = false + const files = await packagedCanonicalMarkdownSeed() + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, files, new Uint8Array(markdownPrimaryBootstrapIndex), origin) try { - await materializeMarkdownDatabaseIntegration(seed.php) const passwordFile = "/tmp/wordpress-admin-password" - seed.php.writeFile(passwordFile, new TextEncoder().encode(this.env.WORDPRESS_ADMIN_PASSWORD)) - const output = (await seed.php.run({ code: canonicalSeedExportCode(passwordFile, origin) })).text.trim() - const changes = JSON.parse(output) as MarkdownChanges - validateMarkdownChanges(changes) - const files = collectRuntimeFiles(seed.php, MARKDOWN_ROOT) - const nextPointer = await persistMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, files) + runtime.php.writeFile(passwordFile, new TextEncoder().encode(this.env.WORDPRESS_ADMIN_PASSWORD)) + const output = (await runtime.php.run({ code: canonicalBootstrapSetupCode(passwordFile, origin) })).text.trim() + if (output !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") + const nextPointer = await persistMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT)) await this.env.WORDPRESS_STATE_BUCKET.put(R2_MARKDOWN_POINTER_KEY, JSON.stringify(nextPointer), { httpMetadata: { contentType: "application/json" } }) - seed.php.exit() - seedExited = true - return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, files, new Uint8Array(markdownPrimaryBootstrapIndex), origin), pointer: nextPointer } + return { ...runtime, pointer: nextPointer } } catch (error) { - if (!seedExited) seed.php.exit() + runtime.php.exit() throw error } } @@ -289,10 +293,8 @@ async function isCompleteMarkdownRevision(bucket: R2Bucket, pointer: MarkdownPoi return Boolean(admin?.ID && usermetaRows.some((row) => row.user_id === admin.ID)) } -function canonicalSeedExportCode(passwordFile: string, origin: string): string { +function canonicalBootstrapSetupCode(passwordFile: string, origin: string): string { return `ID); $password = null; update_option('siteurl', ${JSON.stringify(origin)}); update_option('home', ${JSON.stringify(origin)}); -foreach (['class-wp-markdown-frontmatter-profiles.php', 'class-wp-markdown-storage.php', 'class-wp-markdown-driver.php', 'class-wp-markdown-search.php', 'class-wp-markdown-write-engine.php', 'class-wp-markdown-loader.php', 'class-wp-markdown-primary-storage-runtime.php'] as $file) require_once '/wordpress/wp-content/plugins/markdown-database-integration/inc/' . $file; -$connection = new WP_SQLite_Connection(['pdo' => $GLOBALS['@pdo'], 'path' => FQDB]); -$runtime = WP_Markdown_Primary_Storage_Runtime::bootstrap_existing_cache(['content_root' => '${MARKDOWN_ROOT}', 'state_root' => '${MARKDOWN_ROOT}'], $connection, defined('DB_NAME') && '' !== DB_NAME ? DB_NAME : 'database_name_here', array_filter(array_map('trim', explode(',', MARKDOWN_DB_EXCLUDED_TYPES))), $wpdb->prefix); -$driver = $runtime->get_driver(); -$prefix = $wpdb->prefix; -foreach ($driver->query("SELECT ID FROM " . $prefix . "posts") as $row) $driver->query("UPDATE " . $prefix . "posts SET post_modified = post_modified WHERE ID = " . (int) $row->ID); -foreach (['options' => 'option_id', 'users' => 'ID', 'usermeta' => 'umeta_id', 'postmeta' => 'meta_id', 'terms' => 'term_id', 'term_taxonomy' => 'term_taxonomy_id', 'termmeta' => 'meta_id', 'term_relationships' => 'object_id', 'comments' => 'comment_ID', 'commentmeta' => 'meta_id', 'links' => 'link_id'] as $table => $column) { - try { $driver->query("UPDATE " . $prefix . $table . " SET " . $column . " = " . $column); } catch (Throwable $ignored) {} +$GLOBALS['wpdb']->flush_canonical_writes(); +echo 'flushed';` } -echo json_encode($runtime->flush());` + +async function packagedCanonicalMarkdownSeed(): Promise { + const manifest = canonicalMarkdownSeedManifest as CanonicalSeedManifest + if (manifest.schema !== "wp-codebox/cloudflare-canonical-mdi-seed/v1" || manifest.markdownDatabaseIntegrationRevision !== MARKDOWN_DATABASE_INTEGRATION_REVISION) throw new Error("Packaged canonical MDI seed provenance is invalid.") + if (await sha256Hex(new Uint8Array(canonicalMarkdownSeed)) !== manifest.archiveSha256) throw new Error("Packaged canonical MDI seed hash is invalid.") + const expected = new Map(manifest.files.map((file) => [file.path, file])) + const files: RuntimeFile[] = [] + for await (const entry of decodeZip(new Blob([canonicalMarkdownSeed]).stream())) { + const bytes = new Uint8Array(await entry.arrayBuffer()) + const expectedFile = expected.get(entry.name) + if (!expectedFile || expectedFile.size !== bytes.byteLength || expectedFile.sha256 !== await sha256Hex(bytes)) throw new Error("Packaged canonical MDI seed file validation failed.") + files.push({ path: entry.name, bytes }) + } + if (files.length !== expected.size) throw new Error("Packaged canonical MDI seed archive is incomplete.") + return files.sort((left, right) => left.path.localeCompare(right.path)) } async function runBootProbe(phase: string): Promise { diff --git a/packages/runtime-cloudflare/wrangler.jsonc b/packages/runtime-cloudflare/wrangler.jsonc index 9072a4c7..490d017d 100644 --- a/packages/runtime-cloudflare/wrangler.jsonc +++ b/packages/runtime-cloudflare/wrangler.jsonc @@ -17,6 +17,6 @@ ], "rules": [ { "type": "CompiledWasm", "globs": ["**/*.wasm"], "fallthrough": false }, - { "type": "Data", "globs": ["**/*.sqlite", "**/*-runtime.zip"], "fallthrough": false } + { "type": "Data", "globs": ["**/*.sqlite", "**/*-runtime.zip", "**/*-canonical-seed.zip"], "fallthrough": false } ] } diff --git a/scripts/build-cloudflare-canonical-mdi-seed.php b/scripts/build-cloudflare-canonical-mdi-seed.php new file mode 100644 index 00000000..a0cf5f90 --- /dev/null +++ b/scripts/build-cloudflare-canonical-mdi-seed.php @@ -0,0 +1,109 @@ +pdo; } +} + +class WP_SQLite_Driver { + public function __construct( private WP_SQLite_Connection $connection, string $database ) { unset( $database ); } + public function get_connection(): WP_SQLite_Connection { return $this->connection; } + public function get_insert_id(): int { return (int) $this->connection->get_pdo()->lastInsertId(); } + public function query( string $sql, $fetch_mode = PDO::FETCH_OBJ, ...$args ) { + unset( $args ); + $statement = $this->connection->get_pdo()->query( $sql ); + return false === $statement ? array() : $statement->fetchAll( $fetch_mode ); + } +} + +function apply_filters( string $hook, mixed $value, mixed ...$args ): mixed { unset( $hook, $args ); return $value; } +function cloudflare_seed_rm( string $path ): void { + if ( ! is_dir( $path ) ) { return; } + foreach ( scandir( $path ) ?: array() as $entry ) { + if ( '.' === $entry || '..' === $entry ) { continue; } + $child = $path . '/' . $entry; + is_dir( $child ) ? cloudflare_seed_rm( $child ) : unlink( $child ); + } + rmdir( $path ); +} +function cloudflare_seed_files( string $root ): array { + $files = array(); + $iterator = new RecursiveIteratorIterator( new RecursiveDirectoryIterator( $root, FilesystemIterator::SKIP_DOTS ) ); + foreach ( $iterator as $file ) { + if ( ! $file->isFile() || str_contains( $file->getFilename(), '.tmp.' ) || str_starts_with( $file->getFilename(), 'markdown-index.sqlite' ) ) { continue; } + $path = substr( $file->getPathname(), strlen( $root ) + 1 ); + $files[ str_replace( DIRECTORY_SEPARATOR, '/', $path ) ] = $file->getPathname(); + } + ksort( $files, SORT_STRING ); + return $files; +} + +$repository = dirname( __DIR__ ); +$assets = $repository . '/packages/runtime-cloudflare/assets'; +$runtime_zip = $assets . '/markdown-database-integration-runtime.zip'; +$sqlite_seed = $assets . '/wordpress-install-seed.sqlite'; +$output_zip = $assets . '/markdown-database-integration-canonical-seed.zip'; +$output_manifest = $assets . '/markdown-database-integration-canonical-seed.json'; +$root = sys_get_temp_dir() . '/wp-codebox-canonical-mdi-' . getmypid() . '-' . bin2hex( random_bytes( 4 ) ); + +try { + mkdir( $root, 0755, true ); + $runtime_root = $root . '/runtime'; + $markdown_root = $root . '/markdown'; + $database = $root . '/wordpress.sqlite'; + if ( ! copy( $sqlite_seed, $database ) ) { throw new RuntimeException( 'Unable to copy wordpress-install-seed.sqlite.' ); } + $zip = new ZipArchive(); + if ( true !== $zip->open( $runtime_zip ) || ! $zip->extractTo( $runtime_root ) ) { throw new RuntimeException( 'Unable to extract the pinned MDI runtime archive.' ); } + $zip->close(); + foreach ( array( 'class-wp-markdown-frontmatter-profiles.php', 'class-wp-markdown-storage.php', 'class-wp-markdown-search.php', 'class-wp-markdown-write-engine.php', 'class-wp-markdown-driver.php', 'class-wp-markdown-loader.php', 'class-wp-markdown-primary-storage-runtime.php' ) as $file ) { + require_once $runtime_root . '/inc/' . $file; + } + $pdo = new PDO( 'sqlite:' . $database ); + $pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); + $runtime = WP_Markdown_Primary_Storage_Runtime::bootstrap_existing_cache( array( 'content_root' => $markdown_root, 'state_root' => $markdown_root ), new WP_SQLite_Connection( $pdo ), 'wordpress' ); + $driver = $runtime->get_driver(); + foreach ( $pdo->query( 'SELECT ID FROM wp_posts ORDER BY ID' )->fetchAll( PDO::FETCH_COLUMN ) as $post_id ) { + $driver->query( 'UPDATE wp_posts SET post_modified = post_modified WHERE ID = ' . (int) $post_id ); + } + $tables = array( 'options' => 'option_id', 'users' => 'ID', 'usermeta' => 'umeta_id', 'postmeta' => 'meta_id', 'terms' => 'term_id', 'term_taxonomy' => 'term_taxonomy_id', 'termmeta' => 'meta_id', 'term_relationships' => 'object_id', 'comments' => 'comment_ID', 'commentmeta' => 'meta_id', 'links' => 'link_id' ); + foreach ( $tables as $table => $column ) { + try { $driver->query( 'UPDATE wp_' . $table . ' SET ' . $column . ' = ' . $column ); } catch ( Throwable $ignored ) {} + } + $runtime->flush(); + $files = cloudflare_seed_files( $markdown_root ); + $missing = array_diff( REQUIRED_PATHS, array_keys( $files ) ); + if ( $missing || ! array_filter( array_keys( $files ), static fn( string $path ): bool => str_ends_with( $path, '.md' ) ) ) { + $seed_tables = $pdo->query( "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name" )->fetchAll( PDO::FETCH_COLUMN ); + throw new RuntimeException( 'Canonical MDI seed is incomplete: ' . implode( ', ', $missing ) . '; SQLite tables: ' . implode( ', ', $seed_tables ) ); + } + $zip = new ZipArchive(); + if ( true !== $zip->open( $output_zip, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) { throw new RuntimeException( 'Unable to create canonical MDI seed archive.' ); } + $manifest_files = array(); + foreach ( $files as $path => $file ) { + $bytes = file_get_contents( $file ); + if ( false === $bytes || ! $zip->addFromString( $path, $bytes ) || ! $zip->setMtimeName( $path, 0 ) || ! $zip->setCompressionName( $path, ZipArchive::CM_STORE ) ) { throw new RuntimeException( "Unable to package canonical file $path." ); } + $manifest_files[] = array( 'path' => $path, 'sha256' => hash( 'sha256', $bytes ), 'size' => strlen( $bytes ) ); + } + $zip->close(); + $manifest = array( + 'schema' => 'wp-codebox/cloudflare-canonical-mdi-seed/v1', + 'markdownDatabaseIntegrationRevision' => MDI_REVISION, + 'wordpressInstallSeedSha256' => hash_file( 'sha256', $sqlite_seed ), + 'archiveSha256' => hash_file( 'sha256', $output_zip ), + 'files' => $manifest_files, + ); + file_put_contents( $output_manifest, json_encode( $manifest, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES ) . "\n" ); + echo 'Built ' . count( $files ) . ' canonical MDI files (' . filesize( $output_zip ) . ' bytes, ' . $manifest['archiveSha256'] . ").\n"; +} finally { + cloudflare_seed_rm( $root ); +} diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 31976c71..9b406c5b 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -1,5 +1,8 @@ import assert from "node:assert/strict" import { readFile } from "node:fs/promises" +import { createHash } from "node:crypto" +import { execFile } from "node:child_process" +import { promisify } from "node:util" import test from "node:test" import { decodeZip } from "@php-wasm/stream-compression" import { RUNTIME_COMMAND_RESULT_SCHEMA } from "../packages/runtime-core/src/runtime-contracts.js" @@ -7,6 +10,8 @@ import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, clo import { routeWorkerRequest } from "../packages/runtime-cloudflare/src/request-routing.js" import { toFetchResponse, toPHPRequest } from "../packages/runtime-cloudflare/src/request-translation.js" +const execFileAsync = promisify(execFile) + test("Cloudflare health response preserves the Codebox execution envelope", async () => { const health = { schema: CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, @@ -68,20 +73,26 @@ test("Cloudflare runtime declares the paid-plan WordPress boot CPU budget", asyn assert.equal(config.limits?.cpu_ms, 300_000) }) -test("Cloudflare runtime packages the disposable WordPress install seed", async () => { +test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => { const config = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")) as { rules?: Array<{ type?: string; globs?: string[] }> r2_buckets?: Array<{ binding?: string; bucket_name?: string }> durable_objects?: { bindings?: Array<{ name?: string; class_name?: string }> } migrations?: Array<{ new_sqlite_classes?: string[] }> } - const seed = await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-install-seed.sqlite", import.meta.url)) const markdownIndex = await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-primary-bootstrap-index.sqlite", import.meta.url)) const markdownRuntime = await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip", import.meta.url)) + const canonicalSeed = await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip", import.meta.url)) + const sqliteInput = await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-install-seed.sqlite", import.meta.url)) + const canonicalManifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json", import.meta.url), "utf8")) as { markdownDatabaseIntegrationRevision: string; wordpressInstallSeedSha256: string; archiveSha256: string; files: Array<{ path: string }> } - assert.equal(seed.subarray(0, 16).toString(), "SQLite format 3\0") assert.equal(markdownIndex.subarray(0, 16).toString(), "SQLite format 3\0") assert.equal(markdownRuntime.subarray(0, 4).toString("hex"), "504b0304") + assert.equal(canonicalSeed.subarray(0, 4).toString("hex"), "504b0304") + assert.equal(canonicalManifest.markdownDatabaseIntegrationRevision, "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3") + assert.equal(canonicalManifest.wordpressInstallSeedSha256, createHash("sha256").update(sqliteInput).digest("hex")) + assert.equal(canonicalManifest.archiveSha256, createHash("sha256").update(canonicalSeed).digest("hex")) + assert.ok(canonicalManifest.files.some((file) => file.path.endsWith(".md"))) assert.ok(config.rules?.some((rule) => rule.type === "Data" && rule.globs?.includes("**/*.sqlite"))) assert.ok(config.rules?.some((rule) => rule.type === "Data" && rule.globs?.includes("**/*-runtime.zip"))) assert.deepEqual(config.r2_buckets, [{ binding: "WORDPRESS_STATE_BUCKET", bucket_name: "wp-codebox-runtime-chubes" }]) @@ -125,6 +136,10 @@ test("Cloudflare routes WordPress and health through the serialized Durable Obje assert.match(worker, /runSyntheticMutation\(new URL\(request\.url\)\.origin\)/) assert.match(worker, /private async runSyntheticMutation\(origin: string\)/) assert.match(worker, /bootWordPressRuntime\("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision/) + const noPointerBoot = worker.slice(worker.indexOf("private async bootRuntime"), worker.indexOf("private async persistRuntime")) + assert.match(noPointerBoot, /packagedCanonicalMarkdownSeed\(\)/) + assert.match(noPointerBoot, /canonicalBootstrapSetupCode\(passwordFile, origin\)/) + assert.doesNotMatch(noPointerBoot, /wordpressInstallSeed|databaseSeed|bootstrap_existing_cache/) assert.match(worker, /maxPhpInstances: 1/) assert.match(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL,\s*\(entry: \{ path: Uint8Array \}\) => isWordPressServerFile\(decoder\.decode\(entry\.path\)\)\)/) assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL\)(?!\s*,)/) @@ -143,9 +158,22 @@ test("serialized Cloudflare mutations use MDI flush paths and complete canonical assert.doesNotMatch(mutation, /write_post|file_put_contents|wp_codebox_mdi_revision\.json/) assert.match(source, /validateMarkdownChanges\(mutation\.canonicalChanges\)/) assert.match(source, /flush_canonical_writes\(\)/) - assert.match(source, /bootstrap_existing_cache/) + assert.match(source, /packagedCanonicalMarkdownSeed/) assert.match(source, /update_option\('siteurl'/) assert.match(source, /_tables\/termmeta\.json/) assert.match(source, /_tables\/users\.json/) assert.match(source, /WORDPRESS_ADMIN_PASSWORD is required/) }) + +test("canonical MDI seed generator is reproducible and validates its pinned inputs", async () => { + const generator = new URL("../scripts/build-cloudflare-canonical-mdi-seed.php", import.meta.url) + const archive = new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip", import.meta.url) + const before = createHash("sha256").update(await readFile(archive)).digest("hex") + await execFileAsync("php", [generator.pathname], { cwd: new URL("..", import.meta.url).pathname }) + const after = createHash("sha256").update(await readFile(archive)).digest("hex") + assert.equal(after, before) + const source = await readFile(generator, "utf8") + assert.match(source, /bootstrap_existing_cache/) + assert.match(source, /SELECT ID FROM wp_posts ORDER BY ID/) + assert.match(source, /MDI_REVISION/) +}) From c590c4af3f15d3d6a0c342c878855b4e7ce21652 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 00:44:49 -0400 Subject: [PATCH 03/87] Keep PHP outside the state coordinator --- packages/runtime-cloudflare/README.md | 6 +- .../src/state-coordinator.ts | 141 ++++++++ packages/runtime-cloudflare/src/worker.ts | 307 +++++++++++------- scripts/cloudflare-local-gate.mjs | 87 ++++- tests/cloudflare-runtime.test.ts | 63 +++- 5 files changed, 466 insertions(+), 138 deletions(-) create mode 100644 packages/runtime-cloudflare/src/state-coordinator.ts diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index bf1e0b40..d0322525 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -4,11 +4,11 @@ This candidate integration for [wp-codebox#1838](https://github.com/Automattic/w ## Runtime Architecture -All normal WordPress, health, and synthetic mutation traffic is forwarded to the named `WordPressStateCoordinator` Durable Object. The object serializes requests and owns one runtime promise. A failed boot clears that promise so the next request can retry; a successful runtime remains available for subsequent requests. +The entry Worker executes PHP-WASM and WordPress. The named `WordPressStateCoordinator` Durable Object remains lightweight: it serializes a bounded lease and atomically promotes the current canonical R2 pointer with token, base-revision, and version checks. It never imports or instantiates PHP-WASM. State reads query the coordinator directly; every request that can observe canonical WordPress state acquires a lease first. -On cold start, the Durable Object reads the current R2 Markdown pointer and rebuilds PHP-WASM's disposable SQLite index from canonical MDI Markdown and JSON files. A missing or incomplete revision materializes the packaged canonical MDI seed and boots one PHP-WASM primary runtime. The build-time PHP CLI generator creates that archive from `wordpress-install-seed.sqlite` through MDI's public `bootstrap_existing_cache()` API, validates its pinned MDI revision and input digest, and never packages SQLite. The runtime updates `siteurl` and `home` through WordPress APIs using the request origin and sets the admin password from `WORDPRESS_ADMIN_PASSWORD`; only WordPress's password hash is canonical. +On cold start, the entry Worker uses the acquired pointer to rebuild PHP-WASM's disposable SQLite index from canonical MDI Markdown and JSON files. A missing pointer materializes the packaged canonical MDI seed and boots one PHP-WASM primary runtime. The build-time PHP CLI generator creates that archive from `wordpress-install-seed.sqlite` through MDI's public `bootstrap_existing_cache()` API, validates its pinned MDI revision and input digest, and never packages SQLite. The runtime updates `siteurl` and `home` through WordPress APIs using the request origin and sets the admin password from `WORDPRESS_ADMIN_PASSWORD`; only WordPress's password hash is canonical. Bootstrap persists and CAS-promotes this mutation before serving the next request. -After each mutating HTTP request, the runtime invokes MDI's explicit request-boundary flush, collects canonical files, stores immutable content-addressed R2 objects and a revision manifest, then advances the current pointer while still serialized by the Durable Object. It does not persist SQLite. Existing manifests are reused when canonical file hashes have not changed. +After each mutating HTTP request, the entry runtime invokes MDI's explicit request-boundary flush, collects canonical files, stores immutable content-addressed R2 objects and a revision manifest, then commits the new pointer through the held lease. GET, HEAD, and asset requests release without promotion. Failed requests abort their leases; stale leases recover by token/version/expiry checks. The entry isolate can cache one runtime only for the exact acquired pointer revision and exits it after promotion or when another isolate advances the pointer. It does not persist SQLite. Existing manifests are reused when canonical file hashes have not changed. The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c7406f1f1ea6dc3` from [MDI PR #127](https://github.com/Automattic/markdown-database-integration/pull/127). The bundle generator, worker provenance, and source-contract test use the same revision. diff --git a/packages/runtime-cloudflare/src/state-coordinator.ts b/packages/runtime-cloudflare/src/state-coordinator.ts new file mode 100644 index 00000000..c636376b --- /dev/null +++ b/packages/runtime-cloudflare/src/state-coordinator.ts @@ -0,0 +1,141 @@ +export interface MarkdownPointer { + revision: string + manifestKey: string + persistedAt: string +} + +interface Lease { + token: string + base: MarkdownPointer | null + version: number + expiresAt: number +} + +interface CoordinatorRecord { + initialized: boolean + pointer: MarkdownPointer | null + version: number + lease?: Lease +} + +interface CoordinatorEnv { + WORDPRESS_STATE_BUCKET: R2Bucket + COORDINATOR_LEASE_MS?: number +} + +const POINTER_KEY = "sites/default/markdown/current.json" +const STORAGE_KEY = "wordpress-state-coordinator" +// A cold PHP-WASM WordPress boot has measured around 53 seconds; this remains bounded +// while allowing one lease to cover a cold boot plus request-boundary persistence. +const LEASE_MS = 90_000 + +export class WordPressStateCoordinator implements DurableObject { + private tail: Promise = Promise.resolve() + + constructor( + private readonly state: DurableObjectState, + private readonly env: CoordinatorEnv, + ) {} + + fetch(request: Request): Promise { + const response = this.tail.then(() => this.handle(request)).catch((error: unknown) => { + if (error instanceof CoordinatorConflict) { + const headers = new Headers() + if (error.retryAt) headers.set("retry-after", String(Math.max(1, Math.ceil((error.retryAt - Date.now()) / 1000)))) + return Response.json({ schema: "wp-codebox/cloudflare-coordinator-conflict/v1", message: error.message, retryAt: error.retryAt }, { status: 409, headers }) + } + return Response.json({ schema: "wp-codebox/cloudflare-coordinator-error/v1", message: error instanceof Error ? error.message : String(error) }, { status: 500 }) + }) + this.tail = response.then(() => undefined, () => undefined) + return response + } + + private async handle(request: Request): Promise { + const action = new URL(request.url).searchParams.get("__wp_codebox_coordinator") + if (request.method === "GET" && action === "state") return Response.json(await this.current()) + if (request.method !== "POST" || !action) return new Response("Coordinator requests require an internal action.", { status: 404 }) + const body = await request.json>() + if (action === "begin") return Response.json(await this.begin()) + if (action === "release") return Response.json(await this.release(body)) + if (action === "abort") return Response.json(await this.abort(body)) + if (action === "commit") return Response.json(await this.commit(body)) + return new Response("Unknown coordinator action.", { status: 404 }) + } + + private async current(): Promise<{ schema: string; durableObjectId: string; pointer: MarkdownPointer | null; version: number }> { + const record = await this.record() + return { schema: "wp-codebox/cloudflare-wordpress-state/v1", durableObjectId: this.state.id.toString(), pointer: record.pointer, version: record.version } + } + + private async begin(): Promise<{ token: string; pointer: MarkdownPointer | null; version: number; expiresAt: number }> { + const record = await this.record() + if (record.lease && record.lease.expiresAt > Date.now()) { + throw new CoordinatorConflict("A canonical WordPress lease is active.", record.lease.expiresAt) + } + if (record.lease) delete record.lease + const lease: Lease = { token: crypto.randomUUID(), base: record.pointer, version: record.version, expiresAt: Date.now() + (this.env.COORDINATOR_LEASE_MS ?? LEASE_MS) } + record.lease = lease + await this.save(record) + return { token: lease.token, pointer: lease.base, version: lease.version, expiresAt: lease.expiresAt } + } + + private async release(body: Record): Promise<{ released: true }> { + const record = await this.record() + this.requireLease(record, body) + delete record.lease + await this.save(record) + return { released: true } + } + + private async abort(body: Record): Promise<{ aborted: true }> { + const record = await this.record() + this.requireLease(record, body) + delete record.lease + await this.save(record) + return { aborted: true } + } + + private async commit(body: Record): Promise<{ pointer: MarkdownPointer; version: number }> { + const record = await this.record() + const lease = this.requireLease(record, body) + const pointer = body.pointer as MarkdownPointer + if (!pointer || typeof pointer.revision !== "string" || typeof pointer.manifestKey !== "string" || typeof pointer.persistedAt !== "string") { + throw new CoordinatorConflict("A complete canonical pointer is required for promotion.") + } + if (body.baseRevision !== (lease.base?.revision ?? null) || body.version !== lease.version || record.version !== lease.version || record.pointer?.revision !== lease.base?.revision) { + throw new CoordinatorConflict("The canonical pointer changed before promotion.") + } + await this.env.WORDPRESS_STATE_BUCKET.put(POINTER_KEY, JSON.stringify(pointer), { httpMetadata: { contentType: "application/json" } }) + record.pointer = pointer + record.version++ + delete record.lease + await this.save(record) + return { pointer, version: record.version } + } + + private requireLease(record: CoordinatorRecord, body: Record): Lease { + const lease = record.lease + if (!lease || lease.expiresAt <= Date.now()) throw new CoordinatorConflict("The canonical WordPress lease has expired.") + if (body.token !== lease.token) throw new CoordinatorConflict("The canonical WordPress lease token is invalid.") + return lease + } + + private async record(): Promise { + const stored = await this.state.storage.get(STORAGE_KEY) + if (stored?.initialized) return stored + const object = await this.env.WORDPRESS_STATE_BUCKET.get(POINTER_KEY) + const record: CoordinatorRecord = { initialized: true, pointer: object ? await object.json() : null, version: 0 } + await this.save(record) + return record + } + + private save(record: CoordinatorRecord): Promise { + return this.state.storage.put(STORAGE_KEY, record) + } +} + +class CoordinatorConflict extends Error { + constructor(message: string, readonly retryAt?: number) { + super(message) + } +} diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 2af1ef47..e94d093b 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -8,6 +8,8 @@ import phpWasmModule from "../../../node_modules/@php-wasm/web-8-5/asyncify/8_5_ import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "./health-envelope.js" import { routeWorkerRequest } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" +import type { MarkdownPointer } from "./state-coordinator.js" +export { WordPressStateCoordinator } from "./state-coordinator.js" import markdownDatabaseIntegrationRuntime from "../assets/markdown-database-integration-runtime.zip" import canonicalMarkdownSeed from "../assets/markdown-database-integration-canonical-seed.zip" import canonicalMarkdownSeedManifest from "../assets/markdown-database-integration-canonical-seed.json" with { type: "json" } @@ -23,7 +25,6 @@ const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite" const MARKDOWN_ROOT = "/wordpress/wp-content/markdown" const MARKDOWN_INDEX_PATH = "/tmp/markdown-index.sqlite" const MARKDOWN_RESOLVED_INDEX_PATH = "/tmp/markdown-index-8133b4cf3c66.sqlite" -const R2_MARKDOWN_POINTER_KEY = "sites/default/markdown/current.json" const R2_MARKDOWN_REVISION_PREFIX = "sites/default/markdown/revisions" const R2_MARKDOWN_OBJECT_PREFIX = "sites/default/markdown/objects" const SERIALIZED_MARKDOWN_MUTATION_CODE = ` { const route = routeWorkerRequest(request) - if (route.kind === "probe") return runBootProbe(route.phase) - return env.WORDPRESS_STATE.getByName("default").fetch(request) + const coordinator = env.WORDPRESS_STATE.getByName("default") + if (route.kind === "probe") { + if (route.phase === "canonical-session") return canonicalSessionProbe(env.WORDPRESS_STATE_BUCKET, await coordinatorCall<{ pointer: MarkdownPointer | null }>(coordinator, request.url, "state")) + return runBootProbe(route.phase) + } + if (route.kind === "r2-state") { + if (request.method !== "GET") return new Response("WordPress state read requires GET.", { status: 405 }) + return coordinator.fetch(new Request(coordinatorUrl(request.url, "state"))) + } + return runCoordinatedWordPressRequest(request, env, coordinator, route.kind) }, } -interface MarkdownPointer { - revision: string - manifestKey: string - persistedAt: string -} - interface MarkdownManifestFile { path: string objectKey: string @@ -106,104 +109,205 @@ interface CanonicalSeedManifest { files: Array<{ path: string; sha256: string; size: number }> } -export class WordPressStateCoordinator implements DurableObject { - private tail: Promise = Promise.resolve() - private runtimePromise: Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string; pointer: MarkdownPointer }> | undefined - - constructor( - private readonly state: DurableObjectState, - private readonly env: Env, - ) {} - - fetch(request: Request): Promise { - const response = this.tail - .then(() => this.handleRequest(request)) - .catch((error: unknown) => Response.json({ - schema: "wp-codebox/cloudflare-wordpress-state-error/v1", - message: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined, - }, { status: 500 })) - this.tail = response.then(() => undefined, () => undefined) - return response - } +interface Lease { + token: string + pointer: MarkdownPointer | null + version: number + expiresAt: number +} - private async handleRequest(request: Request): Promise { - const route = routeWorkerRequest(request) - if (route.kind === "r2-state") { - if (request.method !== "GET") return new Response("WordPress state read requires GET.", { status: 405 }) - const pointer = await readMarkdownPointer(this.env.WORDPRESS_STATE_BUCKET) - return Response.json({ - schema: "wp-codebox/cloudflare-wordpress-state/v1", - durableObjectId: this.state.id.toString(), - pointer, - }) +interface Runtime { + php: PHP + requestHandler: PHPRequestHandler + wordpressVersion: string + pointer: MarkdownPointer +} + +let cachedRuntime: { baseRevision: string; promise: Promise } | undefined +const LEASE_ACQUISITION_TIMEOUT_MS = 100_000 + +async function runCoordinatedWordPressRequest(request: Request, env: Env, coordinator: DurableObjectStub, route: "wordpress" | "health" | "r2-mutate"): Promise { + if (route === "r2-mutate" && request.method !== "POST") return new Response("WordPress state mutation requires POST.", { status: 405 }) + let lease = await acquireLease(coordinator, request.url) + let finalized = false + try { + if (!lease.pointer) { + const bootstrapped = await bootstrapCanonicalRuntime(env, coordinator, request.url, lease) + // Bootstrap promotion consumes its lease before login or any other request observes it. + lease = await acquireLease(coordinator, request.url) + if (!lease.pointer || lease.pointer.revision !== bootstrapped.pointer.revision) throw new Error("Canonical bootstrap promotion was not observed by its next lease.") + cacheRuntime(lease.pointer, bootstrapped) } - if (route.kind === "r2-mutate") { - if (request.method !== "POST") return new Response("WordPress state mutation requires POST.", { status: 405 }) - return this.runSyntheticMutation(new URL(request.url).origin) + const runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) + let response: Response + if (route === "r2-mutate") { + response = await runSyntheticMutation(runtime) + } else if (route === "health") { + response = await health(runtime) + } else { + response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) } - const runtime = await this.getRuntime(new URL(request.url).origin) - if (route.kind === "health") return this.health(runtime) - const response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) - if (!["GET", "HEAD", "OPTIONS"].includes(request.method)) await this.persistRuntime(runtime) + if (isMutation(request, route)) { + const next = await persistRuntime(env.WORDPRESS_STATE_BUCKET, runtime) + await commitLease(coordinator, request.url, lease, next) + // The runtime now represents the promoted revision, including login session state. + runtime.pointer = next + cacheRuntime(next, runtime) + } else { + await releaseLease(coordinator, request.url, lease) + } + finalized = true return response + } catch (error) { + if (!finalized) await abortLease(coordinator, request.url, lease) + throw error } +} - private async getRuntime(origin: string) { - const runtime = this.runtimePromise ?? this.bootRuntime(origin) - this.runtimePromise = runtime - try { - return await runtime - } catch (error) { - if (this.runtimePromise === runtime) this.runtimePromise = undefined - throw error - } +function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate"): boolean { + return route === "r2-mutate" || !["GET", "HEAD", "OPTIONS"].includes(request.method) +} + +function coordinatorUrl(requestUrl: string, action: string): string { + const url = new URL(requestUrl) + url.searchParams.set("__wp_codebox_coordinator", action) + return url.toString() +} + +async function coordinatorCall(coordinator: DurableObjectStub, requestUrl: string, action: string, body?: Record): Promise { + const response = await coordinator.fetch(new Request(coordinatorUrl(requestUrl, action), body ? { method: "POST", headers: { "content-type": "application/json" }, body: JSON.stringify(body) } : undefined)) + if (!response.ok) { + const detail = await response.text() + const retryAfter = response.headers.get("retry-after") + throw new CoordinatorRequestError(response.status, detail, retryAfter ? Number(retryAfter) : undefined) } + return response.json() +} - private async bootRuntime(origin: string) { - const pointer = await readMarkdownPointer(this.env.WORDPRESS_STATE_BUCKET) - if (pointer && await isCompleteMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, pointer)) { - return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin), pointer } - } - if (!this.env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") - const files = await packagedCanonicalMarkdownSeed() - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, files, new Uint8Array(markdownPrimaryBootstrapIndex), origin) +async function acquireLease(coordinator: DurableObjectStub, requestUrl: string): Promise { + const deadline = Date.now() + LEASE_ACQUISITION_TIMEOUT_MS + let lastError: CoordinatorRequestError | undefined + while (Date.now() < deadline) { try { - const passwordFile = "/tmp/wordpress-admin-password" - runtime.php.writeFile(passwordFile, new TextEncoder().encode(this.env.WORDPRESS_ADMIN_PASSWORD)) - const output = (await runtime.php.run({ code: canonicalBootstrapSetupCode(passwordFile, origin) })).text.trim() - if (output !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") - const nextPointer = await persistMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT)) - await this.env.WORDPRESS_STATE_BUCKET.put(R2_MARKDOWN_POINTER_KEY, JSON.stringify(nextPointer), { httpMetadata: { contentType: "application/json" } }) - return { ...runtime, pointer: nextPointer } + return await coordinatorCall(coordinator, requestUrl, "begin", {}) } catch (error) { - runtime.php.exit() - throw error + if (!(error instanceof CoordinatorRequestError) || error.status !== 409) throw error + lastError = error + await new Promise((resolve) => setTimeout(resolve, Math.min(250, Math.max(25, (error.retryAfter ?? 1) * 100)))) } } + throw new Error(`Timed out waiting for the canonical WordPress lease${lastError ? `: ${lastError.message}` : "."}`) +} - private async persistRuntime(runtime: { php: PHP; pointer: MarkdownPointer }): Promise { - const output = (await runtime.php.run({ code: "flush_canonical_writes(); echo 'flushed';" })).text.trim() - if (output !== "flushed") throw new Error("MDI did not confirm its canonical flush.") - const next = await persistMarkdownRevision(this.env.WORDPRESS_STATE_BUCKET, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT), runtime.pointer) - if (next.revision !== runtime.pointer.revision) { - await this.env.WORDPRESS_STATE_BUCKET.put(R2_MARKDOWN_POINTER_KEY, JSON.stringify(next), { httpMetadata: { contentType: "application/json" } }) - runtime.pointer = next - } +function releaseLease(coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise<{ released: true }> { + return coordinatorCall(coordinator, requestUrl, "release", { token: lease.token }) +} + +async function abortLease(coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { + try { + await coordinatorCall(coordinator, requestUrl, "abort", { token: lease.token }) + } catch (error) { + if (!(error instanceof CoordinatorRequestError) || error.status !== 409) throw error } +} - private async runSyntheticMutation(origin: string): Promise { - const runtime = await this.getRuntime(origin) - const mutationOutput = (await runtime.php.run({ code: SERIALIZED_MARKDOWN_MUTATION_CODE })).text.trim() - const mutation = JSON.parse(mutationOutput) as { revisionValue: number; previousPostFound: boolean; postId: number; wordpressVersion: string; canonicalChanges: MarkdownChanges } - validateMarkdownChanges(mutation.canonicalChanges) - await this.persistRuntime(runtime) - return Response.json({ schema: "wp-codebox/cloudflare-wordpress-mutation/v1", source: "durable-object-primary-runtime", ...mutation, canonicalFiles: collectRuntimeFiles(runtime.php, MARKDOWN_ROOT).length, markdownDatabaseIntegrationRevision: MARKDOWN_DATABASE_INTEGRATION_REVISION, sqlitePersisted: false, pointer: runtime.pointer }) +function commitLease(coordinator: DurableObjectStub, requestUrl: string, lease: Lease, pointer: MarkdownPointer): Promise<{ pointer: MarkdownPointer; version: number }> { + return coordinatorCall(coordinator, requestUrl, "commit", { token: lease.token, baseRevision: lease.pointer?.revision ?? null, version: lease.version, pointer }) +} + +async function getRuntime(env: Env, pointer: MarkdownPointer, origin: string): Promise { + if (cachedRuntime && cachedRuntime.baseRevision !== pointer.revision) await discardCachedRuntime() + if (!cachedRuntime) { + const promise = bootRuntime(env.WORDPRESS_STATE_BUCKET, pointer, origin) + cachedRuntime = { baseRevision: pointer.revision, promise } + promise.catch(() => { + if (cachedRuntime?.promise === promise) cachedRuntime = undefined + }) + } + return cachedRuntime.promise +} + +function cacheRuntime(pointer: MarkdownPointer, runtime: Runtime): void { + cachedRuntime = { baseRevision: pointer.revision, promise: Promise.resolve(runtime) } +} + +async function discardCachedRuntime(): Promise { + const cached = cachedRuntime + cachedRuntime = undefined + if (!cached) return + try { + ;(await cached.promise).php.exit() + } catch { + // A rejected boot has no live runtime to dispose. + } +} + +async function discardRuntime(runtime: Runtime): Promise { + if (cachedRuntime?.promise) { + try { + if (await cachedRuntime.promise === runtime) cachedRuntime = undefined + } catch { + cachedRuntime = undefined + } + } + runtime.php.exit() +} + +async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: string): Promise { + return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(bucket, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin), pointer } +} + +async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { + if (!env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") + const origin = new URL(requestUrl).origin + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin) + try { + const passwordFile = "/tmp/wordpress-admin-password" + runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) + const output = (await runtime.php.run({ code: canonicalBootstrapSetupCode(passwordFile, origin) })).text.trim() + if (output !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") + const pointer = await persistMarkdownRevision(env.WORDPRESS_STATE_BUCKET, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT)) + await commitLease(coordinator, requestUrl, lease, pointer) + return { ...runtime, pointer } + } catch (error) { + runtime.php.exit() + throw error } +} + +async function persistRuntime(bucket: R2Bucket, runtime: Runtime): Promise { + const output = (await runtime.php.run({ code: "flush_canonical_writes(); echo 'flushed';" })).text.trim() + if (output !== "flushed") throw new Error("MDI did not confirm its canonical flush.") + return persistMarkdownRevision(bucket, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT), runtime.pointer) +} + +async function runSyntheticMutation(runtime: Runtime): Promise { + const mutationOutput = (await runtime.php.run({ code: SERIALIZED_MARKDOWN_MUTATION_CODE })).text.trim() + const mutation = JSON.parse(mutationOutput) as { revisionValue: number; previousPostFound: boolean; postId: number; wordpressVersion: string; canonicalChanges: MarkdownChanges } + validateMarkdownChanges(mutation.canonicalChanges) + return Response.json({ schema: "wp-codebox/cloudflare-wordpress-mutation/v1", source: "entry-worker-primary-runtime", ...mutation, canonicalFiles: collectRuntimeFiles(runtime.php, MARKDOWN_ROOT).length, markdownDatabaseIntegrationRevision: MARKDOWN_DATABASE_INTEGRATION_REVISION, sqlitePersisted: false }) +} - private async health(runtime: { php: PHP; wordpressVersion: string }): Promise { - const phpVersion = (await runtime.php.run({ code: " { + const phpVersion = (await runtime.php.run({ code: " { + if (!state.pointer) return Response.json({ schema: "wp-codebox/cloudflare-canonical-session/v1", pointerRevision: null, sessionTokenRows: 0 }) + const manifest = await readMarkdownManifest(bucket, state.pointer) + const usermeta = manifest?.files.find((file) => file.path === "_tables/usermeta.json") + if (!usermeta) throw new Error("The canonical WordPress revision is missing usermeta.") + const object = await bucket.get(usermeta.objectKey) + if (!object) throw new Error("The canonical WordPress usermeta object is missing.") + const rows = await object.json>() + const usermetaKeys = rows.map((row) => row.meta_key).filter((key): key is string => typeof key === "string").sort() + return Response.json({ schema: "wp-codebox/cloudflare-canonical-session/v1", pointerRevision: state.pointer.revision, sessionTokenRows: usermetaKeys.filter((key) => key.endsWith("session_tokens")).length, hasSessionTokens: usermetaKeys.some((key) => key.endsWith("session_tokens")), usermetaKeys }) +} + +class CoordinatorRequestError extends Error { + constructor(readonly status: number, message: string, readonly retryAfter?: number) { + super(message) } } @@ -228,11 +332,6 @@ function isCanonicalRelativePath(path: string): boolean { return path.length > 0 && !path.startsWith("/") && !path.split("/").includes("..") } -async function readMarkdownPointer(bucket: R2Bucket): Promise { - const object = await bucket.get(R2_MARKDOWN_POINTER_KEY) - return object ? object.json() : null -} - async function readMarkdownRevision(bucket: R2Bucket, pointer: MarkdownPointer): Promise { const manifestObject = await bucket.get(pointer.manifestKey) if (!manifestObject) throw new Error(`R2 Markdown manifest is missing: ${pointer.manifestKey}`) @@ -275,24 +374,6 @@ async function readMarkdownManifest(bucket: R2Bucket, pointer: MarkdownPointer): return object ? object.json() : null } -async function isCompleteMarkdownRevision(bucket: R2Bucket, pointer: MarkdownPointer): Promise { - const manifest = await readMarkdownManifest(bucket, pointer) - if (!manifest || manifest.revision !== pointer.revision) return false - const paths = new Set(manifest.files.map((file) => file.path)) - if (!["_options/siteurl.json", "_options/home.json", "_tables/users.json", "_tables/usermeta.json", "_tables/terms.json", "_tables/term_taxonomy.json", "_tables/termmeta.json", "_tables/postmeta.json", "_tables/term_relationships.json", "_tables/comments.json", "_tables/commentmeta.json", "_tables/links.json"].every((path) => paths.has(path)) || !manifest.files.some((file) => file.path.endsWith(".md"))) return false - const users = manifest.files.find((file) => file.path === "_tables/users.json") - const usermeta = manifest.files.find((file) => file.path === "_tables/usermeta.json") - if (!users || !usermeta) return false - const [usersObject, usermetaObject] = await Promise.all([bucket.get(users.objectKey), bucket.get(usermeta.objectKey)]) - if (!usersObject || !usermetaObject) return false - const [usersRows, usermetaRows] = await Promise.all([ - usersObject.json>(), - usermetaObject.json>(), - ]) - const admin = usersRows.find((row) => row.user_login === "admin") - return Boolean(admin?.ID && usermetaRows.some((row) => row.user_id === admin.ID)) -} - function canonicalBootstrapSetupCode(passwordFile: string, origin: string): string { return ` { + const body = await response.text() + assertNoPhpDiagnostics(body, "concurrent canonical mutation") + if (response.status !== 200) throw new Error(`Expected concurrent canonical mutation, received ${response.status}: ${body}`) + return JSON.parse(body) + })) + const revisions = mutations.map((mutation) => mutation.revisionValue).sort((left, right) => left - right) + if (revisions[1] !== revisions[0] + 1 || !mutations.some((mutation) => mutation.previousPostFound)) { + throw new Error(`Concurrent canonical mutations were not serialized: ${JSON.stringify(mutations)}`) + } +} + async function assertHealthResponse() { const response = await request(`${origin}/?phase=health`) const body = await response.json() @@ -110,6 +132,16 @@ async function assertWordPressPage(target, label) { return body } +async function assertAuthenticatedDashboard(target) { + const response = await request(target) + const body = await response.text() + assertNoPhpDiagnostics(body, "wp-admin") + if (response.url.includes("wp-login.php") || response.redirected || response.status !== 200 || !/id=["']wpadminbar["']/.test(body) || !/id=["']dashboard-widgets["']/.test(body)) { + throw new Error(`Expected an authenticated wp-admin dashboard, received ${response.status} at ${response.url}; cookie names: ${cookieNames().join(", ")}`) + } + return body +} + async function assertLinkedAssets(html, label) { const links = [...html.matchAll(/<(?:link|script)\b[^>]*?\b(?:href|src)=["']([^"']+\.(?:css|js)(?:\?[^"']*)?)["']/gi)].slice(0, 2) if (!links.length) throw new Error(`Expected ${label} HTML to link CSS or JavaScript assets.`) @@ -123,17 +155,50 @@ async function assertLinkedAssets(html, label) { async function request(target, options = {}) { const headers = new Headers(options.headers) - if (cookies.size) headers.set("cookie", [...cookies].map(([name, value]) => `${name}=${value}`).join("; ")) + const requestUrl = new URL(target) + const requestCookies = cookiesFor(requestUrl) + if (requestCookies.length) headers.set("cookie", requestCookies.join("; ")) const response = await fetch(target, { ...options, headers }) - const setCookies = response.headers.getSetCookie?.() ?? (response.headers.get("set-cookie") ? [response.headers.get("set-cookie")] : []) - for (const cookie of setCookies) { - const [pair] = cookie.split(";", 1) - const separator = pair.indexOf("=") - if (separator > 0) cookies.set(pair.slice(0, separator), pair.slice(separator + 1)) - } + const setCookies = response.headers.getSetCookie?.() + if (!setCookies) throw new Error("The local gate requires Headers.getSetCookie() to preserve distinct WordPress login cookies.") + for (const cookie of setCookies) storeCookie(cookie, requestUrl) + if (new URL(target).pathname === "/wp-login.php" && options.method === "POST") console.log(`Login response cookie names: ${cookieNames().join(", ")}`) return response } +function storeCookie(header, requestUrl) { + const parts = header.split(";").map((part) => part.trim()) + const separator = parts[0].indexOf("=") + if (separator <= 0) throw new Error("Invalid Set-Cookie header.") + const name = parts[0].slice(0, separator) + const value = parts[0].slice(separator + 1) + const attributes = new Map(parts.slice(1).map((part) => { + const index = part.indexOf("=") + return [index === -1 ? part.toLowerCase() : part.slice(0, index).toLowerCase(), index === -1 ? "" : part.slice(index + 1)] + })) + const hostOnly = !attributes.has("domain") + const domain = (attributes.get("domain") || requestUrl.hostname).replace(/^\./, "").toLowerCase() + if (!hostOnly && requestUrl.hostname !== domain && !requestUrl.hostname.endsWith(`.${domain}`)) throw new Error(`Set-Cookie domain ${domain} does not match ${requestUrl.hostname}.`) + const path = attributes.get("path") || requestUrl.pathname.slice(0, requestUrl.pathname.lastIndexOf("/") + 1) || "/" + const expires = attributes.get("max-age") === "0" ? 0 : attributes.has("expires") ? Date.parse(attributes.get("expires")) : undefined + const index = cookies.findIndex((cookie) => cookie.name === name && cookie.domain === domain && cookie.path === path) + if (expires === 0 || (expires && expires <= Date.now())) { + if (index !== -1) cookies.splice(index, 1) + return + } + const cookie = { name, value, domain, hostOnly, path, secure: attributes.has("secure"), expires } + if (index === -1) cookies.push(cookie) + else cookies[index] = cookie +} + +function cookiesFor(url) { + return cookies.filter((cookie) => (!cookie.secure || url.protocol === "https:") && (url.hostname === cookie.domain || (!cookie.hostOnly && url.hostname.endsWith(`.${cookie.domain}`))) && url.pathname.startsWith(cookie.path) && (!cookie.expires || cookie.expires > Date.now())).map((cookie) => `${cookie.name}=${cookie.value}`) +} + +function cookieNames() { + return cookies.map((cookie) => cookie.name).sort() +} + function assertIncludes(html, expected, label) { assertNoPhpDiagnostics(html, label) if (!html.includes(expected)) throw new Error(`${label} did not contain ${JSON.stringify(expected)}.`) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 9b406c5b..0dcbf2ce 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -9,6 +9,7 @@ import { RUNTIME_COMMAND_RESULT_SCHEMA } from "../packages/runtime-core/src/runt import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "../packages/runtime-cloudflare/src/health-envelope.js" import { routeWorkerRequest } from "../packages/runtime-cloudflare/src/request-routing.js" import { toFetchResponse, toPHPRequest } from "../packages/runtime-cloudflare/src/request-translation.js" +import { WordPressStateCoordinator } from "../packages/runtime-cloudflare/src/state-coordinator.js" const execFileAsync = promisify(execFile) @@ -123,23 +124,32 @@ test("Cloudflare runtime pins and bundles the public constrained MDI runtime", a ]) }) -test("Cloudflare routes WordPress and health through the serialized Durable Object runtime", async () => { +test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object only for leases", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const coordinator = await readFile(new URL("../packages/runtime-cloudflare/src/state-coordinator.ts", import.meta.url), "utf8") const materializer = worker.slice(worker.indexOf("async function materializeWordPressServerFiles"), worker.indexOf("function isWordPressServerFile")) const predicate = worker.slice(worker.indexOf("function isWordPressServerFile"), worker.indexOf("function createPhpRuntime")) - assert.match(worker, /return env\.WORDPRESS_STATE\.getByName\("default"\)\.fetch\(request\)/) - assert.match(worker, /private runtimePromise/) - assert.match(worker, /private async getRuntime\(origin: string\)/) - assert.match(worker, /if \(this\.runtimePromise === runtime\) this\.runtimePromise = undefined/) - assert.match(worker, /route\.kind === "health"\) return this\.health\(runtime\)/) - assert.match(worker, /runSyntheticMutation\(new URL\(request\.url\)\.origin\)/) - assert.match(worker, /private async runSyntheticMutation\(origin: string\)/) + assert.match(worker, /return runCoordinatedWordPressRequest\(request, env, coordinator, route\.kind\)/) + assert.match(worker, /let cachedRuntime/) + assert.match(worker, /cachedRuntime\.baseRevision !== pointer\.revision/) + assert.match(worker, /promise\.catch\(\(\) =>/) + assert.match(worker, /runtime\.pointer = next/) + assert.match(worker, /cacheRuntime\(next, runtime\)/) + assert.match(worker, /await abortLease\(coordinator, request\.url, lease\)/) + assert.match(worker, /const LEASE_ACQUISITION_TIMEOUT_MS = 100_000/) assert.match(worker, /bootWordPressRuntime\("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision/) - const noPointerBoot = worker.slice(worker.indexOf("private async bootRuntime"), worker.indexOf("private async persistRuntime")) + const noPointerBoot = worker.slice(worker.indexOf("async function bootstrapCanonicalRuntime"), worker.indexOf("async function persistRuntime")) assert.match(noPointerBoot, /packagedCanonicalMarkdownSeed\(\)/) assert.match(noPointerBoot, /canonicalBootstrapSetupCode\(passwordFile, origin\)/) + assert.match(noPointerBoot, /await commitLease\(coordinator, requestUrl, lease, pointer\)/) assert.doesNotMatch(noPointerBoot, /wordpressInstallSeed|databaseSeed|bootstrap_existing_cache/) + assert.doesNotMatch(coordinator, /@php-wasm|PHPRequestHandler|bootWordPressRuntime|new PHP\(/) + assert.match(coordinator, /token: crypto\.randomUUID\(\)/) + assert.match(coordinator, /record\.version\+\+/) + assert.match(coordinator, /lease\.expiresAt <= Date\.now\(\)/) + assert.match(worker, /phase === "canonical-session"/) + assert.match(worker, /key\.endsWith\("session_tokens"\)/) assert.match(worker, /maxPhpInstances: 1/) assert.match(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL,\s*\(entry: \{ path: Uint8Array \}\) => isWordPressServerFile\(decoder\.decode\(entry\.path\)\)\)/) assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL\)(?!\s*,)/) @@ -147,6 +157,39 @@ test("Cloudflare routes WordPress and health through the serialized Durable Obje assert.match(predicate, /path\.startsWith\("wordpress\/wp-admin\/"\)\) return true/) }) +test("Cloudflare coordinator serializes leases, promotes with CAS, and recovers stale leases", async () => { + const values = new Map() + const objects = new Map() + const state = { + id: { toString: () => "test-do" }, + storage: { + get: async (key: string) => values.get(key) as T | undefined, + put: async (key: string, value: unknown) => { values.set(key, value) }, + }, + } + const bucket = { + get: async (key: string) => objects.has(key) ? { json: async () => JSON.parse(objects.get(key)!) as T } : null, + put: async (key: string, value: string) => { objects.set(key, value) }, + } + const coordinator = new WordPressStateCoordinator(state as never, { WORDPRESS_STATE_BUCKET: bucket as never, COORDINATOR_LEASE_MS: 50 }) + const call = async (action: string, body: Record = {}) => coordinator.fetch(new Request(`https://worker.example/?__wp_codebox_coordinator=${action}`, { method: action === "state" ? "GET" : "POST", headers: { "content-type": "application/json" }, body: action === "state" ? undefined : JSON.stringify(body) })) + + const first = await (await call("begin")).json() as { token: string; pointer: null; version: number } + assert.equal(first.pointer, null) + assert.equal((await call("begin")).status, 409) + const pointer = { revision: "one", manifestKey: "sites/default/markdown/revisions/one.json", persistedAt: "2026-01-01T00:00:00.000Z" } + assert.equal((await call("commit", { token: first.token, baseRevision: null, version: first.version, pointer })).status, 200) + assert.equal(JSON.parse(objects.get("sites/default/markdown/current.json")!).revision, "one") + const second = await (await call("begin")).json() as { token: string; pointer: { revision: string } } + assert.equal(second.pointer.revision, "one") + assert.equal((await call("abort", { token: second.token })).status, 200) + const stale = await (await call("begin")).json() as { token: string } + await new Promise((resolve) => setTimeout(resolve, 60)) + const recovered = await (await call("begin")).json() as { token: string } + assert.notEqual(recovered.token, stale.token) + assert.equal((await call("release", { token: recovered.token })).status, 200) +}) + test("serialized Cloudflare mutations use MDI flush paths and complete canonical state", async () => { const source = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const mutation = source.slice(source.indexOf("const SERIALIZED_MARKDOWN_MUTATION_CODE"), source.indexOf("interface Env")) @@ -160,8 +203,6 @@ test("serialized Cloudflare mutations use MDI flush paths and complete canonical assert.match(source, /flush_canonical_writes\(\)/) assert.match(source, /packagedCanonicalMarkdownSeed/) assert.match(source, /update_option\('siteurl'/) - assert.match(source, /_tables\/termmeta\.json/) - assert.match(source, /_tables\/users\.json/) assert.match(source, /WORDPRESS_ADMIN_PASSWORD is required/) }) From 8138eb144f68922e8426ba3d3d760133eb1f3c41 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 01:12:05 -0400 Subject: [PATCH 04/87] Bound the Cloudflare WordPress runtime --- package.json | 2 + packages/runtime-cloudflare/README.md | 6 +- .../runtime-cloudflare/src/request-routing.ts | 2 + .../runtime-cloudflare/src/wordpress-auth.ts | 14 +++ .../src/wordpress-runtime-corpus.ts | 33 +++++++ packages/runtime-cloudflare/src/worker.ts | 96 ++++++++++++++++--- packages/runtime-cloudflare/wrangler.jsonc | 1 + scripts/cloudflare-local-gate.mjs | 52 ++++++++-- tests/cloudflare-runtime.test.ts | 30 ++++-- ...loudflare-wordpress-archive-corpus.test.ts | 45 +++++++++ tests/cloudflare-wordpress-auth.test.ts | 15 +++ 11 files changed, 261 insertions(+), 35 deletions(-) create mode 100644 packages/runtime-cloudflare/src/wordpress-auth.ts create mode 100644 packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts create mode 100644 tests/cloudflare-wordpress-archive-corpus.test.ts create mode 100644 tests/cloudflare-wordpress-auth.test.ts diff --git a/package.json b/package.json index c7db659d..7a5098e0 100644 --- a/package.json +++ b/package.json @@ -95,6 +95,8 @@ "smoke": "tsx scripts/run-smoke.ts", "test:redaction": "tsx tests/redaction.test.ts", "test:cloudflare-runtime": "tsx tests/cloudflare-runtime.test.ts && node ./node_modules/typescript/bin/tsc -p packages/runtime-cloudflare --noEmit", + "test:cloudflare-wordpress-auth": "tsx tests/cloudflare-wordpress-auth.test.ts", + "test:cloudflare-wordpress-archive-corpus": "tsx tests/cloudflare-wordpress-archive-corpus.test.ts", "test:agent-task-contracts": "tsx tests/agent-task-contracts.test.ts && tsx tests/agent-task-canonical-evidence.test.ts && npm run test:agent-task-workflow-interface && npm run test:runtime-sources-materialization && tsx tests/agent-task-reusable-workflow.test.ts", "test:agent-task-workflow-interface": "tsx tests/run-agent-task-reusable-workflow-interface.test.ts", "test:agent-task-runtime-package-staging": "tsx tests/agent-task-runtime-package-staging.test.ts", diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index d0322525..f73d1191 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -8,8 +8,12 @@ The entry Worker executes PHP-WASM and WordPress. The named `WordPressStateCoord On cold start, the entry Worker uses the acquired pointer to rebuild PHP-WASM's disposable SQLite index from canonical MDI Markdown and JSON files. A missing pointer materializes the packaged canonical MDI seed and boots one PHP-WASM primary runtime. The build-time PHP CLI generator creates that archive from `wordpress-install-seed.sqlite` through MDI's public `bootstrap_existing_cache()` API, validates its pinned MDI revision and input digest, and never packages SQLite. The runtime updates `siteurl` and `home` through WordPress APIs using the request origin and sets the admin password from `WORDPRESS_ADMIN_PASSWORD`; only WordPress's password hash is canonical. Bootstrap persists and CAS-promotes this mutation before serving the next request. +Canonical browser, health, and mutation boots require the separately managed `WORDPRESS_AUTH_SECRET` Worker secret. The entry Worker derives the eight WordPress auth keys and salts from that secret with a versioned, site-scoped (`default`) SHA-256 domain separator before `wp-load.php`. It never logs, persists, or returns the secret or derived values. Configure it independently from the bootstrap password with `wrangler secret put WORDPRESS_AUTH_SECRET --config packages/runtime-cloudflare/wrangler.jsonc`; rotating `WORDPRESS_ADMIN_PASSWORD` does not rotate authentication salts or invalidate sessions. + After each mutating HTTP request, the entry runtime invokes MDI's explicit request-boundary flush, collects canonical files, stores immutable content-addressed R2 objects and a revision manifest, then commits the new pointer through the held lease. GET, HEAD, and asset requests release without promotion. Failed requests abort their leases; stale leases recover by token/version/expiry checks. The entry isolate can cache one runtime only for the exact acquired pointer revision and exits it after promotion or when another isolate advances the pointer. It does not persist SQLite. Existing manifests are reused when canonical file hashes have not changed. +The Worker forwards browser cookies directly to Playground and disables Playground's internal cookie store, preventing an empty per-isolate store from replacing a valid browser session after cold restart. `?phase=canonical-auth` is a boolean-only diagnostic for the canonical runtime; it reports cookie parsing and verification stages without returning credential, token, hash, or salt material. + The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c7406f1f1ea6dc3` from [MDI PR #127](https://github.com/Automattic/markdown-database-integration/pull/127). The bundle generator, worker provenance, and source-contract test use the same revision. ## Verification @@ -17,6 +21,6 @@ The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c 1. Run `npm run generate:cloudflare-canonical-mdi-seed` to regenerate the deterministic canonical MDI archive and provenance manifest. 2. Run `npm run test:cloudflare-runtime` for routing, canonical-state, generator reproducibility, filtered archive, source contract, and TypeScript coverage. 3. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. -4. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It injects a test admin password and verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin assets, PHP diagnostics, and the same login/post checks after a cold restart using the persisted R2/DO state. +4. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 364f34bf..7221ac74 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -3,6 +3,7 @@ export type WorkerRequestRoute = | { kind: "health" } | { kind: "r2-state" } | { kind: "r2-mutate" } + | { kind: "canonical-auth" } | { kind: "probe"; phase: string } export function routeWorkerRequest(request: Request): WorkerRequestRoute { @@ -11,5 +12,6 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "health") return { kind: "health" } if (phase === "r2-state") return { kind: "r2-state" } if (phase === "r2-mutate") return { kind: "r2-mutate" } + if (phase === "canonical-auth") return { kind: "canonical-auth" } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/wordpress-auth.ts b/packages/runtime-cloudflare/src/wordpress-auth.ts new file mode 100644 index 00000000..6a08ae34 --- /dev/null +++ b/packages/runtime-cloudflare/src/wordpress-auth.ts @@ -0,0 +1,14 @@ +export const WORDPRESS_AUTH_CONSTANTS = ["AUTH_KEY", "SECURE_AUTH_KEY", "LOGGED_IN_KEY", "NONCE_KEY", "AUTH_SALT", "SECURE_AUTH_SALT", "LOGGED_IN_SALT", "NONCE_SALT"] as const + +export type WordPressAuthConstant = (typeof WORDPRESS_AUTH_CONSTANTS)[number] + +export async function deriveWordPressAuthConstants(rootSecret: string, site: string): Promise> { + if (!rootSecret) throw new Error("WORDPRESS_AUTH_SECRET is required for canonical WordPress runtime boots.") + const encoder = new TextEncoder() + const entries = await Promise.all(WORDPRESS_AUTH_CONSTANTS.map(async (name) => { + const input = encoder.encode(`wp-codebox/wordpress-auth/v1\0${site}\0${name}\0${rootSecret}`) + const digest = await crypto.subtle.digest("SHA-256", input) + return [name, Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("")] as const + })) + return Object.fromEntries(entries) as Record +} diff --git a/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts b/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts new file mode 100644 index 00000000..eca4cb10 --- /dev/null +++ b/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts @@ -0,0 +1,33 @@ +export interface WordPressArchiveEntry { + path: string + uncompressedSize: number + isDirectory: boolean +} + +const REQUIRED_RUNTIME_EXTENSION = /\.(?:php|json|crt|html)$/ +const RUNTIME_FONT_OR_IMAGE_EXTENSION = /\.(?:woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/ +const RUNTIME_SCRIPT_OR_STYLE_EXTENSION = /\.(?:css|js|mjs)$/ + +export function isWordPressRuntimeFile(path: string, archivePaths: ReadonlySet): boolean { + if (!path.startsWith("wordpress/") || path.endsWith("/") || path.endsWith(".map")) return false + if (path.startsWith("wordpress/wp-content/themes/")) return true + if (REQUIRED_RUNTIME_EXTENSION.test(path)) return true + if (path.includes("/src/") || path.includes("/test/") || path.includes("/tests/") || path.includes("/node_modules/")) return false + if (RUNTIME_FONT_OR_IMAGE_EXTENSION.test(path)) return true + if (!RUNTIME_SCRIPT_OR_STYLE_EXTENSION.test(path)) return false + if (path.endsWith(".mjs") || path.endsWith(".min.js") || path.endsWith(".min.css")) return true + + const minifiedSibling = path.replace(/\.(?:js|css)$/, (extension) => `.min${extension}`) + return !archivePaths.has(minifiedSibling) +} + +export function summarizeWordPressRuntimeCorpus(entries: Iterable) { + const entryList = Array.from(entries) + const archivePaths = new Set(entryList.map((entry) => entry.path)) + const selected = entryList.filter((entry) => !entry.isDirectory && isWordPressRuntimeFile(entry.path, archivePaths)) + return { + selected, + selectedFiles: selected.length, + selectedBytes: selected.reduce((total, entry) => total + entry.uncompressedSize, 0), + } +} diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index e94d093b..c47669a3 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -8,6 +8,8 @@ import phpWasmModule from "../../../node_modules/@php-wasm/web-8-5/asyncify/8_5_ import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "./health-envelope.js" import { routeWorkerRequest } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" +import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" +import { isWordPressRuntimeFile } from "./wordpress-runtime-corpus.js" import type { MarkdownPointer } from "./state-coordinator.js" export { WordPressStateCoordinator } from "./state-coordinator.js" import markdownDatabaseIntegrationRuntime from "../assets/markdown-database-integration-runtime.zip" @@ -68,6 +70,7 @@ interface Env { WORDPRESS_STATE: DurableObjectNamespace WORDPRESS_STATE_BUCKET: R2Bucket WORDPRESS_ADMIN_PASSWORD?: string + WORDPRESS_AUTH_SECRET?: string } export default { @@ -82,6 +85,7 @@ export default { if (request.method !== "GET") return new Response("WordPress state read requires GET.", { status: 405 }) return coordinator.fetch(new Request(coordinatorUrl(request.url, "state"))) } + if (route.kind === "canonical-auth") return runCoordinatedWordPressRequest(request, env, coordinator, route.kind) return runCoordinatedWordPressRequest(request, env, coordinator, route.kind) }, } @@ -126,7 +130,7 @@ interface Runtime { let cachedRuntime: { baseRevision: string; promise: Promise } | undefined const LEASE_ACQUISITION_TIMEOUT_MS = 100_000 -async function runCoordinatedWordPressRequest(request: Request, env: Env, coordinator: DurableObjectStub, route: "wordpress" | "health" | "r2-mutate"): Promise { +async function runCoordinatedWordPressRequest(request: Request, env: Env, coordinator: DurableObjectStub, route: "wordpress" | "health" | "r2-mutate" | "canonical-auth"): Promise { if (route === "r2-mutate" && request.method !== "POST") return new Response("WordPress state mutation requires POST.", { status: 405 }) let lease = await acquireLease(coordinator, request.url) let finalized = false @@ -144,6 +148,8 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi response = await runSyntheticMutation(runtime) } else if (route === "health") { response = await health(runtime) + } else if (route === "canonical-auth") { + response = await canonicalAuthProbe(runtime, request) } else { response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) } @@ -164,7 +170,7 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi } } -function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate"): boolean { +function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate" | "canonical-auth"): boolean { return route === "r2-mutate" || !["GET", "HEAD", "OPTIONS"].includes(request.method) } @@ -218,7 +224,7 @@ function commitLease(coordinator: DurableObjectStub, requestUrl: string, lease: async function getRuntime(env: Env, pointer: MarkdownPointer, origin: string): Promise { if (cachedRuntime && cachedRuntime.baseRevision !== pointer.revision) await discardCachedRuntime() if (!cachedRuntime) { - const promise = bootRuntime(env.WORDPRESS_STATE_BUCKET, pointer, origin) + const promise = bootRuntime(env.WORDPRESS_STATE_BUCKET, pointer, origin, await canonicalWordPressAuthConstants(env)) cachedRuntime = { baseRevision: pointer.revision, promise } promise.catch(() => { if (cachedRuntime?.promise === promise) cachedRuntime = undefined @@ -253,14 +259,14 @@ async function discardRuntime(runtime: Runtime): Promise { runtime.php.exit() } -async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: string): Promise { - return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(bucket, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin), pointer } +async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: string, authConstants: Record): Promise { + return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(bucket, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants), pointer } } async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { if (!env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") const origin = new URL(requestUrl).origin - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env)) try { const passwordFile = "/tmp/wordpress-admin-password" runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) @@ -275,6 +281,10 @@ async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStu } } +async function canonicalWordPressAuthConstants(env: Env): Promise> { + return deriveWordPressAuthConstants(env.WORDPRESS_AUTH_SECRET ?? "", "default") +} + async function persistRuntime(bucket: R2Bucket, runtime: Runtime): Promise { const output = (await runtime.php.run({ code: "flush_canonical_writes(); echo 'flushed';" })).text.trim() if (output !== "flushed") throw new Error("MDI did not confirm its canonical flush.") @@ -293,16 +303,57 @@ async function health(runtime: Runtime): Promise { return cloudflareRuntimeHealthResponse({ schema: CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, marker: CLOUDFLARE_RUNTIME_HEALTH_MARKER, wordpressVersion: runtime.wordpressVersion, phpVersion, runtime: { backend: "wordpress-playground", environment: "wordpress" }, evidence: { initialization: "completed", execution: "completed", initializationScope: "isolate" } }) } +async function canonicalAuthProbe(runtime: Runtime, request: Request): Promise { + const cookiePath = "/tmp/wp-codebox-canonical-auth-cookie" + runtime.php.writeFile(cookiePath, new TextEncoder().encode(request.headers.get("cookie") ?? "")) + const output = (await runtime.php.run({ code: `ID) : null; +$session_row = $user ? $GLOBALS['wpdb']->get_var($GLOBALS['wpdb']->prepare("SELECT meta_value FROM {$GLOBALS['wpdb']->usermeta} WHERE user_id = %d AND meta_key LIKE %s", $user->ID, '%session_tokens')) : null; +$session_value = is_string($session_row) ? maybe_unserialize($session_row) : null; +$auth_constants = ['AUTH_KEY', 'SECURE_AUTH_KEY', 'LOGGED_IN_KEY', 'NONCE_KEY', 'AUTH_SALT', 'SECURE_AUTH_SALT', 'LOGGED_IN_SALT', 'NONCE_SALT']; +echo json_encode([ + 'schema' => 'wp-codebox/cloudflare-canonical-auth/v1', + 'authCookiePresent' => isset($_COOKIE[LOGGED_IN_COOKIE]), + 'authCookieParsed' => is_array($parsed) && isset($parsed['username'], $parsed['expiration'], $parsed['token'], $parsed['hmac']), + 'adminCookiePresent' => isset($_COOKIE[AUTH_COOKIE]), + 'adminCookieParsed' => is_array($admin_parsed) && isset($admin_parsed['username'], $admin_parsed['expiration'], $admin_parsed['token'], $admin_parsed['hmac']), + 'userFound' => false !== $user, + 'sessionTokenRowPresent' => is_string($session_row) && $session_row !== '', + 'sessionTokenSerializedArray' => is_array($session_value), + 'sessionTokenVerified' => $session instanceof WP_Session_Tokens && is_string($token) && $token !== '' && $session->verify($token), + 'authConstantsDefined' => !array_diff($auth_constants, array_filter($auth_constants, 'defined')), + 'adminCookieValidated' => false !== wp_validate_auth_cookie('', 'auth'), + 'loggedInCookieValidated' => false !== wp_validate_auth_cookie('', 'logged_in'), +]);` })).text.trim() + return Response.json(JSON.parse(output) as Record) +} + async function canonicalSessionProbe(bucket: R2Bucket, state: { pointer: MarkdownPointer | null }): Promise { if (!state.pointer) return Response.json({ schema: "wp-codebox/cloudflare-canonical-session/v1", pointerRevision: null, sessionTokenRows: 0 }) const manifest = await readMarkdownManifest(bucket, state.pointer) + const users = manifest?.files.find((file) => file.path === "_tables/users.json") const usermeta = manifest?.files.find((file) => file.path === "_tables/usermeta.json") - if (!usermeta) throw new Error("The canonical WordPress revision is missing usermeta.") + if (!users || !usermeta) throw new Error("The canonical WordPress revision is missing users or usermeta.") + const usersObject = await bucket.get(users.objectKey) const object = await bucket.get(usermeta.objectKey) - if (!object) throw new Error("The canonical WordPress usermeta object is missing.") - const rows = await object.json>() + if (!usersObject || !object) throw new Error("The canonical WordPress user identity objects are missing.") + const usersRows = await usersObject.json>() + const rows = await object.json>() const usermetaKeys = rows.map((row) => row.meta_key).filter((key): key is string => typeof key === "string").sort() - return Response.json({ schema: "wp-codebox/cloudflare-canonical-session/v1", pointerRevision: state.pointer.revision, sessionTokenRows: usermetaKeys.filter((key) => key.endsWith("session_tokens")).length, hasSessionTokens: usermetaKeys.some((key) => key.endsWith("session_tokens")), usermetaKeys }) + const sessionTokenRows = rows.filter((row) => row.meta_key?.endsWith("session_tokens")) + const adminRow = usersRows.find((row) => typeof row.user_login === "string") + return Response.json({ schema: "wp-codebox/cloudflare-canonical-session/v1", pointerRevision: state.pointer.revision, sessionTokenRows: sessionTokenRows.length, hasSessionTokens: sessionTokenRows.some((row) => typeof row.meta_value === "string" && row.meta_value.length > 0), usersTableStructurallyValid: !!adminRow && (typeof adminRow.ID === "number" || (typeof adminRow.ID === "string" && /^\d+$/.test(adminRow.ID))) && typeof adminRow.user_pass === "string", usersTableFieldTypes: { id: typeof adminRow?.ID, login: typeof adminRow?.user_login, password: typeof adminRow?.user_pass }, usermetaTableStructurallyValid: rows.every((row) => typeof row.meta_key === "string" && typeof row.meta_value === "string"), usermetaKeys }) } class CoordinatorRequestError extends Error { @@ -390,6 +441,7 @@ $GLOBALS['wpdb']->flush_canonical_writes(); echo 'flushed';` } + async function packagedCanonicalMarkdownSeed(): Promise { const manifest = canonicalMarkdownSeedManifest as CanonicalSeedManifest if (manifest.schema !== "wp-codebox/cloudflare-canonical-mdi-seed/v1" || manifest.markdownDatabaseIntegrationRevision !== MARKDOWN_DATABASE_INTEGRATION_REVISION) throw new Error("Packaged canonical MDI seed provenance is invalid.") @@ -600,12 +652,14 @@ async function bootWordPressRuntime( markdownFiles?: RuntimeFile[], markdownIndexSeed?: Uint8Array, siteUrl = SITE_URL, + authConstants: Partial> = {}, ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, constants: { AUTOMATIC_UPDATER_DISABLED: true, DISABLE_WP_CRON: true, + ...authConstants, ...(markdownFiles ? { MARKDOWN_DB_CONTENT_DIR: MARKDOWN_ROOT, MARKDOWN_DB_EXCLUDED_TYPES: "revision,auto-draft,nav_menu_item,customize_changeset,oembed_cache,wp_navigation,wp_global_styles,wp_template,wp_template_part", @@ -617,6 +671,9 @@ async function bootWordPressRuntime( WP_HTTP_BLOCK_EXTERNAL: true, }, dataSqlPath: DATABASE_PATH, + // Browser cookies are carried by the Worker Fetch request; Playground's + // internal store would overwrite that header after an isolate restart. + cookieStore: false, hooks: streamWordPressFiles || databaseSeed || markdownFiles ? { beforeWordPressFiles: streamWordPressFiles || markdownFiles ? async (php: PHP) => { if (streamWordPressFiles) await materializeWordPressServerFiles(php) @@ -710,13 +767,14 @@ async function materializeWordPressServerFiles(php: PHP): Promise<{ materialized const decoder = new TextDecoder() let materializedFiles = 0 let materializedBytes = 0 - const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry: { path: Uint8Array }) => isWordPressServerFile(decoder.decode(entry.path))) + const archivePaths = await wordPressArchivePaths(decoder) + const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry: { path: Uint8Array }) => isWordPressRuntimeFile(decoder.decode(entry.path), archivePaths)) const reader = stream.getReader() while (true) { const { done, value: entry } = await reader.read() if (done) break const path = entry instanceof File ? entry.name : decoder.decode(entry.path) - if (!isWordPressServerFile(path) || path.endsWith("/")) continue + if (!isWordPressRuntimeFile(path, archivePaths)) continue const destination = `/${path}` const bytes = entry instanceof File ? new Uint8Array(await entry.arrayBuffer()) : entry.bytes @@ -728,9 +786,17 @@ async function materializeWordPressServerFiles(php: PHP): Promise<{ materialized return { materializedFiles, materializedBytes } } -function isWordPressServerFile(path: string): boolean { - if (path.startsWith("wordpress/wp-admin/")) return true - return /\.(?:php|json|crt|html|css|js|mjs|woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/.test(path) +async function wordPressArchivePaths(decoder: TextDecoder): Promise> { + const paths = new Set() + // decodeRemoteZip filters central-directory entries before requesting file ranges. + const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry: { path: Uint8Array }) => { + paths.add(decoder.decode(entry.path)) + return false + }) + for await (const _ of stream) { + // Draining the metadata-only stream completes central-directory parsing. + } + return paths } function createPhpRuntime() { diff --git a/packages/runtime-cloudflare/wrangler.jsonc b/packages/runtime-cloudflare/wrangler.jsonc index 490d017d..80c6c7c1 100644 --- a/packages/runtime-cloudflare/wrangler.jsonc +++ b/packages/runtime-cloudflare/wrangler.jsonc @@ -1,3 +1,4 @@ +// Configure WORDPRESS_AUTH_SECRET with `wrangler secret put`; it is intentionally absent from this file. { "name": "wp-codebox-cloudflare-runtime", "main": "src/worker.ts", diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index a355a77c..be7e0584 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -6,6 +6,7 @@ import { join } from "node:path" const port = 8792 const origin = `http://127.0.0.1:${port}` const password = "cloudflare-runtime-test-password" +const authSecret = "cloudflare-runtime-test-auth-secret" const stateDirectory = await mkdtemp(join(tmpdir(), "wp-codebox-cloudflare-gate-")) const cookies = [] let child @@ -15,21 +16,27 @@ try { await startWorker() await assertConcurrentMutations() const adminHtml = await login() + const editorHtml = await assertPostNewEditor() const post = await createPost(adminHtml) const frontPage = await assertWordPressPage(`${origin}/${post.slug}/`, "published post") assertIncludes(frontPage, post.title, "published post") await assertHealthResponse() await assertLinkedAssets(frontPage, "front-end") await assertLinkedAssets(adminHtml, "admin") + await assertLinkedAssets(editorHtml, "editor") + await assertCanonicalAuth("before restart") await stopWorker() - cookies.length = 0 await startWorker() - const restartedAdmin = await login() + await assertCanonicalIdentity("after restart") + await assertCanonicalAuth("after restart") + const restartedAdmin = await assertAuthenticatedDashboard(new URL("/wp-admin/", origin)) const restartedPost = await assertWordPressPage(`${origin}/${post.slug}/`, "post after cold restart") assertIncludes(restartedPost, post.title, "post after cold restart") await assertLinkedAssets(restartedAdmin, "admin after cold restart") - console.log("Cloudflare local runtime gate passed: login, concurrent canonical mutations, authenticated REST post creation, frontend/admin assets, and cold-restart persistence.") + cookies.length = 0 + await login() + console.log("Cloudflare local runtime gate passed: login, dashboard, post editor, concurrent canonical mutations, authenticated REST post creation, frontend/admin/editor assets, and cold-restart session persistence.") } finally { await stopWorker() await rm(stateDirectory, { recursive: true, force: true }) @@ -37,7 +44,7 @@ try { async function startWorker() { output = "" - child = spawn("npm", ["exec", "--", "wrangler", "dev", "--config", "packages/runtime-cloudflare/wrangler.jsonc", "--port", String(port), "--persist-to", stateDirectory, "--var", `WORDPRESS_ADMIN_PASSWORD:${password}`], { + child = spawn("npm", ["exec", "--", "wrangler", "dev", "--config", "packages/runtime-cloudflare/wrangler.jsonc", "--port", String(port), "--persist-to", stateDirectory, "--var", `WORDPRESS_ADMIN_PASSWORD:${password}`, "--var", `WORDPRESS_AUTH_SECRET:${authSecret}`], { cwd: process.cwd(), // The host PAC resolves these public archive hosts through an unavailable local proxy. env: { ...process.env, NO_PROXY: "wordpress.org,github.com,codeload.github.com", no_proxy: "wordpress.org,github.com,codeload.github.com" }, @@ -78,12 +85,26 @@ async function login() { const location = response.headers.get("location") if (!location?.includes("/wp-admin/")) throw new Error(`Login did not redirect to wp-admin: ${location}`) const admin = await assertAuthenticatedDashboard(new URL(location, origin)) - const session = await (await request(`${origin}/?phase=canonical-session`)).json() - if (!session.hasSessionTokens || session.sessionTokenRows < 1) throw new Error(`Login did not persist a canonical WordPress session token: ${JSON.stringify(session)}`) - console.log(`Canonical login session: revision ${session.pointerRevision}, session-token rows ${session.sessionTokenRows}, usermeta keys ${session.usermetaKeys.join(", ")}`) + await assertCanonicalIdentity("after login") return admin } +async function assertCanonicalIdentity(stage) { + const session = await (await request(`${origin}/?phase=canonical-session`)).json() + if (!session.hasSessionTokens || session.sessionTokenRows < 1 || !session.usersTableStructurallyValid || !session.usermetaTableStructurallyValid) throw new Error(`Login did not persist a structurally valid canonical identity: ${JSON.stringify(session)}`) + console.log(`Canonical identity ${stage}: revision ${session.pointerRevision}, session-token rows ${session.sessionTokenRows}, users field types ${JSON.stringify(session.usersTableFieldTypes)}, usermeta keys ${session.usermetaKeys.join(", ")}`) +} + +async function assertCanonicalAuth(stage) { + const response = await request(`${origin}/wp-admin/?phase=canonical-auth`) + const diagnostic = await response.json() + console.log(`Canonical auth ${stage}: ${JSON.stringify(diagnostic)}`) + const checks = ["authCookiePresent", "authCookieParsed", "adminCookiePresent", "adminCookieParsed", "userFound", "sessionTokenRowPresent", "sessionTokenSerializedArray", "sessionTokenVerified", "authConstantsDefined", "adminCookieValidated", "loggedInCookieValidated"] + if (!response.ok || diagnostic.schema !== "wp-codebox/cloudflare-canonical-auth/v1" || checks.some((check) => diagnostic[check] !== true)) { + throw new Error(`Canonical auth diagnostic failed ${stage}: ${JSON.stringify(diagnostic)}`) + } +} + async function createPost(adminHtml) { const nonce = adminHtml.match(/"nonce":"([^"]+)"/)?.[1] if (!nonce) throw new Error("wp-admin did not expose a REST nonce.") @@ -101,6 +122,16 @@ async function createPost(adminHtml) { return { slug: post.slug, title } } +async function assertPostNewEditor() { + const response = await request(`${origin}/wp-admin/post-new.php`) + const body = await response.text() + assertNoPhpDiagnostics(body, "post editor") + if (response.status !== 200 || response.url.includes("wp-login.php") || !/wp-edit-post|block-editor/i.test(body)) { + throw new Error(`Expected the authenticated block editor, received ${response.status} at ${response.url}.`) + } + return body +} + async function assertConcurrentMutations() { const responses = await Promise.all([ fetch(`${origin}/?phase=r2-mutate`, { method: "POST" }), @@ -143,9 +174,10 @@ async function assertAuthenticatedDashboard(target) { } async function assertLinkedAssets(html, label) { - const links = [...html.matchAll(/<(?:link|script)\b[^>]*?\b(?:href|src)=["']([^"']+\.(?:css|js)(?:\?[^"']*)?)["']/gi)].slice(0, 2) - if (!links.length) throw new Error(`Expected ${label} HTML to link CSS or JavaScript assets.`) - for (const match of links) { + const links = [...html.matchAll(/<(?:link|script)\b[^>]*?\b(?:href|src)=["']([^"']+\.(?:css|js)(?:\?[^"']*)?)["']/gi)] + const representatives = [links.find((match) => /\.css(?:\?|$)/i.test(match[1])), links.find((match) => /\.js(?:\?|$)/i.test(match[1]))] + if (representatives.some((match) => !match)) throw new Error(`Expected ${label} HTML to link both CSS and JavaScript assets.`) + for (const match of representatives) { const response = await request(new URL(match[1], origin)) const body = await response.text() assertNoPhpDiagnostics(body, `${label} asset ${match[1]}`) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 0dcbf2ce..535fb91a 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -38,6 +38,7 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=health")), { kind: "health" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-state")), { kind: "r2-state" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-mutate")), { kind: "r2-mutate" }) + assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=canonical-auth")), { kind: "canonical-auth" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) @@ -70,12 +71,12 @@ test("Cloudflare translates Fetch requests and PHP responses without losing brow }) test("Cloudflare runtime declares the paid-plan WordPress boot CPU budget", async () => { - const config = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")) as { limits?: { cpu_ms?: number } } + const config = JSON.parse((await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")).replace(/^\s*\/\/.*\n/, "")) as { limits?: { cpu_ms?: number } } assert.equal(config.limits?.cpu_ms, 300_000) }) test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => { - const config = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")) as { + const config = JSON.parse((await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")).replace(/^\s*\/\/.*\n/, "")) as { rules?: Array<{ type?: string; globs?: string[] }> r2_buckets?: Array<{ binding?: string; bucket_name?: string }> durable_objects?: { bindings?: Array<{ name?: string; class_name?: string }> } @@ -127,8 +128,8 @@ test("Cloudflare runtime pins and bundles the public constrained MDI runtime", a test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object only for leases", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const coordinator = await readFile(new URL("../packages/runtime-cloudflare/src/state-coordinator.ts", import.meta.url), "utf8") - const materializer = worker.slice(worker.indexOf("async function materializeWordPressServerFiles"), worker.indexOf("function isWordPressServerFile")) - const predicate = worker.slice(worker.indexOf("function isWordPressServerFile"), worker.indexOf("function createPhpRuntime")) + const materializer = worker.slice(worker.indexOf("async function materializeWordPressServerFiles"), worker.indexOf("function createPhpRuntime")) + const corpus = await readFile(new URL("../packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts", import.meta.url), "utf8") assert.match(worker, /return runCoordinatedWordPressRequest\(request, env, coordinator, route\.kind\)/) assert.match(worker, /let cachedRuntime/) @@ -149,12 +150,23 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(coordinator, /record\.version\+\+/) assert.match(coordinator, /lease\.expiresAt <= Date\.now\(\)/) assert.match(worker, /phase === "canonical-session"/) - assert.match(worker, /key\.endsWith\("session_tokens"\)/) + assert.match(worker, /canonicalAuthProbe\(runtime, request\)/) + assert.match(worker, /WP_Session_Tokens::get_instance/) + assert.match(worker, /wp_validate_auth_cookie\('', 'logged_in'\)/) + assert.match(worker, /authConstantsDefined/) + assert.match(worker, /adminCookieValidated/) + assert.match(worker, /cookieStore: false/) + assert.match(worker, /meta_key\?\.endsWith\("session_tokens"\)/) assert.match(worker, /maxPhpInstances: 1/) - assert.match(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL,\s*\(entry: \{ path: Uint8Array \}\) => isWordPressServerFile\(decoder\.decode\(entry\.path\)\)\)/) - assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL\)(?!\s*,)/) - assert.match(predicate, /php\|json\|crt\|html\|css\|js\|mjs\|woff2\?\|ttf\|otf\|eot\|svg\|png\|jpe\?g\|gif\|webp\|avif\|ico/) - assert.match(predicate, /path\.startsWith\("wordpress\/wp-admin\/"\)\) return true/) + assert.match(worker, /WORDPRESS_AUTH_SECRET/) + assert.match(worker, /canonicalWordPressAuthConstants\(env\)/) + assert.match(worker, /authConstants/) + assert.match(materializer, /const archivePaths = await wordPressArchivePaths\(decoder\)/) + assert.match(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL,\s*\(entry: \{ path: Uint8Array \}\) => isWordPressRuntimeFile\(decoder\.decode\(entry\.path\), archivePaths\)\)/) + assert.match(materializer, /return false/) + assert.match(corpus, /path\.endsWith\("\.map"\)/) + assert.match(corpus, /path\.startsWith\("wordpress\/wp-content\/themes\/"\)/) + assert.match(corpus, /minifiedSibling/) }) test("Cloudflare coordinator serializes leases, promotes with CAS, and recovers stale leases", async () => { diff --git a/tests/cloudflare-wordpress-archive-corpus.test.ts b/tests/cloudflare-wordpress-archive-corpus.test.ts new file mode 100644 index 00000000..00909ae6 --- /dev/null +++ b/tests/cloudflare-wordpress-archive-corpus.test.ts @@ -0,0 +1,45 @@ +import assert from "node:assert/strict" +import test from "node:test" +import { decodeRemoteZip, type CentralDirectoryEntry } from "@php-wasm/stream-compression" +import { isWordPressRuntimeFile, summarizeWordPressRuntimeCorpus, type WordPressArchiveEntry } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" + +const WORDPRESS_ARCHIVE_URL = "https://wordpress.org/latest.zip" +// The legacy selection is 85 MB; 64 MiB preserves 13 MiB of growth room while +// remaining safely below the entry Worker's 128 MB memory ceiling. +const MAX_RUNTIME_CORPUS_BYTES = 64 * 1024 * 1024 + +test("WordPress production runtime corpus stays within the Worker materialization budget", async () => { + const decoder = new TextDecoder() + const entries: WordPressArchiveEntry[] = [] + const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry) => { + const directoryEntry = entry as CentralDirectoryEntry + entries.push({ path: decoder.decode(directoryEntry.path), uncompressedSize: directoryEntry.uncompressedSize, isDirectory: directoryEntry.isDirectory }) + return false + }) + for await (const _ of stream) { + // The predicate records central-directory metadata and selects no file ranges. + } + + const corpus = summarizeWordPressRuntimeCorpus(entries) + const archivePaths = new Set(entries.map((entry) => entry.path)) + const legacy = entries.filter((entry) => !entry.isDirectory && (entry.path.startsWith("wordpress/wp-admin/") || /\.(?:php|json|crt|html|css|js|mjs|woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/.test(entry.path))) + const excluded = entries.filter((entry) => !entry.isDirectory && !isWordPressRuntimeFile(entry.path, archivePaths)) + const byGroup = (files: WordPressArchiveEntry[]) => Object.entries(files.reduce>((groups, entry) => { + const group = entry.path.split("/").slice(0, 3).join("/") + const current = groups[group] ?? { files: 0, bytes: 0 } + current.files++ + current.bytes += entry.uncompressedSize + groups[group] = current + return groups + }, {})).sort(([, left], [, right]) => right.bytes - left.bytes).slice(0, 8) + + console.log(JSON.stringify({ + archiveEntries: entries.length, + selectedFiles: corpus.selectedFiles, + selectedBytes: corpus.selectedBytes, + legacyFiles: legacy.length, + legacyBytes: legacy.reduce((total, entry) => total + entry.uncompressedSize, 0), + largestExcludedGroups: byGroup(excluded), + })) + assert.ok(corpus.selectedBytes <= MAX_RUNTIME_CORPUS_BYTES, `Selected ${corpus.selectedBytes} bytes exceeds the ${MAX_RUNTIME_CORPUS_BYTES}-byte runtime corpus budget.`) +}) diff --git a/tests/cloudflare-wordpress-auth.test.ts b/tests/cloudflare-wordpress-auth.test.ts new file mode 100644 index 00000000..d69b4e57 --- /dev/null +++ b/tests/cloudflare-wordpress-auth.test.ts @@ -0,0 +1,15 @@ +import assert from "node:assert/strict" +import test from "node:test" +import { deriveWordPressAuthConstants, WORDPRESS_AUTH_CONSTANTS } from "../packages/runtime-cloudflare/src/wordpress-auth.js" + +test("WordPress auth constants are deterministic, site-scoped, and domain-separated", async () => { + const first = await deriveWordPressAuthConstants("test-root-secret", "default") + const second = await deriveWordPressAuthConstants("test-root-secret", "default") + const otherSite = await deriveWordPressAuthConstants("test-root-secret", "other") + + assert.deepEqual(first, second) + assert.deepEqual(Object.keys(first), WORDPRESS_AUTH_CONSTANTS) + assert.equal(new Set(Object.values(first)).size, WORDPRESS_AUTH_CONSTANTS.length) + assert.notEqual(first.AUTH_KEY, otherSite.AUTH_KEY) + await assert.rejects(deriveWordPressAuthConstants("", "default"), /WORDPRESS_AUTH_SECRET is required/) +}) From 36a6c1e5dd76c961760c34b8519f53453ca9e8d8 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 01:28:50 -0400 Subject: [PATCH 05/87] Stream WordPress browser assets outside PHP --- .../src/wordpress-runtime-corpus.ts | 61 +++++++++++++++---- packages/runtime-cloudflare/src/worker.ts | 51 ++++++++++++++-- scripts/cloudflare-local-gate.mjs | 14 +++++ tests/cloudflare-runtime.test.ts | 43 ++++++++++++- ...loudflare-wordpress-archive-corpus.test.ts | 4 +- 5 files changed, 150 insertions(+), 23 deletions(-) diff --git a/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts b/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts index eca4cb10..80077dd0 100644 --- a/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts +++ b/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts @@ -4,21 +4,58 @@ export interface WordPressArchiveEntry { isDirectory: boolean } -const REQUIRED_RUNTIME_EXTENSION = /\.(?:php|json|crt|html)$/ -const RUNTIME_FONT_OR_IMAGE_EXTENSION = /\.(?:woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/ -const RUNTIME_SCRIPT_OR_STYLE_EXTENSION = /\.(?:css|js|mjs)$/ +const SERVER_READ_EXTENSION = /\.(?:php|json|crt|html)$/ +const STATIC_ASSET_EXTENSION = /\.(?:css|js|mjs|woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/ +const STATIC_ARCHIVE_ROOTS = ["wp-admin/", "wp-includes/", "wp-content/themes/"] +const SERVER_READ_ASSET_PATHS = new Set([ + // wp-includes/view-transitions.php in current WordPress reads this at bootstrap. + "wordpress/wp-admin/css/view-transitions.min.css", + // wp-includes/formatting.php inlines this loader into generated pages. + "wordpress/wp-includes/js/wp-emoji-loader.min.js", + // wp-admin/includes/dashboard.php inlines this background into dashboard markup. + "wordpress/wp-admin/images/dashboard-background.svg", +]) -export function isWordPressRuntimeFile(path: string, archivePaths: ReadonlySet): boolean { +export function isWordPressRuntimeFile(path: string, _archivePaths: ReadonlySet): boolean { if (!path.startsWith("wordpress/") || path.endsWith("/") || path.endsWith(".map")) return false - if (path.startsWith("wordpress/wp-content/themes/")) return true - if (REQUIRED_RUNTIME_EXTENSION.test(path)) return true - if (path.includes("/src/") || path.includes("/test/") || path.includes("/tests/") || path.includes("/node_modules/")) return false - if (RUNTIME_FONT_OR_IMAGE_EXTENSION.test(path)) return true - if (!RUNTIME_SCRIPT_OR_STYLE_EXTENSION.test(path)) return false - if (path.endsWith(".mjs") || path.endsWith(".min.js") || path.endsWith(".min.css")) return true + // PHP discovers all executable code and structured metadata server-side. Browser + // assets stay in the archive and are fetched directly by the entry Worker. + return SERVER_READ_EXTENSION.test(path) || path.endsWith("/style.css") || SERVER_READ_ASSET_PATHS.has(path) +} + +export function wordpressStaticArchivePath(pathname: string): string | null { + let decoded: string + try { + decoded = decodeURIComponent(pathname) + } catch { + return null + } + if (!decoded.startsWith("/") || decoded.includes("\\") || decoded.split("/").some((segment) => segment === "." || segment === "..")) return null + const relative = decoded.slice(1) + if (!STATIC_ARCHIVE_ROOTS.some((root) => relative.startsWith(root)) || !STATIC_ASSET_EXTENSION.test(relative)) return null + return `wordpress/${relative}` +} - const minifiedSibling = path.replace(/\.(?:js|css)$/, (extension) => `.min${extension}`) - return !archivePaths.has(minifiedSibling) +export function wordpressStaticContentType(archivePath: string): string { + const extension = archivePath.slice(archivePath.lastIndexOf(".") + 1).toLowerCase() + return ({ + css: "text/css; charset=utf-8", + js: "text/javascript; charset=utf-8", + mjs: "text/javascript; charset=utf-8", + svg: "image/svg+xml", + png: "image/png", + jpg: "image/jpeg", + jpeg: "image/jpeg", + gif: "image/gif", + webp: "image/webp", + avif: "image/avif", + ico: "image/x-icon", + woff: "font/woff", + woff2: "font/woff2", + ttf: "font/ttf", + otf: "font/otf", + eot: "application/vnd.ms-fontobject", + }[extension] ?? "application/octet-stream") } export function summarizeWordPressRuntimeCorpus(entries: Iterable) { diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index c47669a3..237ee123 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -9,7 +9,7 @@ import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, clo import { routeWorkerRequest } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" -import { isWordPressRuntimeFile } from "./wordpress-runtime-corpus.js" +import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" import type { MarkdownPointer } from "./state-coordinator.js" export { WordPressStateCoordinator } from "./state-coordinator.js" import markdownDatabaseIntegrationRuntime from "../assets/markdown-database-integration-runtime.zip" @@ -75,6 +75,8 @@ interface Env { export default { async fetch(request: Request, env: Env): Promise { + const staticResponse = await serveWordPressStaticAsset(request) + if (staticResponse) return staticResponse const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") if (route.kind === "probe") { @@ -658,7 +660,9 @@ async function bootWordPressRuntime( createPhpRuntime, constants: { AUTOMATIC_UPDATER_DISABLED: true, + CONCATENATE_SCRIPTS: false, DISABLE_WP_CRON: true, + SCRIPT_DEBUG: false, ...authConstants, ...(markdownFiles ? { MARKDOWN_DB_CONTENT_DIR: MARKDOWN_ROOT, @@ -758,11 +762,6 @@ async function materializeMarkdownDatabaseIntegration(php: PHP): Promise { } } -function archiveRelativePath(path: string): string { - const separator = path.indexOf("/") - return separator === -1 ? "" : path.slice(separator + 1) -} - async function materializeWordPressServerFiles(php: PHP): Promise<{ materializedFiles: number; materializedBytes: number }> { const decoder = new TextDecoder() let materializedFiles = 0 @@ -786,6 +785,46 @@ async function materializeWordPressServerFiles(php: PHP): Promise<{ materialized return { materializedFiles, materializedBytes } } +async function serveWordPressStaticAsset(request: Request): Promise { + if (request.method !== "GET" && request.method !== "HEAD") return null + const archivePath = wordpressStaticArchivePath(new URL(request.url).pathname) + if (!archivePath) return null + + const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default + if (request.method === "GET" && cache) { + try { + const cached = await cache.match(request) + if (cached) return cached + } catch { + // Cache availability is an optimization, never a dependency. + } + } + + const decoder = new TextDecoder() + const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry: { path: Uint8Array }) => decoder.decode(entry.path) === archivePath) + const reader = stream.getReader() + const { done, value: entry } = await reader.read() + if (done || !entry) return new Response("Not found", { status: 404 }) + const path = entry instanceof File ? entry.name : decoder.decode(entry.path) + if (path !== archivePath) return new Response("Not found", { status: 404 }) + const bytes = entry instanceof File ? new Uint8Array(await entry.arrayBuffer()) : entry.bytes + const response = new Response(request.method === "HEAD" ? null : bytes, { + headers: { + "cache-control": "public, max-age=3600", + "content-type": wordpressStaticContentType(archivePath), + "x-wp-codebox-static": "wordpress-archive", + }, + }) + if (request.method === "GET" && cache) { + try { + await cache.put(request, response.clone()) + } catch { + // A full or unavailable Worker cache must not affect the archive response. + } + } + return response +} + async function wordPressArchivePaths(decoder: TextDecoder): Promise> { const paths = new Set() // decodeRemoteZip filters central-directory entries before requesting file ranges. diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index be7e0584..ab8abcec 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -24,6 +24,7 @@ try { await assertLinkedAssets(frontPage, "front-end") await assertLinkedAssets(adminHtml, "admin") await assertLinkedAssets(editorHtml, "editor") + await assertStaticResponseSemantics() await assertCanonicalAuth("before restart") await stopWorker() @@ -182,9 +183,22 @@ async function assertLinkedAssets(html, label) { const body = await response.text() assertNoPhpDiagnostics(body, `${label} asset ${match[1]}`) if (!response.ok || !body.length) throw new Error(`Missing ${label} asset ${match[1]}: ${response.status}`) + if (response.headers.get("x-wp-codebox-static") !== "wordpress-archive") throw new Error(`${label} asset ${match[1]} did not bypass PHP through the WordPress archive static path.`) } } +async function assertStaticResponseSemantics() { + const asset = `${origin}/wp-includes/js/jquery/jquery.min.js?ver=3.7.1` + const get = await request(asset) + if (!get.ok || !get.headers.get("content-type")?.includes("javascript") || get.headers.get("x-wp-codebox-static") !== "wordpress-archive" || !get.headers.get("cache-control")?.includes("max-age")) throw new Error(`Unexpected static asset response: ${get.status}`) + const head = await request(asset, { method: "HEAD" }) + if (!head.ok || head.headers.get("x-wp-codebox-static") !== "wordpress-archive" || (await head.text()) !== "") throw new Error("Static HEAD did not preserve headers with an empty body.") + const missing = await request(`${origin}/wp-includes/js/does-not-exist.min.js`) + if (missing.status !== 404) throw new Error(`Missing static archive asset returned ${missing.status}, not 404.`) + const source = await request(`${origin}/wp-includes/version.php`) + if (source.headers.get("x-wp-codebox-static") === "wordpress-archive") throw new Error("Static handler exposed a PHP source file.") +} + async function request(target, options = {}) { const headers = new Headers(options.headers) const requestUrl = new URL(target) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 535fb91a..69233904 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -10,6 +10,7 @@ import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, clo import { routeWorkerRequest } from "../packages/runtime-cloudflare/src/request-routing.js" import { toFetchResponse, toPHPRequest } from "../packages/runtime-cloudflare/src/request-translation.js" import { WordPressStateCoordinator } from "../packages/runtime-cloudflare/src/state-coordinator.js" +import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" const execFileAsync = promisify(execFile) @@ -42,6 +43,34 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) +test("Cloudflare serves only safe browser assets from the WordPress archive", () => { + assert.equal(wordpressStaticArchivePath("/wp-includes/js/jquery/jquery.min.js"), "wordpress/wp-includes/js/jquery/jquery.min.js") + assert.equal(wordpressStaticArchivePath("/wp-admin/css/common.min.css"), "wordpress/wp-admin/css/common.min.css") + assert.equal(wordpressStaticArchivePath("/wp-content/themes/twentytwentyfive/assets/fonts/manrope.woff2"), "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/manrope.woff2") + assert.equal(wordpressStaticArchivePath("/wp-content/themes/twentytwentyfive/style.css"), "wordpress/wp-content/themes/twentytwentyfive/style.css") + assert.equal(wordpressStaticArchivePath("/wp-admin/admin-ajax.php"), null) + assert.equal(wordpressStaticArchivePath("/wp-includes/version.php"), null) + assert.equal(wordpressStaticArchivePath("/wp-content/plugins/example/app.js"), null) + assert.equal(wordpressStaticArchivePath("/wp-includes/%2e%2e/wp-config.php"), null) + assert.equal(wordpressStaticArchivePath("/wp-includes/..%2fwp-config.php"), null) + assert.equal(wordpressStaticContentType("wordpress/wp-includes/js/jquery/jquery.min.js"), "text/javascript; charset=utf-8") + assert.equal(wordpressStaticContentType("wordpress/wp-content/themes/example/assets/font.woff2"), "font/woff2") + assert.equal(wordpressStaticContentType("wordpress/wp-admin/images/logo.png"), "image/png") +}) + +test("WordPress boot corpus excludes browser assets while retaining server metadata", () => { + const paths = new Set() + assert.ok(isWordPressRuntimeFile("wordpress/wp-includes/version.php", paths)) + assert.ok(isWordPressRuntimeFile("wordpress/wp-includes/blocks/paragraph/block.json", paths)) + assert.ok(isWordPressRuntimeFile("wordpress/wp-content/themes/twentytwentyfive/style.css", paths)) + assert.ok(isWordPressRuntimeFile("wordpress/wp-admin/css/view-transitions.min.css", paths)) + assert.ok(isWordPressRuntimeFile("wordpress/wp-includes/js/wp-emoji-loader.min.js", paths)) + assert.ok(isWordPressRuntimeFile("wordpress/wp-admin/images/dashboard-background.svg", paths)) + assert.ok(!isWordPressRuntimeFile("wordpress/wp-includes/js/jquery/jquery.min.js", paths)) + assert.ok(!isWordPressRuntimeFile("wordpress/wp-admin/css/common.min.css", paths)) + assert.ok(!isWordPressRuntimeFile("wordpress/wp-content/themes/twentytwentyfive/assets/fonts/manrope.woff2", paths)) +}) + test("Cloudflare translates Fetch requests and PHP responses without losing browser data", async () => { const headers = new Headers({ "content-type": "application/octet-stream", "x-request-id": "first" }) headers.append("x-request-id", "second") @@ -161,12 +190,22 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /WORDPRESS_AUTH_SECRET/) assert.match(worker, /canonicalWordPressAuthConstants\(env\)/) assert.match(worker, /authConstants/) + assert.match(worker, /const staticResponse = await serveWordPressStaticAsset\(request\)/) + assert.ok(worker.indexOf("const staticResponse = await serveWordPressStaticAsset(request)") < worker.indexOf("const coordinator = env.WORDPRESS_STATE.getByName")) + assert.match(worker, /CONCATENATE_SCRIPTS: false/) + assert.match(worker, /SCRIPT_DEBUG: false/) + assert.match(worker, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL, \(entry: \{ path: Uint8Array \}\) => decoder\.decode\(entry\.path\) === archivePath\)/) + assert.match(worker, /request\.method === "HEAD" \? null : bytes/) + assert.match(worker, /"x-wp-codebox-static": "wordpress-archive"/) + assert.match(worker, /cache\.match\(request\)/) + assert.match(worker, /cache\.put\(request, response\.clone\(\)\)/) assert.match(materializer, /const archivePaths = await wordPressArchivePaths\(decoder\)/) assert.match(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL,\s*\(entry: \{ path: Uint8Array \}\) => isWordPressRuntimeFile\(decoder\.decode\(entry\.path\), archivePaths\)\)/) assert.match(materializer, /return false/) assert.match(corpus, /path\.endsWith\("\.map"\)/) - assert.match(corpus, /path\.startsWith\("wordpress\/wp-content\/themes\/"\)/) - assert.match(corpus, /minifiedSibling/) + assert.match(corpus, /SERVER_READ_EXTENSION/) + assert.match(corpus, /path\.endsWith\("\/style\.css"\)/) + assert.match(corpus, /STATIC_ARCHIVE_ROOTS/) }) test("Cloudflare coordinator serializes leases, promotes with CAS, and recovers stale leases", async () => { diff --git a/tests/cloudflare-wordpress-archive-corpus.test.ts b/tests/cloudflare-wordpress-archive-corpus.test.ts index 00909ae6..33c378ed 100644 --- a/tests/cloudflare-wordpress-archive-corpus.test.ts +++ b/tests/cloudflare-wordpress-archive-corpus.test.ts @@ -4,9 +4,7 @@ import { decodeRemoteZip, type CentralDirectoryEntry } from "@php-wasm/stream-co import { isWordPressRuntimeFile, summarizeWordPressRuntimeCorpus, type WordPressArchiveEntry } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" const WORDPRESS_ARCHIVE_URL = "https://wordpress.org/latest.zip" -// The legacy selection is 85 MB; 64 MiB preserves 13 MiB of growth room while -// remaining safely below the entry Worker's 128 MB memory ceiling. -const MAX_RUNTIME_CORPUS_BYTES = 64 * 1024 * 1024 +const MAX_RUNTIME_CORPUS_BYTES = 32 * 1024 * 1024 test("WordPress production runtime corpus stays within the Worker materialization budget", async () => { const decoder = new TextDecoder() From c00988e342b77533f85a0c0af23458c1b8f585a2 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 01:49:59 -0400 Subject: [PATCH 06/87] Provision the WordPress runtime corpus in R2 --- package.json | 2 + packages/runtime-cloudflare/README.md | 11 +- .../assets/wordpress-runtime-artifact.json | 9065 +++++++++++++++++ .../src/wordpress-runtime-artifact.ts | 98 + packages/runtime-cloudflare/src/worker.ts | 73 +- scripts/cloudflare-local-gate.mjs | 10 + ...ate-cloudflare-wordpress-runtime-corpus.ts | 48 + ...on-cloudflare-wordpress-runtime-corpus.mjs | 24 + tests/cloudflare-runtime.test.ts | 60 +- 9 files changed, 9338 insertions(+), 53 deletions(-) create mode 100644 packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json create mode 100644 packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts create mode 100644 scripts/generate-cloudflare-wordpress-runtime-corpus.ts create mode 100644 scripts/provision-cloudflare-wordpress-runtime-corpus.mjs diff --git a/package.json b/package.json index 7a5098e0..8118527a 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,8 @@ "build": "node ./node_modules/typescript/bin/tsc -b packages/runtime-core packages/runtime-playground packages/cli && node scripts/ensure-cli-bin-executable.mjs", "cloudflare:dry-run": "npm exec -- wrangler deploy --dry-run --config packages/runtime-cloudflare/wrangler.jsonc", "cloudflare:local-gate": "node scripts/cloudflare-local-gate.mjs", + "generate:cloudflare-wordpress-runtime-corpus": "tsx scripts/generate-cloudflare-wordpress-runtime-corpus.ts", + "provision:cloudflare-wordpress-runtime-corpus": "node scripts/provision-cloudflare-wordpress-runtime-corpus.mjs", "prepare": "npm run build", "package:wordpress-plugin": "tsx scripts/build-wordpress-plugin-zip.ts", "release:package": "tsx scripts/package-release-artifact.ts", diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index f73d1191..90696312 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -16,11 +16,14 @@ The Worker forwards browser cookies directly to Playground and disables Playgrou The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c7406f1f1ea6dc3` from [MDI PR #127](https://github.com/Automattic/markdown-database-integration/pull/127). The bundle generator, worker provenance, and source-contract test use the same revision. +The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache. + ## Verification -1. Run `npm run generate:cloudflare-canonical-mdi-seed` to regenerate the deterministic canonical MDI archive and provenance manifest. -2. Run `npm run test:cloudflare-runtime` for routing, canonical-state, generator reproducibility, filtered archive, source contract, and TypeScript coverage. -3. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. -4. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. +1. Run `npm run generate:cloudflare-canonical-mdi-seed` and `npm run generate:cloudflare-wordpress-runtime-corpus` to regenerate deterministic runtime artifacts and manifests. +2. Run `npm run provision:cloudflare-wordpress-runtime-corpus -- --local --persist-to ` to verify and upload the exact content-addressed artifact into isolated local R2 storage. Use `--remote` only for an explicit deployment operation. +3. Run `npm run test:cloudflare-runtime` for routing, canonical-state, artifact validation, source contract, and TypeScript coverage. +4. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. +5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions the artifact before workerd starts, then injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json b/packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json new file mode 100644 index 00000000..a68d037e --- /dev/null +++ b/packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json @@ -0,0 +1,9065 @@ +{ + "schema": "wp-codebox/wordpress-runtime-artifact/v1", + "key": "runtime/wordpress/6fb95a94d5c06de7ddf35aba94cfc663d281ea500488e5c1206ec374f67a2173.zip", + "archive": { + "sha256": "6fb95a94d5c06de7ddf35aba94cfc663d281ea500488e5c1206ec374f67a2173", + "size": 5658241 + }, + "source": { + "url": "https://downloads.wordpress.org/release/wordpress-7.0.2.zip", + "version": "7.0.2", + "identity": "Fri, 17 Jul 2026 18:01:55 GMT" + }, + "files": [ + { + "path": "wordpress/index.php", + "size": 405, + "sha256": "eea9347b1e266ca5407b92633958c148dbfebea307e511a3a226ea61828e2eba" + }, + { + "path": "wordpress/readme.html", + "size": 7406, + "sha256": "491ef4578defac49ddbd8a3df9fa8dfc61bb6d140a7532fd7948b259b07223e2" + }, + { + "path": "wordpress/wp-activate.php", + "size": 7371, + "sha256": "46f31a350c002f6a87e08afe77d27ef7e796d3399919604e870a64f2d73d6e78" + }, + { + "path": "wordpress/wp-admin/about.php", + "size": 17798, + "sha256": "4615817f17d950ef5f1aa9354ce0fdb3f56c6f22b6c9cffce0c53594c2a7d238" + }, + { + "path": "wordpress/wp-admin/admin-ajax.php", + "size": 5146, + "sha256": "06f3a0e4fbeaed4f965f5fe22f3d767faaa0c775d1446975bf420f4b85ba7a01" + }, + { + "path": "wordpress/wp-admin/admin-footer.php", + "size": 2811, + "sha256": "628066f70b9f793839687ad8a35b7d53d42af2c7d6034e0eaf04589c555be9b5" + }, + { + "path": "wordpress/wp-admin/admin-functions.php", + "size": 479, + "sha256": "8724cfb8d5421054342c4bb7493cd880c28334b60c7af8de83955cc1de967405" + }, + { + "path": "wordpress/wp-admin/admin-header.php", + "size": 9291, + "sha256": "6a65ba93b7903dee323bc8ee932eb078c858af3fb42af0833256d11d5f29a84f" + }, + { + "path": "wordpress/wp-admin/admin-post.php", + "size": 2021, + "sha256": "7c0267056bc94e1260b9da6f9d1835010287b9630d474ee29ead14019098f5f0" + }, + { + "path": "wordpress/wp-admin/admin.php", + "size": 12935, + "sha256": "c6f57400d24b7783aadabbf6763d9e8fc677bace3b4b39ad54430576a571082b" + }, + { + "path": "wordpress/wp-admin/async-upload.php", + "size": 5604, + "sha256": "cbf153ab3aec6d4b01cb58828aceec81d7d429df3f6e3367a78bd5c7f1cb9b78" + }, + { + "path": "wordpress/wp-admin/authorize-application.php", + "size": 10335, + "sha256": "62f2052f9126122fc20c0ed7d9a52443675c29973229017c3ed98d943bd489ec" + }, + { + "path": "wordpress/wp-admin/comment.php", + "size": 11643, + "sha256": "398d8ab92093668218a2d389e6b7310e87f59f7ebbd53104e12cdb2b74ba980b" + }, + { + "path": "wordpress/wp-admin/contribute.php", + "size": 6039, + "sha256": "c08629ebbc91c171dd0aa59f3c3a9def5fe31e64d0b14fdb70e8939521cb3396" + }, + { + "path": "wordpress/wp-admin/credits.php", + "size": 4524, + "sha256": "66f466c0b7e2266eed2e7f07934e0ed89935ca83b7537b58c18ccc92f980adee" + }, + { + "path": "wordpress/wp-admin/css/view-transitions.min.css", + "size": 192, + "sha256": "5af068ba94aa90d200974e6d5b5eb0c6a0f20ee2394b24fd9f7a026316971ef6" + }, + { + "path": "wordpress/wp-admin/custom-background.php", + "size": 489, + "sha256": "e3519ce6bdc2cf5107ac042c79324b7f2fa60055a5c31836fc1814a6923ce35d" + }, + { + "path": "wordpress/wp-admin/custom-header.php", + "size": 499, + "sha256": "95e1e3bfc5d81b3401b9a415b3ea306341544b6231bb19e323480113e9b64b0c" + }, + { + "path": "wordpress/wp-admin/customize.php", + "size": 11476, + "sha256": "e52622ab31bfbb938ea0f505597b722b0f1bb9e9fd6d1689a53e88351761d965" + }, + { + "path": "wordpress/wp-admin/edit-comments.php", + "size": 14484, + "sha256": "d07706633c9b47f45a6737486afcd1bf9cb896c3139ea9b6e3a5d4970f236f3c" + }, + { + "path": "wordpress/wp-admin/edit-form-advanced.php", + "size": 29486, + "sha256": "a74058bfbc0768353858872e0a170c34220ca52f9d726f37ecbb325e75b9aab6" + }, + { + "path": "wordpress/wp-admin/edit-form-blocks.php", + "size": 15084, + "sha256": "6c95b99b9a51a864880525493d782ec3803140260f40d3e4430bf0dce3723023" + }, + { + "path": "wordpress/wp-admin/edit-form-comment.php", + "size": 8533, + "sha256": "47c7c2c85b7786236b326157f0f8faa4d3c15228bf5dd5ed0c19e08bf0d24a84" + }, + { + "path": "wordpress/wp-admin/edit-link-form.php", + "size": 6354, + "sha256": "adc77586b65cd1ff66855ac2ec9903cd2d7d4f0c96a181b0d38fd8008827dbbf" + }, + { + "path": "wordpress/wp-admin/edit-tag-form.php", + "size": 10665, + "sha256": "c6048841b3d0c9821abac035046657c18753de62b330dc8f647b81033a83af8d" + }, + { + "path": "wordpress/wp-admin/edit-tags.php", + "size": 22510, + "sha256": "3695a5c6430d6b61982f90e4fb0298a915ed88f93f11fd4f9f1a1cc075da96f8" + }, + { + "path": "wordpress/wp-admin/edit.php", + "size": 19952, + "sha256": "94e2dcd0354ff870a0e991dac19a756fbf9c31268fc2330a1b6cee61cc888586" + }, + { + "path": "wordpress/wp-admin/erase-personal-data.php", + "size": 7505, + "sha256": "1f5cea858775cb85104b2e8e848a828126abedfc5f1da6794b645878af799787" + }, + { + "path": "wordpress/wp-admin/export-personal-data.php", + "size": 7941, + "sha256": "86ef3c768278124b22cfc01441fc6066d0b69a668ecb386765b0a94566933d0e" + }, + { + "path": "wordpress/wp-admin/export.php", + "size": 11265, + "sha256": "956911bb9f675c93be60439b84490be4b67571eedecf4224bcfe064c8340b343" + }, + { + "path": "wordpress/wp-admin/font-library.php", + "size": 1036, + "sha256": "654c94f2c59f004b6d0046bf4e077e139c9922bdae19f8cced209132648ca974" + }, + { + "path": "wordpress/wp-admin/freedoms.php", + "size": 4957, + "sha256": "9bc6b8f0c2236bfd04d049e32c676135331f1c9d87c58c0c9fa3b417f6f841fb" + }, + { + "path": "wordpress/wp-admin/images/dashboard-background.svg", + "size": 3344, + "sha256": "eb55d8055063f46e176b6780b55e8d98b999b9ad3b85fbca55c64b0221480546" + }, + { + "path": "wordpress/wp-admin/import.php", + "size": 7766, + "sha256": "7289a8b3d4cef86d5e7a9b53f25eceb27c78c029091c77016c704b444e47b220" + }, + { + "path": "wordpress/wp-admin/includes/admin-filters.php", + "size": 8034, + "sha256": "cdf2b61a87f8a1e59eca31a7f528178eb8391c3532678594fb3737036ad9320b" + }, + { + "path": "wordpress/wp-admin/includes/admin.php", + "size": 3628, + "sha256": "b009ef8cc36ff1a4ed9b38da26792acdea1c3af8d623d0e1fe0d2f21ef786d9c" + }, + { + "path": "wordpress/wp-admin/includes/ajax-actions.php", + "size": 152775, + "sha256": "7fd86b5b8a6e1a72ac0440dddc26be9b800fd1cb258f7fb9cf23f6af3999f69b" + }, + { + "path": "wordpress/wp-admin/includes/bookmark.php", + "size": 11678, + "sha256": "c27da3f111ac1597bf3fb13ca591219e58cf55d6245ad3e1945f526e9df24ef5" + }, + { + "path": "wordpress/wp-admin/includes/class-automatic-upgrader-skin.php", + "size": 3663, + "sha256": "b25a7bc5140770327c572c98b7d08c913733011ec8bf33209ef4c42ebde75ec2" + }, + { + "path": "wordpress/wp-admin/includes/class-bulk-plugin-upgrader-skin.php", + "size": 2590, + "sha256": "bbc5e11ed01c12421ef923c0f38381a6033f89b08357fefaff57e81e982e9961" + }, + { + "path": "wordpress/wp-admin/includes/class-bulk-theme-upgrader-skin.php", + "size": 2660, + "sha256": "4762ff150f6fbd19446da617ff9867a4836afa201b6961eed95c35400c5fd59f" + }, + { + "path": "wordpress/wp-admin/includes/class-bulk-upgrader-skin.php", + "size": 6661, + "sha256": "c5036a7133de0d0b3c096e46a383466fe507642dea423122857e0f426bcdc82b" + }, + { + "path": "wordpress/wp-admin/includes/class-core-upgrader.php", + "size": 15198, + "sha256": "17688da92237c5838e5b0f3a07c17c3bf80ee6c828cd5988faf205d90550fa24" + }, + { + "path": "wordpress/wp-admin/includes/class-custom-background.php", + "size": 21706, + "sha256": "355dbcee6dc4b739cd7810ab30220b5a86a6fa4875a6708515bfe906b59a9238" + }, + { + "path": "wordpress/wp-admin/includes/class-custom-image-header.php", + "size": 49183, + "sha256": "98e05642205b44b8c4cb6098852f2cc2ece4e4fc0ac2deadb77f5b9079f0f20d" + }, + { + "path": "wordpress/wp-admin/includes/class-file-upload-upgrader.php", + "size": 4163, + "sha256": "4bcfc51f1b1bb5453c431cc90b72a9d9822f46fa16f587660baeec66a469f0b4" + }, + { + "path": "wordpress/wp-admin/includes/class-ftp-pure.php", + "size": 5426, + "sha256": "5a2ed5e323983d35c3e16a56a487e98a7ccf12be8d473e08b7e0e26c0ccc6c92" + }, + { + "path": "wordpress/wp-admin/includes/class-ftp-sockets.php", + "size": 8479, + "sha256": "7bec44592cfce0cdffcf0ce3d2d76f29666bf2e1bb81bfce10aa6df38b3b22f7" + }, + { + "path": "wordpress/wp-admin/includes/class-ftp.php", + "size": 27343, + "sha256": "56d7c32f2bc402a50571f6b4acb637868c8db8f56c8c5abe2992737695651ef5" + }, + { + "path": "wordpress/wp-admin/includes/class-language-pack-upgrader-skin.php", + "size": 2870, + "sha256": "7d78463fb9947096703f0c0ea78e72b5578f2be664b9c4c4c420776fb648e037" + }, + { + "path": "wordpress/wp-admin/includes/class-language-pack-upgrader.php", + "size": 15528, + "sha256": "64039e68db3ca1e4bea9cedb1f02fea9f6880a957812051ce0c05fca6029772c" + }, + { + "path": "wordpress/wp-admin/includes/class-pclzip.php", + "size": 196695, + "sha256": "74e844d42c1cbe89b43e9df36453f82632c2eaa800dcf17fc53a7822e808cbc4" + }, + { + "path": "wordpress/wp-admin/includes/class-plugin-installer-skin.php", + "size": 11947, + "sha256": "d9070f5ff52876045ddc8606565073e3737f47ba0235f80f7c8ce17aba67e685" + }, + { + "path": "wordpress/wp-admin/includes/class-plugin-upgrader-skin.php", + "size": 3278, + "sha256": "9395981b15edb1bbc67059d218eb5febcfc5b26d78a17ae5499ac476ceaa5318" + }, + { + "path": "wordpress/wp-admin/includes/class-plugin-upgrader.php", + "size": 23249, + "sha256": "fd6d813719a3533b1f61ef872dd4a793c86fdaf691aa4556b88ec1b5af953ed7" + }, + { + "path": "wordpress/wp-admin/includes/class-theme-installer-skin.php", + "size": 12974, + "sha256": "00c5b15179fb5a0b671334c77383f67611f61529fd0ee150d007e3f0a5dc2040" + }, + { + "path": "wordpress/wp-admin/includes/class-theme-upgrader-skin.php", + "size": 4176, + "sha256": "2546bed7ef09399b7bbd0149242e6ec7870b07c677e98730c0753561ed1d3f72" + }, + { + "path": "wordpress/wp-admin/includes/class-theme-upgrader.php", + "size": 26783, + "sha256": "9457098f5de802d2af05f53539ce69145292a4b937c57dea7599a53c5a4bcca3" + }, + { + "path": "wordpress/wp-admin/includes/class-walker-category-checklist.php", + "size": 5092, + "sha256": "6869facbdcc4025538275f43602d01bd9754e2d0e0af19e62ec788781f48e318" + }, + { + "path": "wordpress/wp-admin/includes/class-walker-nav-menu-checklist.php", + "size": 5781, + "sha256": "331b4da6d5c3b9082c6c806f0b3977a1a0e99f2004532eccf297ae609ec2815e" + }, + { + "path": "wordpress/wp-admin/includes/class-walker-nav-menu-edit.php", + "size": 14298, + "sha256": "e9f9954eb697a739a368f09dab6490024a80a063212876b8984cc3d86ab61e48" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-ajax-upgrader-skin.php", + "size": 4193, + "sha256": "f9c0ac547d59a8b53f8137216009708264db0e9ef7eedf9b01590283becefebd" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-application-passwords-list-table.php", + "size": 6949, + "sha256": "86c23e64d85ac1cf251e191d5d09bc51fcc00cd4061e5e8c2f72acfeecf384d7" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-automatic-updater.php", + "size": 61902, + "sha256": "aabd404cddf78561888b57a22c53ae0840104ece2c8dc5b7e7bf33079b28b255" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-comments-list-table.php", + "size": 34627, + "sha256": "a6d2635043d6cec0b26907681993e089e1fc8be193f1baf8091d7c0366109f18" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-community-events.php", + "size": 18647, + "sha256": "9dad557223654f506c725ec2aef9bbbd13a4347ef74e302c0dc3fd7897929661" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-debug-data.php", + "size": 71960, + "sha256": "fbe505b9d8012a72c62abb3ac5b2b99cb6c6ac1fb4b09c32431ab1a7817a3f54" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-filesystem-base.php", + "size": 24410, + "sha256": "28ada0d3504f25cdd28ec4ba167a820d1a1ac583e3ef894674c718e6238c85c9" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-filesystem-direct.php", + "size": 18607, + "sha256": "de772536fc842ad5a57bdc8fc3e2bee709e8a57a45584f42c1137111e011d812" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-filesystem-ftpext.php", + "size": 23273, + "sha256": "b4813d5e02ced5341af60024c1bfec0891b8952f75cd83f3db0aaeef2f22fb8d" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-filesystem-ftpsockets.php", + "size": 18497, + "sha256": "89804413af1be9d530fe2fca517007999f409fc16ad05f24611c6e4c8e30ba67" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-filesystem-ssh2.php", + "size": 23390, + "sha256": "9e1be9b2afbf1011be0545fd528bba678c384b9c12de64cc09b690786c373c85" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-importer.php", + "size": 7823, + "sha256": "48c715f4447f209b1cde07bb6cf0686779fb8a0a02302e58198179f130bbcbcb" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-internal-pointers.php", + "size": 4593, + "sha256": "d97f51774dc08705066a88b3cdb18d091ea05e468496073935ec8a48c5bbc6b1" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-links-list-table.php", + "size": 9528, + "sha256": "9d6518bb71542c39c172e8eab0f7480339e50df09f88ebf7b7ae199e69cd0bb0" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-list-table-compat.php", + "size": 1497, + "sha256": "db2b1abcd67e7101ab1d8f38c90a93a96919de7679f83e7d1203c332f1f73037" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-list-table.php", + "size": 53109, + "sha256": "a7849864ccf1b142008b6a3f39f7b3477082ae8410c355cdf53968c002ac7743" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-media-list-table.php", + "size": 27080, + "sha256": "e480fa867d3a6b63c5e9bb973f860707d7f9f69b1a6bc7a3803f1a61d7c14763" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-ms-sites-list-table.php", + "size": 22765, + "sha256": "faeb91ffc791c04383fda916750e6429be2239a74634c2307bfb959026f7c993" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-ms-themes-list-table.php", + "size": 30227, + "sha256": "06735eb97e2d9323f6af48141f8a2239d584b6ecda8e86b9f6ba34a1036da5a2" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-ms-users-list-table.php", + "size": 15691, + "sha256": "1f1ce7203825e94ec048e40bc1d30524060dd129092cf0283104022ba8e32a4d" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-plugin-install-list-table.php", + "size": 24974, + "sha256": "9975ead90e1254cb33911722b9781619ee113a22af804eaea95cfe80d84531bf" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-plugins-list-table.php", + "size": 58107, + "sha256": "0d4887b8c6b27d58346ade915f4517033892f8ed8d5d9d65cf227c10cd28faf7" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-post-comments-list-table.php", + "size": 1453, + "sha256": "2ceb39e5b22f2cca85c31387c602373bc2726748707d7bdfd3a53a142293ba39" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-posts-list-table.php", + "size": 64997, + "sha256": "3ab2092e8648c5bdb50a7b4f40792eaf9da9979edb800ee99808c9d98c3cc2a2" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-privacy-data-export-requests-list-table.php", + "size": 5563, + "sha256": "d8a237f0abbbfb56064cb58aef63bffa6438c6162c0a4f6e7f381ecf0ba490c0" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-privacy-data-removal-requests-list-table.php", + "size": 5715, + "sha256": "9fb9444099ee4a81d355480b6f6f1a40034a9f709021e611252e4230961c4ead" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-privacy-policy-content.php", + "size": 32665, + "sha256": "e184f80b01d796aaa8f2bcef6dce338e166aed1e31ee09efa09da59940176fd5" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-privacy-requests-table.php", + "size": 14791, + "sha256": "0ef4318ef1c1b06cd36d64935089aad486853d1f3989e4f753e106568931a5cd" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-screen.php", + "size": 37470, + "sha256": "e72da44c6a424b080f11d5d8a5dc0ef5e2f80561526a87fe92d7e19e1f2eebbc" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-site-health-auto-updates.php", + "size": 14337, + "sha256": "21041d6b1e9c7fbb1fd2b251522317dbfacb6d023852619587437cab6ed94bbf" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-site-health.php", + "size": 131241, + "sha256": "dc3d56310118a8936e2564b826524c0dc9d85bdf67dee778b7a3476dfd09d5d6" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-site-icon.php", + "size": 6414, + "sha256": "4d7501d89dc606d406849783bcccf7ff128511d854f2c7c600570812e18247bb" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-terms-list-table.php", + "size": 21071, + "sha256": "eff4371cf63e2993b98ae559e5637c8f023f4e4af49231f28bac270136c99f4a" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-theme-install-list-table.php", + "size": 15703, + "sha256": "2cd0d9d48cf76ec73be66adc0ec5469c469e22c8dd458b6f1f3bb087a9886d57" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-themes-list-table.php", + "size": 10339, + "sha256": "a1648925867e2bebadcab30f9da3ec3eb9d331956edab9d7bdf5c102c42ad8f7" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-upgrader-skin.php", + "size": 7064, + "sha256": "a8219a3e0b89519f02206d0da9fa664fdd99837db8f46aa9baa90228bca4f86b" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-upgrader-skins.php", + "size": 1477, + "sha256": "23bc58067b6e0ff1c6557ef1fd52618d4619593713bc0213a0388ece9d21b02a" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-upgrader.php", + "size": 48366, + "sha256": "510128b556bec5f58a504360f654b2b260b7608f7a99f9b42243e983d3116477" + }, + { + "path": "wordpress/wp-admin/includes/class-wp-users-list-table.php", + "size": 19004, + "sha256": "a497f65a4dad9fa8b8687c6eff86b52ffce234b115bff3e2c98334c310dffcbf" + }, + { + "path": "wordpress/wp-admin/includes/comment.php", + "size": 6231, + "sha256": "a283931fdf1bd02ceebcc21658b7d3919c6697d11eef452504d2a09bbf000d49" + }, + { + "path": "wordpress/wp-admin/includes/continents-cities.php", + "size": 20540, + "sha256": "f5e6fd27327b3c2da44f29c892aa3cd8f0ef6083aa409dd04839056f73f5bd9b" + }, + { + "path": "wordpress/wp-admin/includes/credits.php", + "size": 5832, + "sha256": "cae64242dc73636c2636b382858de8dc568a9ee031373f490d841047375a7cbd" + }, + { + "path": "wordpress/wp-admin/includes/dashboard.php", + "size": 70383, + "sha256": "23839c1486756f45b894c2ed6dd711c97914f7220aada219e13f86de45d4f1a0" + }, + { + "path": "wordpress/wp-admin/includes/deprecated.php", + "size": 41753, + "sha256": "8fe6484f383961aaa7e67be101ab03853cd1a5e53b0b680df654d5afd1f44ab3" + }, + { + "path": "wordpress/wp-admin/includes/edit-tag-messages.php", + "size": 1478, + "sha256": "b30dd750003aa7e8b9cbab37e04cd2dccf380a8388853f2493f617b4bc0d4eb2" + }, + { + "path": "wordpress/wp-admin/includes/export.php", + "size": 25976, + "sha256": "897111daa379171a5210e21a055a50841eb3884e99508367f3673f33f5a5a18a" + }, + { + "path": "wordpress/wp-admin/includes/file.php", + "size": 97913, + "sha256": "8bb99b94ad43d996b9f3b39e1ec4778d7e7baca3de764a29c99177e3c89ec0d8" + }, + { + "path": "wordpress/wp-admin/includes/image-edit.php", + "size": 43994, + "sha256": "6a3c6e1795f22a37f410c2277086cde23fad5538e2577308db2870bad182276c" + }, + { + "path": "wordpress/wp-admin/includes/image.php", + "size": 45171, + "sha256": "54ea3386fe9ecbefc2e88dc4e95d4de35f2a5a4109e2676bf9538c057a83e16d" + }, + { + "path": "wordpress/wp-admin/includes/import.php", + "size": 6617, + "sha256": "47403e589b5ced97f9f94398142e52b4639f51865f2bc517ab9f43b0ccc73a4d" + }, + { + "path": "wordpress/wp-admin/includes/list-table.php", + "size": 3802, + "sha256": "b6178f3ef28bf65bdefe4a74fff706634da99a21b7edd9908f8ec7b2625fc09d" + }, + { + "path": "wordpress/wp-admin/includes/media.php", + "size": 119948, + "sha256": "9bbe54a96d9c62e50edb13ccd7215db78cac215b9907aaa024cf40c85749fa4d" + }, + { + "path": "wordpress/wp-admin/includes/menu.php", + "size": 9640, + "sha256": "de7a1b71f854ea53d2f48bb36eb69a0b55aff170d9f4975592fc1a9e1fdf6e43" + }, + { + "path": "wordpress/wp-admin/includes/meta-boxes.php", + "size": 66857, + "sha256": "89a875d249761781410f623c67f3af1b3b7170bd6081586a5a2463aeb31f9c91" + }, + { + "path": "wordpress/wp-admin/includes/misc.php", + "size": 46443, + "sha256": "7937d8d6d77da0fbdf761176592501c8663e41a44b70f35a801355153ccbeaea" + }, + { + "path": "wordpress/wp-admin/includes/ms-admin-filters.php", + "size": 1296, + "sha256": "078c72a4348b7b4b33ab7240fcb38dd35e1a9ba257c1993e864fc59095f5a268" + }, + { + "path": "wordpress/wp-admin/includes/ms-deprecated.php", + "size": 3770, + "sha256": "7e262824ff2ee42c157db57dbdc168e56fb59498402e8cb10b334471d5aea019" + }, + { + "path": "wordpress/wp-admin/includes/ms.php", + "size": 34252, + "sha256": "811d6c42bb3d52d9f9e6638430c8d9a6c92cd3bf976279b52dcde9611f6df19c" + }, + { + "path": "wordpress/wp-admin/includes/nav-menu.php", + "size": 49127, + "sha256": "a2327a16958cc8b090753480879c3323ea0c54fad9ebf983badf86a3316825ae" + }, + { + "path": "wordpress/wp-admin/includes/network.php", + "size": 27038, + "sha256": "536192345e6be7eacbcb0afa4216e7728c9ecc5de2011489abf1c7860759d601" + }, + { + "path": "wordpress/wp-admin/includes/noop.php", + "size": 1148, + "sha256": "2cee6aee18eb8d90cd702f6795c17cf037d6611173df1222162cb1709ed800da" + }, + { + "path": "wordpress/wp-admin/includes/options.php", + "size": 4244, + "sha256": "636298a08e143f8d1f7f2bdab0ac7398ea9144935c3e8f5a597381226304d44d" + }, + { + "path": "wordpress/wp-admin/includes/plugin-install.php", + "size": 39160, + "sha256": "187ef612c137b2dbf53fdaa464f024014f3faffa7c142549df66ced16a876423" + }, + { + "path": "wordpress/wp-admin/includes/plugin.php", + "size": 93281, + "sha256": "794037bced480bf6653098e094b76729d0cc8ba1bca1d6a756f6f32c9864c007" + }, + { + "path": "wordpress/wp-admin/includes/post.php", + "size": 82260, + "sha256": "77eb614b1abfad7d0840ae7229684cd108dd8dd9a55ac119d64a242b49d86667" + }, + { + "path": "wordpress/wp-admin/includes/privacy-tools.php", + "size": 33441, + "sha256": "3a2ee04b7bf10a5a87c2af9db9052240ac6c9e8829e580cacab22728f2124ebb" + }, + { + "path": "wordpress/wp-admin/includes/revision.php", + "size": 16629, + "sha256": "d912a6e6016be96defe318417925a8cbaeccfdae6d73c9555967fd8be1ac9b75" + }, + { + "path": "wordpress/wp-admin/includes/schema.php", + "size": 45583, + "sha256": "a446ddd76ce401fc8173bfee4e79a98f8c3567ed3ba7560c2f167ca85e6976b0" + }, + { + "path": "wordpress/wp-admin/includes/screen.php", + "size": 6390, + "sha256": "5eb0ace1297a8eb8180a21abdc850b91bba9e8ea71925c6767f0e577c9697908" + }, + { + "path": "wordpress/wp-admin/includes/taxonomy.php", + "size": 8483, + "sha256": "f07896e388b61b1924de65bd1a3ea225357248d471e933fa2fc0acfddf6eaa93" + }, + { + "path": "wordpress/wp-admin/includes/template.php", + "size": 99684, + "sha256": "b3abbedf693a7237057d94178c7a4730ac152ba6ebcd2c238f027e76a07cf33a" + }, + { + "path": "wordpress/wp-admin/includes/theme-install.php", + "size": 6990, + "sha256": "277d525ca451a684831e1c99442dc91e46c31906c2f203ee8a211453720a2180" + }, + { + "path": "wordpress/wp-admin/includes/theme.php", + "size": 47533, + "sha256": "797341ef8ddb18372f11112bf1949e2b7e3fe4224681b1bcbcad22630dcfcb1c" + }, + { + "path": "wordpress/wp-admin/includes/translation-install.php", + "size": 11075, + "sha256": "5f43979a5039188566646f71198245d57b98d501e8f2bf39bbc73b43dd285678" + }, + { + "path": "wordpress/wp-admin/includes/update-core.php", + "size": 72913, + "sha256": "3a08d86dd1a4029daea47f5c50920300eb519ff44ca26b9a17cc62c21b6ba559" + }, + { + "path": "wordpress/wp-admin/includes/update.php", + "size": 34844, + "sha256": "4b283793019da3561e3070a171715b3eb85b20ef1bae188faff5d544f74170cc" + }, + { + "path": "wordpress/wp-admin/includes/upgrade.php", + "size": 116693, + "sha256": "45073fd59f493b42efe7a08e25a32c523bdad7169dc6da3b1e1224661d781878" + }, + { + "path": "wordpress/wp-admin/includes/user.php", + "size": 23950, + "sha256": "b2e73ea7a3b2ea7f85a2c9749bd0f56f8dff46d9a2683b48c3c554907e09eb28" + }, + { + "path": "wordpress/wp-admin/includes/widgets.php", + "size": 10546, + "sha256": "b1eed340b1b1aa691c11ee0c4de7682e70a571aa448397f66ea2bc4199d4069c" + }, + { + "path": "wordpress/wp-admin/index.php", + "size": 7864, + "sha256": "28df862f5e409cdfab85c888f21c4c6d67a3ddd8c08e44830dd244116c758806" + }, + { + "path": "wordpress/wp-admin/install-helper.php", + "size": 6961, + "sha256": "2667d0f4ed343d8f5f12d7bdf158bc0c33795530201db703407b1435c98c0320" + }, + { + "path": "wordpress/wp-admin/install.php", + "size": 18337, + "sha256": "0870081ba09fdd86122ddb5afbcca4364c954614ee8d9d07058eabe4e1d8eda3" + }, + { + "path": "wordpress/wp-admin/link-add.php", + "size": 934, + "sha256": "7560ee63efa4b64a7798c65b4d75a80f04b05c72c569f5a59a6a98bed6936166" + }, + { + "path": "wordpress/wp-admin/link-manager.php", + "size": 4361, + "sha256": "6d1abe7515d308222f1b1ecd7289d02890c3e86a0edfb4870b8338372f550cd1" + }, + { + "path": "wordpress/wp-admin/link-parse-opml.php", + "size": 2698, + "sha256": "68730260d1c58dbd1245aaf9ee599c79e3b8eaea3f23de6538beaec976049be2" + }, + { + "path": "wordpress/wp-admin/link.php", + "size": 2957, + "sha256": "4c7dddd928e82a4ede850a26b2007819a07da4a4ecd5d1329c4dd7ad4934a321" + }, + { + "path": "wordpress/wp-admin/load-scripts.php", + "size": 2070, + "sha256": "29655f9e545cca5dc29cd3cfe42ed29f9f0e34a5f07abb2411eadccc57e10241" + }, + { + "path": "wordpress/wp-admin/load-styles.php", + "size": 2995, + "sha256": "c2776a9cd9e5ce8e94616847a067f1621503f84f8aa6a91dba31fd3d0337fce5" + }, + { + "path": "wordpress/wp-admin/maint/repair.php", + "size": 7647, + "sha256": "fcc089befe07191a2afc6da6248e581f21f4880ed386efafc3cff940c480ccb3" + }, + { + "path": "wordpress/wp-admin/media-new.php", + "size": 3249, + "sha256": "ab8d3de696f27311844c81617c28097f9e0fe9a86fdc943ec01ffd80dc7d0cec" + }, + { + "path": "wordpress/wp-admin/media-upload.php", + "size": 3668, + "sha256": "a3b42daf318b840b1f9f615e1cab71caba078529a838a07be264102b2173d0e3" + }, + { + "path": "wordpress/wp-admin/media.php", + "size": 819, + "sha256": "f6fcf282df3a7143660c99d77e8ab2d0f011ca7c35534e1fb160cdde74d37cfd" + }, + { + "path": "wordpress/wp-admin/menu-header.php", + "size": 10059, + "sha256": "783c3e8cb25cbb251a852d45498a24c2c517bf8dc173ee5f7003b625e567e1f0" + }, + { + "path": "wordpress/wp-admin/menu.php", + "size": 18141, + "sha256": "cdeb7558ee20f9a06848cfb242a6efacc5c63e577b0981654c2d8370db0dbf9c" + }, + { + "path": "wordpress/wp-admin/moderation.php", + "size": 307, + "sha256": "1ce9a072818f7e66797f625da00fe6c236828f7f0663c5c9c33c7c2a7a7a38a9" + }, + { + "path": "wordpress/wp-admin/ms-admin.php", + "size": 196, + "sha256": "618457c2f674b650f61a0721b741fa68bb097c738dea9ec72bf662a1be09db12" + }, + { + "path": "wordpress/wp-admin/ms-delete-site.php", + "size": 4613, + "sha256": "6465c535277c726fbfb7d5772eee3b9691ccf03c557e534c3f3ec1f667397f69" + }, + { + "path": "wordpress/wp-admin/ms-edit.php", + "size": 216, + "sha256": "e5d26edf1e869711c7a10128e3a40a44c53c1a6a0a7e1558a20d3f6172ff5fc0" + }, + { + "path": "wordpress/wp-admin/ms-options.php", + "size": 229, + "sha256": "d80de481a624afc27010a0acbe0e58f43b8e53efda1ef7e1c31385e64248975b" + }, + { + "path": "wordpress/wp-admin/ms-sites.php", + "size": 215, + "sha256": "b912160e6e4c2cfba4833ed69e9df46d3bf2f64cbec7b1ae8cd8ff84c8cc8f82" + }, + { + "path": "wordpress/wp-admin/ms-themes.php", + "size": 217, + "sha256": "ff6868a45aa301e011e5f2f984bd7ee89ca982aa22a40a88750c952f048b7751" + }, + { + "path": "wordpress/wp-admin/ms-upgrade-network.php", + "size": 219, + "sha256": "8deb9cd3e12b8ed451da5e72d3913a41398b173366cc7a3d7d9eb4cb4f65e608" + }, + { + "path": "wordpress/wp-admin/ms-users.php", + "size": 215, + "sha256": "003e73c9c3f65667cf2182ef0e106fc137625f8ff888661a92156d2e9a76f7f1" + }, + { + "path": "wordpress/wp-admin/my-sites.php", + "size": 4831, + "sha256": "56284c91e6434bba1d1b9166a9f356b512a5b4acac25a4c39100989d31ba2293" + }, + { + "path": "wordpress/wp-admin/nav-menus.php", + "size": 50251, + "sha256": "b55eb676b59789057f867df6647ee93097f114065e05b12b7e55ce3b6016d61b" + }, + { + "path": "wordpress/wp-admin/network.php", + "size": 5522, + "sha256": "3ba061afa2ee4ad4dae933e68090b3254d8076cea89f2b85eb99defb0cdd716a" + }, + { + "path": "wordpress/wp-admin/network/about.php", + "size": 245, + "sha256": "8c910c455c5aa493055cad34c32e7dc0039d12bda20ecde29ef032d977490b97" + }, + { + "path": "wordpress/wp-admin/network/admin.php", + "size": 1024, + "sha256": "06624070a3774e3d93477c324cccb6521958d25b949480f8fc7122d3883136ec" + }, + { + "path": "wordpress/wp-admin/network/contribute.php", + "size": 255, + "sha256": "4f0c2e50f6835f9c08ea8776cd1e4f26809d87e5b55dcd6d2fbfe6aea44ea6be" + }, + { + "path": "wordpress/wp-admin/network/credits.php", + "size": 249, + "sha256": "7a59596b3c3a958ce190163390b66dd753e6849774e98463a12d0c115aa9efbd" + }, + { + "path": "wordpress/wp-admin/network/edit.php", + "size": 878, + "sha256": "148ec46a813cb34e6d1a87d91ef058b55f1358d3d06840a6f41b5912f9115827" + }, + { + "path": "wordpress/wp-admin/network/freedoms.php", + "size": 251, + "sha256": "d78679aedd13bef3f8b0474a22c1d2b0ca0d184b9e9d04cb25c35400669e9de8" + }, + { + "path": "wordpress/wp-admin/network/index.php", + "size": 2911, + "sha256": "ad5955b06470fb2042e28efa6c49b5981652995bde12104156da7e312b35cbc0" + }, + { + "path": "wordpress/wp-admin/network/menu.php", + "size": 4798, + "sha256": "6eaa963411974757bc6026a880f75b8b1030ea99bd99148adbf01e5684f2563f" + }, + { + "path": "wordpress/wp-admin/network/plugin-editor.php", + "size": 266, + "sha256": "d05985516efc3b68fe1cca6790f4894f4d69f04ab66196d11c6314db35d46da8" + }, + { + "path": "wordpress/wp-admin/network/plugin-install.php", + "size": 377, + "sha256": "8c5442eeefff8dcb3baed97c9ba40a8fe1a2346c50fbbe0bcab38274ce0ed500" + }, + { + "path": "wordpress/wp-admin/network/plugins.php", + "size": 249, + "sha256": "c5a7ed2efc5e80f45cf2ead4c22743a89520799063ff1b42695734a4bb1bd2fe" + }, + { + "path": "wordpress/wp-admin/network/privacy.php", + "size": 249, + "sha256": "2bf098f71423350f22638e702c89feed1008d7aea1237754e2beae9b52bd8eca" + }, + { + "path": "wordpress/wp-admin/network/profile.php", + "size": 254, + "sha256": "09655dcee202c63de1a50b775f7a3795db2179071d5e12e4740db8714ac88350" + }, + { + "path": "wordpress/wp-admin/network/settings.php", + "size": 22027, + "sha256": "4edcff1012aca45c16a456d68936ae14af3e8293dc92e23f2031b7061a01b26e" + }, + { + "path": "wordpress/wp-admin/network/setup.php", + "size": 247, + "sha256": "b8f346abe6de644f5f2bb6188448840aa4958f181a90ed4ce6e77064a607f06b" + }, + { + "path": "wordpress/wp-admin/network/site-info.php", + "size": 7789, + "sha256": "2c4ba59fba18606e3f60f59156ca5159804992c1fed9f8d41c89704a4b98e025" + }, + { + "path": "wordpress/wp-admin/network/site-new.php", + "size": 9735, + "sha256": "9f33d3585292e6e86addb43f808b6ab4b4d15602281939b343b26acef62be01c" + }, + { + "path": "wordpress/wp-admin/network/site-settings.php", + "size": 5983, + "sha256": "5ee631a90beb8ebdbfb92897c52f1e1f26393a7466634bf8f8ad9984fb720c8e" + }, + { + "path": "wordpress/wp-admin/network/site-themes.php", + "size": 6850, + "sha256": "2aacaa851da793948006d31998e818d730617b23c13dd5a521b1fc589f9f1607" + }, + { + "path": "wordpress/wp-admin/network/site-users.php", + "size": 11787, + "sha256": "c9b7835728b481c8367e0e54ffd042e37c64e3bacf1ab4a620624ed09c770001" + }, + { + "path": "wordpress/wp-admin/network/sites.php", + "size": 14588, + "sha256": "3cf00b8c1202275f47c1b0a1195f4145e6d4c9a864502b23ed3d5a3c3f846979" + }, + { + "path": "wordpress/wp-admin/network/theme-editor.php", + "size": 264, + "sha256": "e909b8bd10ef0151649851c9731bc753aadbdab2551dd0e9d4cff6ad287c560e" + }, + { + "path": "wordpress/wp-admin/network/theme-install.php", + "size": 374, + "sha256": "d7ddb9aee76c5f9f0044ca36a6303eb576d06fad44683e39ad317b49c5ac44f6" + }, + { + "path": "wordpress/wp-admin/network/themes.php", + "size": 15964, + "sha256": "d6d88d324ddd04b6724a76b238a06e9b22fd6cd482b089ca01bc96525b317a85" + }, + { + "path": "wordpress/wp-admin/network/update-core.php", + "size": 253, + "sha256": "a4f2dc94deabb61bce2fd6bb9885f1e9a6cd64fadcdff6cbc88b12948a41ed3b" + }, + { + "path": "wordpress/wp-admin/network/update.php", + "size": 450, + "sha256": "0f60541cb4dd19dec07163d56c0c7073bcdbff72d22c327cb85e406996885a0b" + }, + { + "path": "wordpress/wp-admin/network/upgrade.php", + "size": 4895, + "sha256": "17dbd8c53273e38c1fe6a7850c3a4a9b7e3aa4a4cf9bd7b452215eae2e4e4d3f" + }, + { + "path": "wordpress/wp-admin/network/user-edit.php", + "size": 253, + "sha256": "47e0065e0b9bd82fbe2186a92e9709464feb02ad6b6c45d7bf0ec271ed7c7ddf" + }, + { + "path": "wordpress/wp-admin/network/user-new.php", + "size": 5228, + "sha256": "887d31ca59eb5df736109df82297d04ea1dc04ecc4714d9a458dc65b369ff020" + }, + { + "path": "wordpress/wp-admin/network/users.php", + "size": 10595, + "sha256": "333f07ab085a2a424e24e56887e86acf179f8690c6dfe90228753af2bb9497c1" + }, + { + "path": "wordpress/wp-admin/options-connectors.php", + "size": 1096, + "sha256": "ee00ba8cfc7abb9c2bee3b19c13ea681b2a7105486d1a2348e17a6f3a200cfbb" + }, + { + "path": "wordpress/wp-admin/options-discussion.php", + "size": 16297, + "sha256": "03badc01f3001675f092693909e9818b330ed48f62e2f5465b29a87ee0ffb2fd" + }, + { + "path": "wordpress/wp-admin/options-general.php", + "size": 22851, + "sha256": "7ceb1921d075f0f9ca54b83cdc2c0f5e38381efb89ee6a0e2fc38cf2c491f39e" + }, + { + "path": "wordpress/wp-admin/options-head.php", + "size": 621, + "sha256": "9a8f14a2e9cf74683d6611ea9c4238b440bb5da128b9ebec9c35f7e0a586f24f" + }, + { + "path": "wordpress/wp-admin/options-media.php", + "size": 6530, + "sha256": "32cccd536c77332964e488fe0c5fb0fe5109bcdedbec2bfc9d7dff89f3fc3f82" + }, + { + "path": "wordpress/wp-admin/options-permalink.php", + "size": 22416, + "sha256": "8d650474c8c1019d41e8731212afb30b4ede34c764c6a0042ac6ef699d811240" + }, + { + "path": "wordpress/wp-admin/options-privacy.php", + "size": 10163, + "sha256": "98fbc19d9076cfffe94c08a4a49b51078ea7077f5d699281a869205c414da061" + }, + { + "path": "wordpress/wp-admin/options-reading.php", + "size": 10213, + "sha256": "2ae2d39edecec248bb7e73f4339045c6d60bdc8be50f4946136f37393fd5b7e1" + }, + { + "path": "wordpress/wp-admin/options-writing.php", + "size": 9323, + "sha256": "4fb90ba1c1e12da5826c5cfb6708230499dffaee8d2f25390c2623209d4475f8" + }, + { + "path": "wordpress/wp-admin/options.php", + "size": 14266, + "sha256": "be1c998d13f04a61cb3b27a9d2614ca2e22d0a65da06b893e977f7fe6e92ab8f" + }, + { + "path": "wordpress/wp-admin/plugin-editor.php", + "size": 14082, + "sha256": "1998c1480a28fcb2bbdcc29bfdcbde9579dc0e74c9ec0274e248a25fe210affa" + }, + { + "path": "wordpress/wp-admin/plugin-install.php", + "size": 7124, + "sha256": "7c528488c45e2aab5dd46c9abae510a27e545b81168d471227072d19e79c29e5" + }, + { + "path": "wordpress/wp-admin/plugins.php", + "size": 30724, + "sha256": "39f237dda3e826ee702fe361f9713eeacad360c97c3f1bf2d40450e91bd2e714" + }, + { + "path": "wordpress/wp-admin/post-new.php", + "size": 2768, + "sha256": "7eb19c25d261af582cfef80bb4f2542e50c2f86e010640dafb443058093ffb12" + }, + { + "path": "wordpress/wp-admin/post.php", + "size": 10274, + "sha256": "36c0d120c9ee4bd2b6afcfc3c8036e37f73954c596e6da9bd6b8fbf55891ea38" + }, + { + "path": "wordpress/wp-admin/press-this.php", + "size": 2470, + "sha256": "b0015ef08b954cdf582b52bf513b548f5029274e4f6535ffd46e3028ac7e4064" + }, + { + "path": "wordpress/wp-admin/privacy-policy-guide.php", + "size": 3756, + "sha256": "2bffd0c6707ce90c709c028ead6d72d1bd266e6aa4b9ed77327e9d2340003584" + }, + { + "path": "wordpress/wp-admin/privacy.php", + "size": 2895, + "sha256": "06da88807d949517f438b0b7be63332f072deb6516fc0206a2fd94880e3e9873" + }, + { + "path": "wordpress/wp-admin/profile.php", + "size": 283, + "sha256": "b859d979af3ded96684bf71b206641d8634ef37e3aecde6ec6c26d9e2d121a34" + }, + { + "path": "wordpress/wp-admin/revision.php", + "size": 5836, + "sha256": "6aa4fb77be0c818f5117f08fb9f2b3707a54856f531e3f690d97e94deaf5183e" + }, + { + "path": "wordpress/wp-admin/setup-config.php", + "size": 17945, + "sha256": "44625b1c354c8f4f8975367ada80fe92edb8fe49d7321b2f0fc1aeef26c7b0ff" + }, + { + "path": "wordpress/wp-admin/site-editor.php", + "size": 12362, + "sha256": "3a91f1c963c3b8c1cd80d2425c0bcfd0ef823a45e4f1a6d537823083b5b8ee9f" + }, + { + "path": "wordpress/wp-admin/site-health-info.php", + "size": 4150, + "sha256": "5b7c98b85c155c9558ff18ad8b167792fc66ee99bf974729d233a15e9fdee2f9" + }, + { + "path": "wordpress/wp-admin/site-health.php", + "size": 10421, + "sha256": "5d327dfa91441d098f1f1c21607a85882556e7bea0681b43cde5b9d4ff5d6345" + }, + { + "path": "wordpress/wp-admin/term.php", + "size": 2249, + "sha256": "54f1ccf84e5de91a8320ca57371e6c8046409faf622f932d2707f98e6854ea42" + }, + { + "path": "wordpress/wp-admin/theme-editor.php", + "size": 17279, + "sha256": "c17b3f27be5d2465b8585ddbed81d99af66f0553dc8c79faa2c2f601feac8673" + }, + { + "path": "wordpress/wp-admin/theme-install.php", + "size": 24222, + "sha256": "cf0203139d622a02ea1f33f9891a13368c84dceeaab3df2ab9afcb94726ee183" + }, + { + "path": "wordpress/wp-admin/themes.php", + "size": 49405, + "sha256": "09951294b01478f5e9c2b46955ee357c1e644f128d0e8ca1d09d21c2e0cb1906" + }, + { + "path": "wordpress/wp-admin/tools.php", + "size": 3514, + "sha256": "78ff5ca5fc50dc436b73a453fedc10c98d7a5168a7f407c9c28abf3ac4030cd2" + }, + { + "path": "wordpress/wp-admin/update-core.php", + "size": 46202, + "sha256": "eff7c82a67965a93452849328374ef40424d392a9882ae2c6b934c4ba513d940" + }, + { + "path": "wordpress/wp-admin/update.php", + "size": 13062, + "sha256": "ce5629d14daa99214b1721f3510bb9ab943da4b47f80363b402e8feb6a0b628a" + }, + { + "path": "wordpress/wp-admin/upgrade-functions.php", + "size": 341, + "sha256": "74a3a807acd39eb3be1cf6510714674df094058f6510d8e27f390cf1b87d49b4" + }, + { + "path": "wordpress/wp-admin/upgrade.php", + "size": 6390, + "sha256": "2498f13efeeb2618d342e09ec486fddd777539258bfa5dfe28771c0df63826da" + }, + { + "path": "wordpress/wp-admin/upload.php", + "size": 15257, + "sha256": "c8b4afa27075461cbaee946dc0405b5572e551242160f2d1dd2ef95ce5756222" + }, + { + "path": "wordpress/wp-admin/user-edit.php", + "size": 41319, + "sha256": "ef3cc4c5cd45f2f7d6fc347c1f16101279a7d8da3367b39a6479bc4d1d120033" + }, + { + "path": "wordpress/wp-admin/user-new.php", + "size": 24639, + "sha256": "007d5b3bd81c22fd16076ccf58d9b6c6dc3c0d84924c6d3399d9ec71f9f89479" + }, + { + "path": "wordpress/wp-admin/user/about.php", + "size": 257, + "sha256": "1374b35c4525ea452452160250e4b3661765c4f0f413808158be47cb23922058" + }, + { + "path": "wordpress/wp-admin/user/admin.php", + "size": 865, + "sha256": "9b31d08d5227392518bcf912b16ee0fb8efb25d26e1643082a9faaf2a6c6425e" + }, + { + "path": "wordpress/wp-admin/user/contribute.php", + "size": 267, + "sha256": "45a9c76ac59e6ebe599a8c52300173a34698225eb1451623777d5de32bc31e0c" + }, + { + "path": "wordpress/wp-admin/user/credits.php", + "size": 261, + "sha256": "2e903fe3146f05e599992d85cbe049a61ec469c340d3229a63cb7c24b633ec94" + }, + { + "path": "wordpress/wp-admin/user/freedoms.php", + "size": 263, + "sha256": "2f2121c5dc7bbeb91ad281c919f998099ac7e193c669e08fb01f7896238a3386" + }, + { + "path": "wordpress/wp-admin/user/index.php", + "size": 251, + "sha256": "2a47f94923cef9c4bdc45eef385664a3161b33f5246e9a3b926b10e98177ee61" + }, + { + "path": "wordpress/wp-admin/user/menu.php", + "size": 831, + "sha256": "ce519895a313d0c953e5a3c9a0a27c28cebb0c0f24a56fed2210c86f3bbccb22" + }, + { + "path": "wordpress/wp-admin/user/privacy.php", + "size": 261, + "sha256": "eeb547f3d29435971119bcdf0667d860f709687e7064593311a9453198e39e72" + }, + { + "path": "wordpress/wp-admin/user/profile.php", + "size": 252, + "sha256": "e29a9a480e50e8008741e8734ac7f2c7be23e9a8f92136d59775f17c257f89a6" + }, + { + "path": "wordpress/wp-admin/user/user-edit.php", + "size": 250, + "sha256": "e5b8cee3709facd536ce37e360f0ec61b9de951bc74ce85b83ca902b0b9db75d" + }, + { + "path": "wordpress/wp-admin/users.php", + "size": 24002, + "sha256": "01c9363e2c645f527784c602232ae11b7dd76df31c596db97b1990e8a0778519" + }, + { + "path": "wordpress/wp-admin/widgets-form-blocks.php", + "size": 5242, + "sha256": "b96a798f9eb855ed619e793e04483ded975bcd70651783c3ed4c5632c4e2d2f7" + }, + { + "path": "wordpress/wp-admin/widgets-form.php", + "size": 19595, + "sha256": "19cf46b95e12ec2ea26aba4275f66737f135a6024d237a233475fa8d2d96a44f" + }, + { + "path": "wordpress/wp-admin/widgets.php", + "size": 1112, + "sha256": "d7664b079479728722ecb16dc6fc94db1fc5a849b17afa23430dc713640d0188" + }, + { + "path": "wordpress/wp-blog-header.php", + "size": 351, + "sha256": "eb6491db4178b3858d44402ec337623a5ee12de66128dbc92742dd8735f6ed07" + }, + { + "path": "wordpress/wp-comments-post.php", + "size": 2323, + "sha256": "2b4a47b8da911e184f8d1554c9c860245feb19176b3715b24e6437bd9fa393c9" + }, + { + "path": "wordpress/wp-config-sample.php", + "size": 3339, + "sha256": "b2b3f3fe6dbf560b38db952a868df3120fff14bb39ba96ea34e2f26b5c2892f8" + }, + { + "path": "wordpress/wp-content/index.php", + "size": 28, + "sha256": "bd48c24ce4500e60d2524571756787283f0d3fbfa50b116331309c09773f0cd1" + }, + { + "path": "wordpress/wp-content/plugins/akismet/abilities/class-akismet-ability-comment-check.php", + "size": 6385, + "sha256": "2c7f54b636dc5dd8a2d25b63178d257eb1c2d99e8a2c257068ecaeafb957512e" + }, + { + "path": "wordpress/wp-content/plugins/akismet/abilities/class-akismet-ability-get-stats.php", + "size": 6058, + "sha256": "e81071866e5d18faa254ffd98b242602ba86f564a6c7d32d796def20bbafb360" + }, + { + "path": "wordpress/wp-content/plugins/akismet/abilities/class-akismet-ability.php", + "size": 1539, + "sha256": "7eb568b57357468a06b7a730ed62383c1ea062464618c5835ca89373a31c9f9a" + }, + { + "path": "wordpress/wp-content/plugins/akismet/abilities/interface-akismet-ability.php", + "size": 2936, + "sha256": "68d7fa85856af4344ffe31ee2fac093ecd2c33670431588e9ac12760f9033c2e" + }, + { + "path": "wordpress/wp-content/plugins/akismet/akismet.php", + "size": 3386, + "sha256": "5966f801ef51b21bc54c9a1faabc4bb24823bd53afa7a28d63aaad1f0f4dc3ff" + }, + { + "path": "wordpress/wp-content/plugins/akismet/class-akismet-abilities.php", + "size": 2097, + "sha256": "be62a8c84c06c9e7b0264bb07b0db0169ed773336b74e01bf5f9ed36fb35c0ee" + }, + { + "path": "wordpress/wp-content/plugins/akismet/class-akismet-compatible-plugins.php", + "size": 8022, + "sha256": "732c80a04789b9f93c38a0ff649620d763a9e8df467affc38ac149315cbf0807" + }, + { + "path": "wordpress/wp-content/plugins/akismet/class-akismet-connector.php", + "size": 4580, + "sha256": "4238c192eb2493af1aac57c0f856e805e35d17a55d788ba347a16e7223909de4" + }, + { + "path": "wordpress/wp-content/plugins/akismet/class.akismet-admin.php", + "size": 65945, + "sha256": "2d86c34ad140233a25f42468f51f413156d451bbb60d63b09ce6f6f957ceba68" + }, + { + "path": "wordpress/wp-content/plugins/akismet/class.akismet-cli.php", + "size": 5009, + "sha256": "bcbc31cf0bd70981f66fadc4f193701a65111484fed8d84323c2582525d84ebf" + }, + { + "path": "wordpress/wp-content/plugins/akismet/class.akismet-rest-api.php", + "size": 20557, + "sha256": "86ce6ccc6aade08eab2033abbe110a61f54c78a116145c7d2b7ed0d530b04b98" + }, + { + "path": "wordpress/wp-content/plugins/akismet/class.akismet-widget.php", + "size": 4494, + "sha256": "fa4ee1b40f1d7563249e0708d506196c8975c7c2a3131390e36a6c4cec333766" + }, + { + "path": "wordpress/wp-content/plugins/akismet/class.akismet.php", + "size": 89458, + "sha256": "2280353fcd35cce5987cc8bd08eafbde71d67015a0c1e4db1d9106e62f18112e" + }, + { + "path": "wordpress/wp-content/plugins/akismet/index.php", + "size": 58, + "sha256": "592803033cd5181c05dfc0607145b5822f28dc0a8a3bb5b4494438f7a2d1ae0a" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/activate.php", + "size": 138, + "sha256": "e01c60498bce6ef0712f8565db5882dadc15064c904e816228d5634fe2b99f6e" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/compatible-plugins.php", + "size": 4122, + "sha256": "a23e281a1b32184d73ebd41e04f24c1ad90e5e2977490af4ada5436ddae3c476" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/config.php", + "size": 17766, + "sha256": "707f777f5645d07d7fe18f2a0644e3232fb8e5d4a3c7ceb2b4a4497a93e501ba" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/connect-jp.php", + "size": 457, + "sha256": "2643a036469ffc390943669cc33e663870cc97064cb7e55e65c0bffdb79029e6" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/enter.php", + "size": 1044, + "sha256": "2d4ccbee674fa0d52a12fdac171a209263be5d48eb1cbd5f7603d114c3e0e605" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/footer.php", + "size": 149, + "sha256": "169b038fc152dd41ed8f73464a9fc8448b5c84eb0442ae976bb8dc9cb0b73c98" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/get.php", + "size": 1273, + "sha256": "9cef9c634f2c339d505dc632ad9a6af6ba2f4cd45f463b42578aee3223842e9b" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/logo.php", + "size": 770, + "sha256": "e17714c9f264ecb8a12eca71872b9f519f8cd61d53b5f6f8094205c8e4b889e8" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/notice.php", + "size": 18025, + "sha256": "b970ea4d220977573403b1d0f1b4056cc7e6b5241b6bfbc29bd0fcb88ca86b08" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/predefined.php", + "size": 312, + "sha256": "faa27f42f687d9091b5c046d22db52750210afa87be0d16f5f90f1fa1145ffd0" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/setup-jetpack.php", + "size": 4342, + "sha256": "3321b84d707039ec92b531d4be55205b766c6cb3f70776b6e71a628e912ab418" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/setup.php", + "size": 3395, + "sha256": "3f87ef1afcf5cffd7b658dcc08af0981f131f496c680ff582df243a6772b2214" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/start.php", + "size": 1059, + "sha256": "1f646e21f97c025c8a08647a86d0aaa4160b4adc53d5ed8e43bfe6a19e8882be" + }, + { + "path": "wordpress/wp-content/plugins/akismet/views/stats.php", + "size": 1259, + "sha256": "318cad544efb6c32f3c8f041d25b4dcaf0274505d297938c91fb2db9b29dfd01" + }, + { + "path": "wordpress/wp-content/plugins/akismet/wrapper.php", + "size": 6434, + "sha256": "44df3623a0c6d178a718199cd6b3b9a8a54a6bb6312c86a034aacfa522561497" + }, + { + "path": "wordpress/wp-content/plugins/hello.php", + "size": 2671, + "sha256": "baa99cd5d48da5df35390aa8fbfb0b49b2242fcfb98832573b564a8f744b9eaa" + }, + { + "path": "wordpress/wp-content/plugins/index.php", + "size": 28, + "sha256": "bd48c24ce4500e60d2524571756787283f0d3fbfa50b116331309c09773f0cd1" + }, + { + "path": "wordpress/wp-content/themes/index.php", + "size": 28, + "sha256": "bd48c24ce4500e60d2524571756787283f0d3fbfa50b116331309c09773f0cd1" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/functions.php", + "size": 4119, + "sha256": "51681ddc22490e05066da69a44e74bd8ee78249f6322d8c6efcd777d19641f84" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/package-lock.json", + "size": 54180, + "sha256": "608110aa82e03301f6df2db16ac7b4d78549a95a28c97888257f3fb56315470d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/package.json", + "size": 776, + "sha256": "dcfcfe70a9ac4da8c894eb48f1806fe6b053363093112331c51217180daf28b8" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/parts/footer-columns.html", + "size": 64, + "sha256": "72a13f85d46ba9e3389a039b49d3a4ac24e81e84020d3a9bd775c6a0c6621d0c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/parts/footer-newsletter.html", + "size": 67, + "sha256": "d644864553b62b4a7228268d3813441a4de6f700debfe1597aef18403fbd6199" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/parts/footer.html", + "size": 56, + "sha256": "7d08f40a56e7b2fd9f7004ff2b3f52b6f8d3fd209b41f89201436a679a69ceb6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/parts/header-large-title.html", + "size": 68, + "sha256": "ffb8fa38ce94c2099d00407bd347a7af9a0ce6bb8444c3d33108376484bbe489" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/parts/header.html", + "size": 56, + "sha256": "a517b91628cc33c6f584d6e42403ad19149af1fd6f44678f41a9ad56ed56a724" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/parts/sidebar.html", + "size": 64, + "sha256": "cf928bb2e23ccfd759cefd30d180d7902cc446a26b31729a8d03740c6a202555" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/parts/vertical-header.html", + "size": 65, + "sha256": "405bacb530efc777f31fb32acd5c1f6530533421e680de5ef6cbf15868773eb0" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/banner-about-book.php", + "size": 2777, + "sha256": "1c58ac440c82ed295a0614ae3e88fc4978ca3cb1f8cecc98a052e48fe25d666a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/banner-cover-big-heading.php", + "size": 1892, + "sha256": "e4559bde50f5439158627e543b97f42b5478efe006d9f5e724652745eda26b65" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/banner-intro-image.php", + "size": 2420, + "sha256": "876264471170b5f444a1e9f0d62f927d404d5dc3d16c880192df61c5a4b6e4e8" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/banner-intro.php", + "size": 1259, + "sha256": "9b53736f8cab1b713d0553ba95fee8b678d5032472f4462da8ea72b12c781d1d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/banner-poster.php", + "size": 4622, + "sha256": "b27a4df1363f14c746ad369693ce5d8b251f690bb7d2555bffee085fab937978" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/banner-with-description-and-images-grid.php", + "size": 3030, + "sha256": "20910ff931bb394ad8ead1cb7d58a604a7f99a1a0534a10ea927016994211e7d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/binding-format.php", + "size": 489, + "sha256": "64ce8fca9ebcb3be9add69d71d5bad9efb956c6d5ef1f8d60d86ae4d98faca42" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/comments.php", + "size": 1988, + "sha256": "7553ccfe075e5d626f6f130176e524870d05f960164cb671dd0e4ab5beaf8d40" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/contact-centered-social-link.php", + "size": 2052, + "sha256": "5d4615551e19330659d8d27c03bf247b0342de7a5a8b2de8dd1af4b54ff4e0cb" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/contact-info-locations.php", + "size": 6463, + "sha256": "b6f3ab7286c44731fd0bf3d07fe699c35f516386ca8f8d5f304ce29f45727763" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/contact-location-and-link.php", + "size": 2552, + "sha256": "357fac1f2eaf17c4da08411da976000f14411bcc464a67f3809a1c666788082a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/cta-book-links.php", + "size": 3364, + "sha256": "a5e2767f9fb13c0d92a8aa3afb653c264ce3919ac1215810c8b4a768eadf252d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/cta-book-locations.php", + "size": 7944, + "sha256": "968be379532c2a575bc1ee03712c8bbcc575734d2aed7a997584c9775d76ed3c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/cta-centered-heading.php", + "size": 2041, + "sha256": "d8fd01c6955d51466603e78ec3c788bebd5b2f9b63ccc5534544fd9e54f4a444" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/cta-events-list.php", + "size": 8886, + "sha256": "000fa85d975e0b58349d00a7a7a1645505bceb7c482155e597a626728e0902d6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/cta-grid-products-link.php", + "size": 10593, + "sha256": "25718eca3c669e8029108ecba76da6819678a94e09158e8b1719e2cef4597526" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/cta-heading-search.php", + "size": 1329, + "sha256": "9b0b4211451c4267dc38a40953ad688c6c5b04632539b7a85f41abbea126060a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/cta-newsletter.php", + "size": 2363, + "sha256": "9fe0ba554a0502e4752e841be0de820988481ad09db47ffa0b4ac4cb4b7995d4" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/event-3-col.php", + "size": 6641, + "sha256": "9b81e9606df809b42f905d092f1451ca3e88b16d8c1fe807d743dad24edcde0b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/event-rsvp.php", + "size": 6009, + "sha256": "0b95aa5ae32e0b519f4021a10c22d88967befda98937220ba3ede9449bb9b5a6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/event-schedule.php", + "size": 12877, + "sha256": "5718bab1b9e465b4853abc214544e6fb465e0e56d823a17ba29dd3be5cb19f47" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/footer-centered.php", + "size": 1542, + "sha256": "0d2a7e9c1471a4aa56cd2f9203435014f7b78c592440b305befbe9d16662655e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/footer-columns.php", + "size": 4963, + "sha256": "c9295fc2b6cb30e5099dc216f6dfd5b07883527582204addb944009f7980922e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/footer-newsletter.php", + "size": 2646, + "sha256": "5e58c759a93a8636c28197cce785a28503598f87c586b42682e7059069c989ea" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/footer-social.php", + "size": 2427, + "sha256": "b5486305555f9f5dd76902362a79b43a6aac1339b6ab06c72eac2f4a711884c8" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/footer.php", + "size": 3945, + "sha256": "9dbd6b8467075fb28afa2d485716a009dfbe9bcb8d8383e60a467ad15ae65da6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/format-audio.php", + "size": 2208, + "sha256": "e332ba8ba7f797adc0605877125838de0f7efc95ee816c90e646ebd87c5f7b53" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/format-link.php", + "size": 1524, + "sha256": "c5cd56d7eede3431002795b00a184c76e84e5cbb66afe88ef5049fa18a7acace" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/grid-videos.php", + "size": 2122, + "sha256": "ef5ce841af73898251063205c65d46bd9cdfaeb8158721fb6c88bd8f1f9f534b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/grid-with-categories.php", + "size": 4917, + "sha256": "d28f35fa949b1f656b8daf28b8e26d10650a50020cf095d227b60f913db94e7a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/header-centered.php", + "size": 1130, + "sha256": "7f3721c45c16c19819119188b102bbd8c515b4a7516b44912493c814e4055a20" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/header-columns.php", + "size": 1498, + "sha256": "e6f32b2ca7c7a22335ed05a102843ea6a9e317b4509297411a007e129d636d80" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/header-large-title.php", + "size": 1607, + "sha256": "4bc78c4143819da93527c118243e667715455175d8e7e940d52dc70913e2ffc1" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/header.php", + "size": 1316, + "sha256": "8b06aa6608bf4bf65f3a1905890b94dc3951d1eaeb4411c2e29944fa8de168e9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/heading-and-paragraph-with-image.php", + "size": 2729, + "sha256": "8f1c0ffdd2dc258c1038d6a5392811c4ce4ea109d726c14cf4fdcbfc01143a09" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hero-book.php", + "size": 2950, + "sha256": "02c2020da40a8e5e84012a67de0317e2369cbb30b45668bd2659b4ec4b88779c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hero-full-width-image.php", + "size": 2679, + "sha256": "ca767958f4fab96cf2f829414613452f9dcfaf0f6cb857ee4ff5506132c3d61d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hero-overlapped-book-cover-with-links.php", + "size": 6981, + "sha256": "fe054cac45488c67b1e82f6023983a23cb9e38459499f4d5bfc33bcbb7856f34" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hero-podcast.php", + "size": 4025, + "sha256": "f5939b9624fd4e2fd326cd22cbae92a8aef1f0326cf7d4f03ef9b555e781c612" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hidden-404.php", + "size": 1849, + "sha256": "770b643728d80316c87d8ab6dc8bad89b858bc7cf5aab14ab453532f4e86d011" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hidden-blog-heading.php", + "size": 448, + "sha256": "a4d343e44b9ffd1fbc4c4185454816389512715f5e689b4d647455b6bd87a86b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hidden-search.php", + "size": 529, + "sha256": "827ded3410afabaeaae1ddbe5d5795caac47e2bb95294fb14113eac668e7b52e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hidden-sidebar.php", + "size": 2055, + "sha256": "2942917189fc14caeec11208e4ed58381070ba8511b67b9ffe4aaaed01f11c5e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/hidden-written-by.php", + "size": 847, + "sha256": "570f30c5df0aab0515a02ea5df276b7e7b0b454b4d3c0444f400a134bbac012c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/logos.php", + "size": 2559, + "sha256": "4b06b390187b11fee293a5634ab3af055f1d182e13e0d618f9710c138e157c3c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/media-instagram-grid.php", + "size": 4817, + "sha256": "aa6a9af004656d219459d5f859076015084d92d92af95cf3313483d1937d9ec0" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/more-posts.php", + "size": 2199, + "sha256": "f44d79ff07c6040fe5ca70ea215c9e9418fa857fd84d8ba80bc73756a3c916dc" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/overlapped-images.php", + "size": 3429, + "sha256": "de6f6e7f7781ec912ebab5bb09dd717d71aba00f44108f9b8e87c1e532a8b2c1" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-business-home.php", + "size": 784, + "sha256": "097d86f6fcb51678a0cf1288ead96f4f99b04ad2ba0e1b1730893cb9f9da6ed9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-coming-soon.php", + "size": 3356, + "sha256": "05b250e09c4cf5fa39c6529dd073d832f3a23cf1407b1696917a97bf4f276c21" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-cv-bio.php", + "size": 4292, + "sha256": "a56448a8590133bd999f9c811ee895911ee64fc3ab4a963eb3181020c248b485" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-landing-book.php", + "size": 848, + "sha256": "04a99876066e62c4441733f9407a2be2df2d3c732a31da7933d8fc1d2e494852" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-landing-event.php", + "size": 822, + "sha256": "2114cff68d5c040c0a04595b186e9ebdbd92c100ccc55be9d3b18414dc18e1b3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-landing-podcast.php", + "size": 804, + "sha256": "f0eec78695bca2cbd86d8c5eaf785a1e6113313035afb9989ee822db074372dc" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-link-in-bio-heading-paragraph-links-image.php", + "size": 3732, + "sha256": "24ccac8ecc4fe407415ab069458ed3c7d36e19c5ad54080b5ec103e4e6363ee5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-link-in-bio-wide-margins.php", + "size": 3251, + "sha256": "525b918f098c68975c4d40fc7e0ab88d7086361ce8b3f206d92657a350f9868d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-link-in-bio-with-tight-margins.php", + "size": 4017, + "sha256": "209ebae99a5c3e47411b551a7ca8bd670b81d8aa99f2d8b66206c1dd3fcb29df" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-portfolio-home.php", + "size": 12825, + "sha256": "b0163fc4a27b05c76257bef7683d8163aa2af3ea12ce74263123219e1bd33863" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/page-shop-home.php", + "size": 574, + "sha256": "a16cea3ccf9e704f34a5d94b63a271595c9ca21b5000b1ca060644e8f308f5eb" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/post-navigation.php", + "size": 1451, + "sha256": "6d4636488432093080187d8f4cd4b65f199c775534de76f90e261abc97a9d0e3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/pricing-2-col.php", + "size": 6539, + "sha256": "d184df5651b745e00df43fcd6b69e7af5df30409c51d3e67549d4f849cedeb40" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/pricing-3-col.php", + "size": 10361, + "sha256": "59ab98ed9e68422ad4355289ccb172e0024c987becfa30b5b6bf401cef10eca6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/services-3-col.php", + "size": 3845, + "sha256": "73ac7cb49d1c84ab9a1209b218ba5e821b94e016cdb592faf240f2369e1e0446" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/services-subscriber-only-section.php", + "size": 4075, + "sha256": "0d74f9a10b7df2f2f946f4a66d710315725d6612184d1ff981d22f81f55b0f89" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/services-team-photos.php", + "size": 2934, + "sha256": "9fff23fadb72494176bcffd70bb19106c8b33a06b24c116397ab48d2c14543ea" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-404-vertical-header-blog.php", + "size": 1822, + "sha256": "95b56e4fab19688ce812697ac055315a027ddc365fc020c795341fb16be8ea74" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-archive-news-blog.php", + "size": 1284, + "sha256": "7467b1705ef53c0f200f0505b1ed51290a9a1255a3a74a7b8644366657a5e0a2" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-archive-photo-blog.php", + "size": 793, + "sha256": "52e8e673e5045d636ad573074a89e160e0345e3eaf9752abe4acede066c26135" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-archive-text-blog.php", + "size": 976, + "sha256": "88f521271a666fb847d00dcd6bb31ca6467fce83ce8165e9286f344b9e519e3d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-archive-vertical-header-blog.php", + "size": 2164, + "sha256": "5763f62f9df09dfe4e50501184b729980ce3be45a2d35285d5e8e8687704483f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-home-news-blog.php", + "size": 9407, + "sha256": "fbcfd519ae48bb09eb22e68eee6de1fdc741ce0734e0722f9c62afe958436c33" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-home-photo-blog.php", + "size": 1346, + "sha256": "8e915a91015cdeb5f6295803a4e1c080cd4676a76abf7df545fcc30da779cc8c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-home-posts-grid-news-blog.php", + "size": 8773, + "sha256": "e0677fb321a5edc5045b284d47498326c1180a103f3367ad92d3e9fd91740b81" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-home-text-blog.php", + "size": 1059, + "sha256": "5a33d2feb7595c7cf45127aaabf271eee77240bec6695c4ef01305bbbb0052ef" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-home-vertical-header-blog.php", + "size": 2119, + "sha256": "a9d64f72631066a8cf6449e9707d29da32fe5eb9327f73f6d5958f58e917e05c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-home-with-sidebar-news-blog.php", + "size": 7716, + "sha256": "829a58689243d55b67d85b38165a623d35c677dc929b19a4d7a00516739ada54" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-page-photo-blog.php", + "size": 1222, + "sha256": "346931e4af500de6508e922999cd9e2c537471dccf2c59bbc16895e027bbdccd" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-page-vertical-header-blog.php", + "size": 2952, + "sha256": "559f3c74e4b0d6bb11d275bc6ee18c2af4c6725caa9816628927786fadae7cfd" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-query-loop-news-blog.php", + "size": 3444, + "sha256": "bfff273809b4de939e71a2efe7fd472e67872cdfa97de1ca7f837f62100ec832" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-query-loop-photo-blog.php", + "size": 2084, + "sha256": "ba589dcf3ebe0a62ef5d810a185552c8cec66f432b9f803f6bcbc6dafc198212" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-query-loop-text-blog.php", + "size": 2715, + "sha256": "e3416dd8cf5a21234ecbb54363226a3667b19467d78824e9a24c9ea524c19056" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-query-loop-vertical-header-blog.php", + "size": 2258, + "sha256": "50f1be3c620903729135e48e6b4ffb4ad6a25b98cae863491b2011dc480e4b43" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-query-loop.php", + "size": 2467, + "sha256": "ee3bbb651a60edf086ba2e8e184894deca0fcd02f684fdf5ec7299b2a3108e95" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-search-news-blog.php", + "size": 1309, + "sha256": "1f3880fd04f0619053a43a2946c523417ff5c461574621b0519a2760c86daa94" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-search-photo-blog.php", + "size": 954, + "sha256": "e23f9f9dcde749facda0015bb893f0b5b70ce189e96d2b8bef6288852387a085" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-search-text-blog.php", + "size": 1130, + "sha256": "cbc3ff334a9621743070eca89c2a2f3ae5e3bd0ffde58877cb640f942955e57a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-search-vertical-header-blog.php", + "size": 2196, + "sha256": "2d9a5dbad9de31bc13923207492da1fc61c67d358d7c15431d36e9a0ac7ab664" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-single-left-aligned-content.php", + "size": 5939, + "sha256": "37cdc16170ff8fb3fc66b92b14c07789e87eb7d22ae0d0f0910ad0b3f792b51e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-single-news-blog.php", + "size": 7100, + "sha256": "3299a3a0c08944caf1da4e493903429c1c228ca3864463eb18112635fca187e5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-single-offset.php", + "size": 5301, + "sha256": "9e9e510f2c5ec18942434538767deacccdbb602d978bcc9451556e0887f14d6d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-single-photo-blog.php", + "size": 5698, + "sha256": "594266ec70405e14c4f5af98676123f7e3f630da6c6ff65f79624ba843107002" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-single-text-blog.php", + "size": 3111, + "sha256": "97775963624c15ee0f37030bb020f641ffd11c44152f6cc2fb23db158dd45fbf" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/template-single-vertical-header-blog.php", + "size": 5997, + "sha256": "6612fa66eedb3c246985e20ba63d9b7ce5ddab22129205d17e0d5af56a35103d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/testimonials-2-col.php", + "size": 4463, + "sha256": "037e34bee61a465cfca301eeb3985e162827f563d8fab70bd4bd411a439a638a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/testimonials-6-col.php", + "size": 12003, + "sha256": "c0e8cb3e7e1d59e6012fcc791dc294c6461b56bdd82c4ad3995688407e62783e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/testimonials-large.php", + "size": 3456, + "sha256": "92e4cbb13c8e81fcb4ee649dfc22317c31d5d46ffa78ac3adda634d5a61fecee" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/text-faqs.php", + "size": 5895, + "sha256": "3d3c46b96c203d26c85f82d6379d7e66c14feea0c1b1af4169848bfa088d5307" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/patterns/vertical-header.php", + "size": 1602, + "sha256": "8ed9ff2cfce496a989ea017cb135c83b72efdf14596f960edd038b133af52601" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/style.css", + "size": 3009, + "sha256": "069ec4541fc7a3ee2e49d69fefc9bb648c05091ba336bb5c6e9ae72998f0af46" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/01-evening.json", + "size": 2336, + "sha256": "fc0ba07a733b51658b181f40a37d953ab84239c8d94084dc45663666fa51b0dd" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/02-noon.json", + "size": 9294, + "sha256": "dfb0860ca05cdb8a46caa5e9d9df00944478ec8f2ab2db2916ad2b0e518c7c14" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/03-dusk.json", + "size": 7574, + "sha256": "48e34125e45d42adec4c845d17ab06c2f5ee2e64ac10473bf9fbe6f1b8868f3c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/04-afternoon.json", + "size": 5956, + "sha256": "74dfb37e1d67671b80ee5f80b4ee13615f609443a688b690d9bdd5c1cc48e050" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/05-twilight.json", + "size": 5021, + "sha256": "c59104ccba867950b025018f35afbf4de61862bd7c327878281ddd04eb34881b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/06-morning.json", + "size": 10607, + "sha256": "7f905a350041ffb807b7d88d43dee46ad85aa5a4e4ddad159fb872a497186734" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/07-sunrise.json", + "size": 10730, + "sha256": "ea300ad13fa4209cea160809479490451594d0033bb5323a65bc37133f67b403" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/08-midnight.json", + "size": 13311, + "sha256": "e698c03db0d538c516848ee1bda76e014eae5a1cc9253837a2a3f570f2d16987" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/blocks/01-display.json", + "size": 308, + "sha256": "66bce646942f48f934562b2c3d4befc9e1882149e1543230b470127367b4626d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/blocks/02-subtitle.json", + "size": 311, + "sha256": "d8a64c9d899cd8295435ca9186c75cb94d5bfbd6447f3d8ec4eb9b6da894f285" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/blocks/03-annotation.json", + "size": 678, + "sha256": "fc7588abce000b181beda4eb464732cec141bbb38e4f3f602480244576334b32" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/blocks/post-terms-1.json", + "size": 680, + "sha256": "8a8bb1cc73a7b000994d7e5aaa21907c8b8cce4c493240f555db0fa7db016a34" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/colors/01-evening.json", + "size": 1934, + "sha256": "cd5fa328144d0e01fac9948f40280683b54527ba2349f08eff2a63fccad983f9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/colors/02-noon.json", + "size": 2809, + "sha256": "37693133181be66b2f7877377b1638470aed196add64b1a743ca2e6ae3aef3a4" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/colors/03-dusk.json", + "size": 4819, + "sha256": "4c64496a410be3aff21285b56940d757b4dcc057124f79dc144334a0ec48bc49" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/colors/04-afternoon.json", + "size": 1944, + "sha256": "2a326125cf021f4b52bfb289d19aaaac5bb04ebea8371ca3e1cd39db2344454f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/colors/05-twilight.json", + "size": 1301, + "sha256": "78d4cf099c67c93ef8a3b25bf8da367e412f0bd59def54c38724fd1fc57b56b5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/colors/06-morning.json", + "size": 4361, + "sha256": "cbe4387ecc061f1a0680c4af5bda9d9d4949372f0eda73d56208fe55797269a2" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/colors/07-sunrise.json", + "size": 5365, + "sha256": "15cd52ac33eb0eb6e8d2476fc3cd70c5ee4e9794e3186fd2f61c11ac81ee7c53" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/colors/08-midnight.json", + "size": 2642, + "sha256": "07190670829d9c14bcd6adf78498f94ab0d36cc101c917d9789210326a23f4a7" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/sections/section-1.json", + "size": 2145, + "sha256": "28d4c1bea128efdd71e5740e46d01054e79b4e7a80a827c84c50070dafa293e5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/sections/section-2.json", + "size": 1964, + "sha256": "b17d670a561cc519df5af8106f74e172fe6a804bbfe1c8226f6df5a816a029d9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/sections/section-3.json", + "size": 1964, + "sha256": "33bfd7e0d328b2affad7f84061524d373936070d27d170bda580588f50941661" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/sections/section-4.json", + "size": 2298, + "sha256": "dcb100daf19d668acabf4de4ad5e2276781816c618126720cf9657e03e54ac0a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/sections/section-5.json", + "size": 2286, + "sha256": "635d404f92e0352820a975c9be05c8ec3e6e0c5e9d7db2ccd436e5345b6a85cc" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/typography/typography-preset-1.json", + "size": 6342, + "sha256": "ab7c1856b3ea537361f9379ebc5448e74280ef11d0860646ad247baace18db4a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/typography/typography-preset-2.json", + "size": 3132, + "sha256": "3a33dd285964b5ca8f71dc5738815b23575f607c169216ce2c08d2c2708b0fb5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/typography/typography-preset-3.json", + "size": 3809, + "sha256": "4f9b37abb95a125565741529716eec924a896233a46dfdfcd4c6a10ba718a132" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/typography/typography-preset-4.json", + "size": 3006, + "sha256": "12193427f994b9aa50790279ca5c761ef7f2e334edc389da2180e061d9d99775" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/typography/typography-preset-5.json", + "size": 6655, + "sha256": "bfbd72f9ebfba2cd7a13f68b0f87cf47315f59be2c1278e156bfb7cbc7ccf34b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/typography/typography-preset-6.json", + "size": 5804, + "sha256": "71b6114af4c19dd76f24001a2e3daff9a9f18aa3f47eea055752954673398155" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/styles/typography/typography-preset-7.json", + "size": 9609, + "sha256": "6cccc91963f1e0ca6456ba851e845fa6bfce545206d78e573f96546ce346ad3c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/templates/404.html", + "size": 621, + "sha256": "426beaa3762f195102bf4baac83acb7bbfd0f90eb019132631900af1fda0f2ea" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/templates/archive.html", + "size": 478, + "sha256": "4fbf558c27a84c265a2c54213584e675ad714643322e53b4f19018bd13ed0fe0" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/templates/home.html", + "size": 471, + "sha256": "6fc6acd188fd1b93cdb4fd0fed6fd49edd48c8610400d313b5304fbf5dd5ec92" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/templates/index.html", + "size": 471, + "sha256": "6fc6acd188fd1b93cdb4fd0fed6fd49edd48c8610400d313b5304fbf5dd5ec92" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/templates/page-no-title.html", + "size": 347, + "sha256": "d406a40c836ceee23e68c9600e2e140e23781ebc3e7c917c11ab0038e575f7d2" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/templates/page.html", + "size": 879, + "sha256": "a09bc716cd7036c8f8553fe1edd9d4f9cd36dc110b36975c49d7cea3a3ba7c4d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/templates/search.html", + "size": 570, + "sha256": "d9478197855d9d584380e8f4659e36ed589adff77f6d54db01f4282385740d1c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/templates/single.html", + "size": 1502, + "sha256": "5f66ec63899ce500050a931d99fccc58cee0b6d9058e769e0c70438a757da2fb" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/theme.json", + "size": 14856, + "sha256": "4ecb78d5040a3af40740d4c00663b604b70710bc53c0bafa15cdfd6b7a3a7b75" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/functions.php", + "size": 5434, + "sha256": "219b63131905c8599ab7627a8eaa20de85d68a3aba19ffe0fa7391b698c844c9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/parts/footer.html", + "size": 56, + "sha256": "9ee48a673b5350b65f9bbda55541419fc5e09e433a1d3324dc3d7b79ce9250e9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/parts/header.html", + "size": 1287, + "sha256": "1e1d7cc5a27849bc88adccc977d1782ba0975ea1ec5c0b72891f0803887f6e6d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/parts/post-meta.html", + "size": 66, + "sha256": "31e206f67189f5b73a5ad7d1750cf8d41de1b49a2a3310b7f99f4559e1aacecb" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/parts/sidebar.html", + "size": 64, + "sha256": "35233c4542e61996c2a9e128eff05502f0d3a24b8f53292c4ed8caddb7a41a79" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/banner-hero.php", + "size": 2813, + "sha256": "4c89665b72d7031a66deba29e43196f34280c9519395ed1d417bf118ddcb4728" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/banner-project-description.php", + "size": 2562, + "sha256": "f482cd6d7101e16124bb6a8f9a91b66946d9fbed2948cee389e2cf160594f21b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/cta-content-image-on-right.php", + "size": 3503, + "sha256": "cce6a370d3f931f3346ebff135267fe80aae8f43f933b5bd77141b58f7b76ec3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/cta-pricing.php", + "size": 13696, + "sha256": "200c49f9d4b4df73dfde95e1037c5bf98a9ec27f96c30e595a8a2f9cfd21d6d8" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/cta-rsvp.php", + "size": 3765, + "sha256": "1cd5e2a4aec33986695af7b0989930c77869cbca9c96e05ac242778d776c2ec6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/cta-services-image-left.php", + "size": 2866, + "sha256": "d61771013e2d7f286c8d523ced7f1c5504809c40cb5fd7cbb7afe14f993e9533" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/cta-subscribe-centered.php", + "size": 2687, + "sha256": "9e282cec73ee258dc8effec7f7efb2fb34d0f9f8b48b36cf164807871cdcafed" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/footer-centered-logo-nav.php", + "size": 1386, + "sha256": "751a9100b6a3afb4434758cdb2a749b7830980f884d738a8ae4399262d977bc3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/footer-colophon-3-col.php", + "size": 4752, + "sha256": "9458f27a268eb4d063cc4bfad9a2d49ebf338fff89865e75d0b85de26b9ce3ec" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/footer.php", + "size": 6619, + "sha256": "6c4029a0b9d78a2d46fbfbea1431788234648e8f4f5b1610fadb2001e0010931" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/gallery-full-screen-image.php", + "size": 1473, + "sha256": "a5d7623a17c5e94c5b69ed71acb6e27a70e233f04803849f1153f0d8097bcd12" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/gallery-offset-images-grid-2-col.php", + "size": 2871, + "sha256": "225c830167a3240dc369d7c0a8d66795853302041bfae35f3a46d3eb13c40c7a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/gallery-offset-images-grid-3-col.php", + "size": 4937, + "sha256": "083ff335cecf188622a1b008c021708f691bd819a0889c128d467c2c59cacb9b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/gallery-offset-images-grid-4-col.php", + "size": 6359, + "sha256": "79095205fd9f5ac9a9d2388b4770fe3e27e1a3cfacaea9f35396834909bb286f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/gallery-project-layout.php", + "size": 4554, + "sha256": "f95299169df5beffb626280ea711e073c3ca24e270ed351d98be8996533ab062" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-404.php", + "size": 632, + "sha256": "033026b6caf097ab02858bb6ef4b371e65a4fc2db180a9a3aadc6c23577b0870" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-comments.php", + "size": 1609, + "sha256": "a8b589b07dd02643d445c1f09b5960f5a871fb8563d0066db94a5acacfb7190b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-no-results.php", + "size": 293, + "sha256": "34bc4824052300e1c01b5af6641bfd6cd99fe76867cfe9f1a6019f2f7b1a7d69" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-portfolio-hero.php", + "size": 842, + "sha256": "f1cb017f807c6a0e55f2f74520f29cbe65c80049e5b586f1e3fdc0f44a522e36" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-post-meta.php", + "size": 1060, + "sha256": "21c7453a0aee13c415d630238f7203baf6e15e278bfca516e6124f30faa1ec2e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-post-navigation.php", + "size": 1095, + "sha256": "8303b487bd65b4611a8e6e45e1086f3107ee049ff46bf8346e1a3c6bce3dca5e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-posts-heading.php", + "size": 405, + "sha256": "a1d39104160088d3420691b235f87b3c60f00f2cc1a8ae38c1a35b36e3d14bc5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-search.php", + "size": 335, + "sha256": "3f5cc24736022d0ff0dd786852fb1f348263fb4821e00e4f4d17fff882d026ad" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/hidden-sidebar.php", + "size": 4654, + "sha256": "99cd27aa9e477ac26ebd2ab7eb8cbfffed94308fff1c8383b0a967b0ffc9f99b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/page-about-business.php", + "size": 855, + "sha256": "03fa86a6dd5dddb64f77943ffdf6c807179a395affa0df98f762e3cab7d3c647" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/page-home-blogging.php", + "size": 2994, + "sha256": "343c56dfa316c1b432119f6400ca25bd00ac89ee67f8cb682c4cf2aa782e1f6d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/page-home-business.php", + "size": 808, + "sha256": "189721cd8c0066b738ec7d49f7141b1c87d620edcd5a24bffc0cdda17c2236c7" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/page-home-portfolio-gallery.php", + "size": 466, + "sha256": "12d35c9df116fcad6290256d7a106d7e0299be1a0a98f9f42a6a2fa08e3c75ce" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/page-home-portfolio.php", + "size": 526, + "sha256": "ededb8c55eb541fd96d9834d023d717b3f6d960402fdd8495384af5ec96191b0" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/page-newsletter-landing.php", + "size": 2982, + "sha256": "bd3208778a34583d0880f0965e2329254df22d4c331662a17da72cc0d5b7aa16" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/page-portfolio-overview.php", + "size": 785, + "sha256": "35dc3cf13d2c59fa94bf046d62dd47c57e7e34380a9e8daf3bfad55ec2a8ed36" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/page-rsvp-landing.php", + "size": 4179, + "sha256": "95d0b6da9cccb40438ea59a61d33b4c800050b7dd8c3b73e2d6bb93bf6c97e57" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/posts-1-col.php", + "size": 2392, + "sha256": "d9244909f19733777d02107b802fda1d967f42d6e3d2ced0fb6672480d253435" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/posts-3-col.php", + "size": 2726, + "sha256": "7677d44b5f0e7230f76895c504dc9e228e8f39f51f32d7822ab7254ffc111bb6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/posts-grid-2-col.php", + "size": 3853, + "sha256": "06d1ec00429666796927017252728b9a1d8a272bfffbbd6a307392f43eb01c5b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/posts-images-only-3-col.php", + "size": 1906, + "sha256": "504d270c400e615f99f32032e1daaa20b11c0bc7773aae2ada32d048d331b23c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/posts-images-only-offset-4-col.php", + "size": 4383, + "sha256": "2985566b157d0635e6af172a4ae4ef0af6d8a04bb8aa9bbb31a97ce46aba9258" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/posts-list.php", + "size": 3449, + "sha256": "870d8809d8fbc8a1f8bf0d05730b46bde198978c8d35153b0c91fddc8745085d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/team-4-col.php", + "size": 6796, + "sha256": "941b130a4a7433a1dccb12fc3c822247996e1757ff961751e3f439c70552f156" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-archive-blogging.php", + "size": 773, + "sha256": "572049202fbc9698025104bdc31c0b6cf2f1fc9cbb41d7c45510da755a3fb8b4" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-archive-portfolio.php", + "size": 721, + "sha256": "9c22ddebe25e5d5c005fa567847d69eca813200d153c20d38bfd433686c897a0" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-home-blogging.php", + "size": 766, + "sha256": "c656b430e400cbf79b901a08d925c5bd3c2f2c1006bc5341f3246e93023e0177" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-home-business.php", + "size": 608, + "sha256": "7fed605d765da95a12eef4a64e721a51ee62a4ef975e3f111e8ce6bd6ef5bc08" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-home-portfolio.php", + "size": 871, + "sha256": "72ee9f40027a5d0116982d7cfd5c41d38239b47e07fb7a2ffaeebb343a886f27" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-index-blogging.php", + "size": 920, + "sha256": "b985367ba44fe1899fb166ad294652b5a018f1bd2fd5e4c80b763026c375e07a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-index-portfolio.php", + "size": 847, + "sha256": "941d2b6931c0c7487e5b0c1842b3cefb6604f3c60f6d0b9a0600c2991c54f06a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-search-blogging.php", + "size": 946, + "sha256": "f422ff850853d71c861fbb751311c5dffcd5bcde1b67377b091bdda913fa450d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-search-portfolio.php", + "size": 996, + "sha256": "ee542cc02564a3d0fafb8017f237417515c1ade9682182348d78453bd7addaf6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/template-single-portfolio.php", + "size": 1188, + "sha256": "206732d68655839d472b60cc183ffbdc33bc64468209bfd604235c290a483798" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/testimonial-centered.php", + "size": 3436, + "sha256": "d220d94aa3badfa67e44d35d2820bff26edb3c6280c510878d152876510a7c7b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/text-alternating-images.php", + "size": 6090, + "sha256": "744daa3938ac9aec9a04efe6531e55140b018b2462139f75a6a24228ac18f062" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/text-centered-statement-small.php", + "size": 1406, + "sha256": "678be6e809a5f415cc5a376605c8e3088237428cff03330e34f76091e469700b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/text-centered-statement.php", + "size": 1906, + "sha256": "560f3ae3dd28daac709e34fc410b3e2e029f1b5078152081a3a44d320ade726c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/text-faq.php", + "size": 7995, + "sha256": "4564675b978a75e8e4e2ee2551d5a0dae90d450d8539748f64c7e6e3d8472229" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/text-feature-grid-3-col.php", + "size": 8004, + "sha256": "44edec98cf3513f74be11adfb8fe78079e81c3815a2dbc4478100d64efb4def3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/text-project-details.php", + "size": 3530, + "sha256": "85fdfb65c3149aa73bab78c06acaa754c849a3ecb5651f976767594107bf62ef" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/patterns/text-title-left-image-right.php", + "size": 3522, + "sha256": "744080a08822aee0e6c483ae5dbf94d0156439d6204237ca8aedc7b76cda9f04" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/style.css", + "size": 1201, + "sha256": "b425dcb68ff9c572878c2e2ffce1a9382812f3d2cad8124f3ef09bba45283d6f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/styles/ember.json", + "size": 5983, + "sha256": "16b55d6ffa4e5c6f797b149533402cbb8ab0f767c4357be3bb4c026a10f34746" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/styles/fossil.json", + "size": 6457, + "sha256": "f0f91a4ec994e07bd2c29cad7f33efc4029f58f049acb4d7847639e49dc54300" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/styles/ice.json", + "size": 6426, + "sha256": "4c67cb1a3feb16c01a92302663ae78ea36acc97fa19bc711ea4bb39afc48a6ab" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/styles/maelstrom.json", + "size": 4385, + "sha256": "8960ad29c76ddf544df466073e5ff54488760e293802024b1244f08c43937cec" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/styles/mint.json", + "size": 3789, + "sha256": "e7a603af968bbc7195e292f34433f9c8d5d9ee50526a91721f4e8f1270ee4835" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/styles/onyx.json", + "size": 3701, + "sha256": "dab477a2fa8565c04c473b98672e463caa1c91c630332485ecf5499d620c8d9b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/styles/rust.json", + "size": 3158, + "sha256": "d9e4ff05d7e166372892802fa966fd8095956086723fff97fdfdac0680ea1544" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/404.html", + "size": 575, + "sha256": "19cd83c05f5487ebc8815420ec63cbb69d910d80b65154d601a36f80368b2a0d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/archive.html", + "size": 534, + "sha256": "c0a735474b70cf7c3d3cb3e51e0400849655c40170f3d9eff2be6daa332e2dad" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/home.html", + "size": 432, + "sha256": "347845003e01c1fc2cecb9e1c3ede7616a0e58e7eaab1f6f9745e25294129c9b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/index.html", + "size": 446, + "sha256": "f8dec887aa917bb20ecc4c46d0d9deb19125c50c1b68cf0751ff9a5b6a9c8b66" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/page-no-title.html", + "size": 417, + "sha256": "b85e76ced2c6becddaba50da45b87b94f1a7369ab2b5084e277d3a71f1af573c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/page-wide.html", + "size": 1177, + "sha256": "ac915b5ab44ecdf8bc9f5e56acd63b4f8deb11e40b05a9d410ba06c6d3c590e3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/page-with-sidebar.html", + "size": 2001, + "sha256": "0abf33689ae0d171398dcff3463edf0755ac7354511457417e9a34dc0bfc475c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/page.html", + "size": 1080, + "sha256": "d3ec6167d57d4160ae9457cc0a2d071bb98570647e6048c2e61a1749b3f8d22a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/search.html", + "size": 812, + "sha256": "b93aab4926b0fe6e6b46900a6b3c69a41ad049273a3acd2b21b2a726706abaa4" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/single-with-sidebar.html", + "size": 2846, + "sha256": "3615bf526bafc0dfd2af847005cf8f395f8e5669dafd23f9e9895e033f8219db" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/templates/single.html", + "size": 2468, + "sha256": "080efd72dacd507a3f353c5b275d413ad91c48f6240d89e00f132a8dd1a964ca" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/theme.json", + "size": 22273, + "sha256": "3d7be217a7624ff6705d4886232ae7762e6a06496f550dd2624ea48c99da918a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/parts/comments.html", + "size": 66, + "sha256": "517ed8d1d1ea776d7a70975a174cc16c06597fc4a4ab78c94095e8b62a6498fa" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/parts/footer.html", + "size": 65, + "sha256": "349c109ebcfee80614d03c23e5e9b193ba69b6f74ffd62277a195cc59ea82eda" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/parts/header.html", + "size": 687, + "sha256": "3232b989d2e19ea402e4479bdf893523c6046067b3677af91905bd034a5f29c2" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/parts/post-meta.html", + "size": 60, + "sha256": "ea9b98bf19adf383bb0bd7864b321d5cfc9806f9f18b60918dff6a8ea0c50432" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/patterns/call-to-action.php", + "size": 1361, + "sha256": "1dcb28b464d27ba7b3fa5a6167f368fd777ad22c800fd99ce75726c3841979d2" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/patterns/footer-default.php", + "size": 986, + "sha256": "59a3bcfebe4b1936f00a0ca3d2403cf46fa372a6b57f95710a7053c9a6453fbf" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/patterns/hidden-404.php", + "size": 1414, + "sha256": "6450397011504abe6b52be9e3b83c3aae575d806794c581bf1adb3ea9434460b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/patterns/hidden-comments.php", + "size": 2140, + "sha256": "74800aec2e23851f9b02bf647a12e257d97eacdf9c6f5e3ef5ef4ae6158f4fac" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/patterns/hidden-heading.php", + "size": 444, + "sha256": "937bc13480b97498df7f031b5ab8230ba2749904c89dce813e5dc55f40bb9459" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/patterns/hidden-no-results.php", + "size": 711, + "sha256": "6bec17c35509da7a52872f82904d9493fc2dddbf2064dce6d4833199d0ba8126" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/patterns/post-meta.php", + "size": 2729, + "sha256": "d68358f9d776d001c571b9339c8f718bdb157fa779d7952945b242e6d3e161f2" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/style.css", + "size": 1114, + "sha256": "be56f896f47ac1ed7de596bbe41df522ddea8f19b89a6236a85abe6a69796638" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/aubergine.json", + "size": 6045, + "sha256": "244cf52824b71ec7addd728219b3c20d74d77b3afea0e9537579e095820d5901" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/block-out.json", + "size": 4368, + "sha256": "790d4b8753687db179925a99f5e12729925722d6e4ffc94f1428baeb078dfe3a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/canary.json", + "size": 4593, + "sha256": "b8c6aaaaef656eb23aeeabfb3a18aabdc77384c81080bf8a1bb2b42bdda24054" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/electric.json", + "size": 1873, + "sha256": "dbb58ea5d1ed4639e763f94d4f9cbb8e51ab3b30956922444dbdf273f8fe2721" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/grapes.json", + "size": 1752, + "sha256": "562c31e56ec9759bc9d8c79eb5b96e7737d097d55a72e74e6c45489d5c029c95" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/marigold.json", + "size": 6135, + "sha256": "46b224d98303891ef6958738e425f9543348b33f25d2e65d655dda20ff4f3561" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/pilgrimage.json", + "size": 6530, + "sha256": "14f49c815dfe99fcb2f86ab3d083c71eedfd03706e08768a453b5a6baf1757ce" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/pitch.json", + "size": 4756, + "sha256": "0bc0d30dd1bd68a579e987d539ae45dd984b4711e6946737bb2af0a0ae7076a7" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/sherbet.json", + "size": 5286, + "sha256": "c0af2c4f62bdc44a235597404b97f3249b99e6c3cb940790105f553e0348a49a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/styles/whisper.json", + "size": 11857, + "sha256": "2d685f7f3d6b4d1b8e545fa7a0f406861b9827a071b5b6c2fcba1d6c5272347e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/404.html", + "size": 318, + "sha256": "b648b2f51ff02e8a93545d120ab9f7089f9b03a93fd40f07207b597e50fa2b3f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/archive.html", + "size": 1625, + "sha256": "9444e37f43b3bd5820902e6f3cdd746a4b1adcd5040f91732bb8bf45144eba75" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/blank.html", + "size": 60, + "sha256": "64af1ec149b4d3bd72fdbcdfe1fffa43c34ee802dfd6d8491b6ad5e0819ccdb9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/blog-alternative.html", + "size": 1502, + "sha256": "58b803d8108e21e0276986e6676e28ab1de1ce1555d7fb431283e96c1b0d2660" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/home.html", + "size": 1828, + "sha256": "3527ee11a4a50d500d91259716499ccd9b4b37f36ee5d1aa43c1f7169f5b0acc" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/index.html", + "size": 1366, + "sha256": "a5012ea74a897aca0cb6232141be02cdd4fab27e452bdfa3e7af380f8fa8eb35" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/page.html", + "size": 890, + "sha256": "9e68746c73cc488d9881e60da7b6181baaa9b45dd1ede0b07cd8672aefee8ec4" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/search.html", + "size": 1767, + "sha256": "55cc9da8eef1e4560f38707fe25195b918a70182a593561e83d2f6d3e0efcec3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/templates/single.html", + "size": 939, + "sha256": "1e3975f67a2368121ceb06b0e5ec748db581278da3f5b5325271da0462bd6e0e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/theme.json", + "size": 15058, + "sha256": "541f7c63588bf4701357a725075c1b23556fbc29142e19ac6303c8e57168e580" + }, + { + "path": "wordpress/wp-cron.php", + "size": 5617, + "sha256": "dd1dfcbc2a02ff16ce5ccf878d4f3d2ad9f5b762e2d1fc39ff27a073ae9a8a53" + }, + { + "path": "wordpress/wp-includes/abilities-api.php", + "size": 24369, + "sha256": "a895a4dfbe5c3562dd8a19f084e92e479927e7c433003dd79cad9dc9497ac835" + }, + { + "path": "wordpress/wp-includes/abilities-api/class-wp-abilities-registry.php", + "size": 11793, + "sha256": "fbab5c7ac9d0f16fe6ff17b0f576a36b2f03a826119cdbf4e4564fbd3d46b0b6" + }, + { + "path": "wordpress/wp-includes/abilities-api/class-wp-ability-categories-registry.php", + "size": 7496, + "sha256": "f034fd22df303317646189850940c45209d09818ae2034e1d525ab01edeecfda" + }, + { + "path": "wordpress/wp-includes/abilities-api/class-wp-ability-category.php", + "size": 5900, + "sha256": "dbe6541a4be06712655d84727d56681ed70c80062d558b6287e1e4894cdaa971" + }, + { + "path": "wordpress/wp-includes/abilities-api/class-wp-ability.php", + "size": 23430, + "sha256": "7c094004a3f8f88fc5986262782c6a3bd588adbe6ff01599bc1d0ac5be206f5f" + }, + { + "path": "wordpress/wp-includes/abilities.php", + "size": 8009, + "sha256": "70bc70d70cd02b9e4d0daceecb90ade211f677b23ba58a950dadc9c4e65cb961" + }, + { + "path": "wordpress/wp-includes/admin-bar.php", + "size": 39315, + "sha256": "e4833e8720bd47275ffbf80d55416c8224ea9f49c22fdd36c6a845a1c978b1bb" + }, + { + "path": "wordpress/wp-includes/ai-client.php", + "size": 2549, + "sha256": "20dea40ca6d32079b28e17c671abf7943e472ff5410b7f04beb23030348d350f" + }, + { + "path": "wordpress/wp-includes/ai-client/adapters/class-wp-ai-client-cache.php", + "size": 6066, + "sha256": "d7be5d691f2c74ef1ef48f841bb3e5794a2bd5894d2af906feefde37aa52e8a2" + }, + { + "path": "wordpress/wp-includes/ai-client/adapters/class-wp-ai-client-discovery-strategy.php", + "size": 1167, + "sha256": "74d19142b3dd8426cd5b2fe9e1a9d8d1489e5f4210ce2123404cf284dec8a3f7" + }, + { + "path": "wordpress/wp-includes/ai-client/adapters/class-wp-ai-client-event-dispatcher.php", + "size": 2555, + "sha256": "47b1bb193255ab0d9c75f25bfb53cc667618ef4f8462904964c01d0f85c909d6" + }, + { + "path": "wordpress/wp-includes/ai-client/adapters/class-wp-ai-client-http-client.php", + "size": 6703, + "sha256": "06f0b8c3c3386f57a42caf6e394888903dfec6e3981e12dfbcf31bd17d8a8cef" + }, + { + "path": "wordpress/wp-includes/ai-client/class-wp-ai-client-ability-function-resolver.php", + "size": 6265, + "sha256": "c501b4f306525b89b1b84849d85c276389478e9a0951bf34a750cfb607e08985" + }, + { + "path": "wordpress/wp-includes/ai-client/class-wp-ai-client-prompt-builder.php", + "size": 19787, + "sha256": "5f4262e964cf3331b936317deb1fccace990f63b32451f97b3b6fff59636c760" + }, + { + "path": "wordpress/wp-includes/assets/icon-library-manifest.php", + "size": 10521, + "sha256": "4226c76915f22de22d432b8bea17c02244d0f8991fd59def628d640c57b4fe2b" + }, + { + "path": "wordpress/wp-includes/assets/script-loader-packages.php", + "size": 16701, + "sha256": "5a8d16f42c7f38c017bf94bf9b5b668a7990ba3924cceb02557482a70063c82d" + }, + { + "path": "wordpress/wp-includes/assets/script-modules-packages.php", + "size": 6034, + "sha256": "0e05f3d3cee14d468bfd4c0e9c7a5a0535636d3266f34ca27a26e493d7a10c1a" + }, + { + "path": "wordpress/wp-includes/atomlib.php", + "size": 12181, + "sha256": "6ef9c762078cf9925f6c9068442edbf9f7e4c5ba7283abf3bae49d0c0ea07e4a" + }, + { + "path": "wordpress/wp-includes/author-template.php", + "size": 19844, + "sha256": "66f27ee8be733b1c0fb94fc6293a9bc130fd02db55eb40612a052471bfd33e10" + }, + { + "path": "wordpress/wp-includes/block-bindings.php", + "size": 7526, + "sha256": "4616d6540edd946eb8e7bc39008789678f2223030f0ab5b288579478de738b9d" + }, + { + "path": "wordpress/wp-includes/block-bindings/pattern-overrides.php", + "size": 1523, + "sha256": "720673322994b4938e6c3577f2a10868a681075d99be49dacd20f663f1da1ef7" + }, + { + "path": "wordpress/wp-includes/block-bindings/post-data.php", + "size": 2833, + "sha256": "fa9d68d5e5652b9c0b1a16fa64ba2fc7c9116c6dff1f6f116fd2e88c57a87fef" + }, + { + "path": "wordpress/wp-includes/block-bindings/post-meta.php", + "size": 2087, + "sha256": "a04abdceccb08c5a246a50aaf3977538572ad6d7b6d1f17510e929e650e37b67" + }, + { + "path": "wordpress/wp-includes/block-bindings/term-data.php", + "size": 3139, + "sha256": "fd5e5188475cb2937522932a7692c26e82c5c62f0491eea760c6106fc307b7f2" + }, + { + "path": "wordpress/wp-includes/block-editor.php", + "size": 28724, + "sha256": "e54bdf877e542e5d387a80123f1fae4a5f8562b4c4aa00f9037ababbc8b9c707" + }, + { + "path": "wordpress/wp-includes/block-i18n.json", + "size": 316, + "sha256": "765254e716d4d50a6b82f04604cc582fcd33c8566841776509bc16e3e737c8f7" + }, + { + "path": "wordpress/wp-includes/block-patterns.php", + "size": 15606, + "sha256": "50d76e2bb2f0a917a0fc15e95c10b44ad435a9e83399bfd7abe937cfbcf9d2c9" + }, + { + "path": "wordpress/wp-includes/block-patterns/navigation-overlay-accent-bg.php", + "size": 2266, + "sha256": "459a7a885889a114c7c1c020b4ffac4d937fca1a702a7a958e30ed5148062a57" + }, + { + "path": "wordpress/wp-includes/block-patterns/navigation-overlay-black-bg.php", + "size": 1994, + "sha256": "b40e6938ddcca309ef622ffaac751c42c1df574b640a5dae1517b2a52f7406e3" + }, + { + "path": "wordpress/wp-includes/block-patterns/navigation-overlay-centered-with-extras.php", + "size": 3349, + "sha256": "c46e7d059c2a2cd8e033b351eb4784c559c64894ac380dc075896ca9df2c951e" + }, + { + "path": "wordpress/wp-includes/block-patterns/navigation-overlay-centered.php", + "size": 1666, + "sha256": "ecd33f521a61c0996ee5235b147f6dc5d962f7ef8920f9ae445dd996153a771e" + }, + { + "path": "wordpress/wp-includes/block-patterns/navigation-overlay.php", + "size": 1378, + "sha256": "df918f3b9678a4c2283f4116d42337e937ac36387a233807b1e8d168a150aec8" + }, + { + "path": "wordpress/wp-includes/block-patterns/query-grid-posts.php", + "size": 972, + "sha256": "da43e9eb00bb76b8ab7cafa66666ddb29c79335a6545be139da317be1f9710b3" + }, + { + "path": "wordpress/wp-includes/block-patterns/query-large-title-posts.php", + "size": 1987, + "sha256": "6935a26c64f4a5b62c19d62d4ccd65cedc9042e48a1ed9b501602716b292bcec" + }, + { + "path": "wordpress/wp-includes/block-patterns/query-medium-posts.php", + "size": 1053, + "sha256": "6d43db0b76176e6772d62607599392cc4295de56f7f8c274ee90b8e6e4bcd61d" + }, + { + "path": "wordpress/wp-includes/block-patterns/query-offset-posts.php", + "size": 2004, + "sha256": "9b08f8f52b0794b08a44b1ab34620fd42a75356cd215db03c300eb04ea335e0d" + }, + { + "path": "wordpress/wp-includes/block-patterns/query-small-posts.php", + "size": 1176, + "sha256": "09d429733d7b4588e8949d5189294629f0b51d885d39c3a95bd38231ba2e1133" + }, + { + "path": "wordpress/wp-includes/block-patterns/query-standard-posts.php", + "size": 808, + "sha256": "ac705b71629fadc73546e5524c65b42dc48fa48dccabd96589822f7c0cc053fa" + }, + { + "path": "wordpress/wp-includes/block-patterns/social-links-shared-background-color.php", + "size": 951, + "sha256": "70e790aedaf84b7d9492b96514cab108534d84cb992b9b22c2613beb06abf5f7" + }, + { + "path": "wordpress/wp-includes/block-supports/align.php", + "size": 1708, + "sha256": "53e6288ee926d137e95cc8dc54526a51377b9198519804bfa688e03fa9d0d3f4" + }, + { + "path": "wordpress/wp-includes/block-supports/anchor.php", + "size": 1574, + "sha256": "bdeb2d51e90c5161f3411337dfad0578711e94a5d01c81cb59a0b7e6050c7379" + }, + { + "path": "wordpress/wp-includes/block-supports/aria-label.php", + "size": 1691, + "sha256": "b28339601e763f89fde2c6e0ad970f82d9b669b22249f57c451b4a8b27686571" + }, + { + "path": "wordpress/wp-includes/block-supports/auto-register.php", + "size": 2233, + "sha256": "49f792670c6ed62e3cd1e76744097ecf05bb438ffe4795e27e040822deabb65b" + }, + { + "path": "wordpress/wp-includes/block-supports/background.php", + "size": 4145, + "sha256": "5cba71703558ebe0b205311aeb7814bc6d6ebb93b049038dfd6202057febfd92" + }, + { + "path": "wordpress/wp-includes/block-supports/block-style-variations.php", + "size": 9501, + "sha256": "69c84071d5446647e7be07e627bd13bd268b146af1d03daa790cbb6e57d51605" + }, + { + "path": "wordpress/wp-includes/block-supports/block-visibility.php", + "size": 5651, + "sha256": "0d8dafc534009ba5c71b680f5dc9f8692e90fa5fefb5eddcd5066c26fac5aacf" + }, + { + "path": "wordpress/wp-includes/block-supports/border.php", + "size": 6248, + "sha256": "91d0bd3e7fd1b7979907a4ca6449909824e9867a3b2c5b9bca286dace5252290" + }, + { + "path": "wordpress/wp-includes/block-supports/colors.php", + "size": 5516, + "sha256": "189eac06f4a073238a0ba91f05a6f5fe4bb3ec5d302c98888e31dd59a9f2ae49" + }, + { + "path": "wordpress/wp-includes/block-supports/custom-classname.php", + "size": 1675, + "sha256": "bb207e09c16db915dbdb6362b035dd9820086cfeccbb316085a6d5d2057e05f1" + }, + { + "path": "wordpress/wp-includes/block-supports/custom-css.php", + "size": 9594, + "sha256": "4e3815a5b3f473466159cfdee6b26a0eb4aacc59027363e277ae152274f185a6" + }, + { + "path": "wordpress/wp-includes/block-supports/dimensions.php", + "size": 5337, + "sha256": "686dfeb2180bda72ad7eea6e509642aeb08c8a292cb324eb2d29d82435f10ec0" + }, + { + "path": "wordpress/wp-includes/block-supports/duotone.php", + "size": 2731, + "sha256": "f636514161fc4c2f41ca24762f800c977e40f640158c7a0bdb307c2e3cf31c29" + }, + { + "path": "wordpress/wp-includes/block-supports/elements.php", + "size": 8395, + "sha256": "6055609000f6c1ed7617ecfb4c542934111465a6529b893de214ffd36568ffd4" + }, + { + "path": "wordpress/wp-includes/block-supports/generated-classname.php", + "size": 1745, + "sha256": "bf7de3924cf40b0301998596f3515ad6a76c2e341d0760017b4748b636d0580e" + }, + { + "path": "wordpress/wp-includes/block-supports/layout.php", + "size": 43328, + "sha256": "eb0dc2c4b42a02b330c143f22b867eec1061d6e1291839bb749c80c9d85547ff" + }, + { + "path": "wordpress/wp-includes/block-supports/position.php", + "size": 4110, + "sha256": "bd0f9f0fb75b95550120ad2aa4a5df6b2e2c926e4b9fff2d6ffe4756c2c7ad70" + }, + { + "path": "wordpress/wp-includes/block-supports/settings.php", + "size": 4553, + "sha256": "057888fc6de1176286f647a5bdb43d8176625a90771b1ac4feccbb26d7df78cb" + }, + { + "path": "wordpress/wp-includes/block-supports/shadow.php", + "size": 2093, + "sha256": "efccedfe607d11f9187afd21c44c719a04d5c59b8d06ad1734f871ed0c406487" + }, + { + "path": "wordpress/wp-includes/block-supports/spacing.php", + "size": 2748, + "sha256": "02031d01a6da90f80ca13f31b004a9e7f7953047200534b522651fd82d1799d3" + }, + { + "path": "wordpress/wp-includes/block-supports/typography.php", + "size": 28341, + "sha256": "3c653bff75da4cd9df545abc2b96db649829ef64ba30eaf1ec2f16507234d988" + }, + { + "path": "wordpress/wp-includes/block-supports/utils.php", + "size": 1011, + "sha256": "76839eaf383863f38f0f228e151ab1dc27bac78fe132d2e2fdade7c311358d1d" + }, + { + "path": "wordpress/wp-includes/block-template-utils.php", + "size": 62804, + "sha256": "64f6ff0bd2290d326ec8e4abe7ae4469698d190db654a0f778c8d905c9eaee8a" + }, + { + "path": "wordpress/wp-includes/block-template.php", + "size": 18261, + "sha256": "de31eda1f6ee3dab26b60a29414cf07c89525767b7a166d52f9d5a807ec905f5" + }, + { + "path": "wordpress/wp-includes/blocks.php", + "size": 119442, + "sha256": "b8b44cb18d6ae7526a36fd3a5fd08c411f3af6c07aba85b3feb34563ed0ad321" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-heading/block.json", + "size": 2175, + "sha256": "c1e16410d9dc79b5d10e03e08d30d514bf287fd351b0a55e154f17d94388fcae" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-heading/style.css", + "size": 883, + "sha256": "a7a23769f84f139b5f2b84ee716ff217760a1d816915ec94ae30c6a2813a70dc" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-item.php", + "size": 3041, + "sha256": "5c8278c2aabe2a4d88dc853087a7fcb982215f4a5a807d0791c865c948f53322" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-item/block.json", + "size": 1421, + "sha256": "4343382a05fa00cad01d94bf94af936062edbf126634eba3c6a6e1ff36aa0847" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-item/style.css", + "size": 518, + "sha256": "9ef016b8b75a286b71f67c00fc5d643442b92b1bc31d86f57ca9cec710030c80" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-panel/block.json", + "size": 1557, + "sha256": "360df412acb5b595f359c29455b485c82ae3f29fb55ed4276189f5b487da0d24" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-panel/style.css", + "size": 123, + "sha256": "f037519fb0b84e529070cec8678c6be0b058ed429d2a746d0c3e2cec4be01501" + }, + { + "path": "wordpress/wp-includes/blocks/accordion.php", + "size": 1147, + "sha256": "a0daf25c479636b5017a5f9cd05fcd5b624412cf4dce39c393e1c0c9b4890b73" + }, + { + "path": "wordpress/wp-includes/blocks/accordion/block.json", + "size": 1979, + "sha256": "dc6a77ce09c82d9a17cdbd739397484a98da57ef3ed4df3467486e42ea1fd9c7" + }, + { + "path": "wordpress/wp-includes/blocks/accordion/style.css", + "size": 49, + "sha256": "7282db043a7881206ee3ff55e440181b39548156287f7bd17d686fce4296b92f" + }, + { + "path": "wordpress/wp-includes/blocks/archives.php", + "size": 4385, + "sha256": "e8a615ea53411d1d80e7b884369f92c02a1f44a1e6a8b3eca4ae8d90a0e9f01d" + }, + { + "path": "wordpress/wp-includes/blocks/archives/block.json", + "size": 1411, + "sha256": "2ef40a464f58a1b8c2db3bd26d2103dfc8aec9bec4d0a66b1e79f105fda1e957" + }, + { + "path": "wordpress/wp-includes/blocks/archives/style.css", + "size": 105, + "sha256": "1e10558c6ca95a24bf9f3ee9c544d45a4953ddb4b121d1a10d5a863b0d3062e3" + }, + { + "path": "wordpress/wp-includes/blocks/audio/block.json", + "size": 1327, + "sha256": "c7dea3ffa058e20e43c5480fb98823c94f12b527689b89db4eadec73a596d60c" + }, + { + "path": "wordpress/wp-includes/blocks/audio/style.css", + "size": 1927, + "sha256": "7da2a5ab63b861de257b720f77b4609ddbacfd39a03f4fb6e6544867b1fe1f01" + }, + { + "path": "wordpress/wp-includes/blocks/avatar.php", + "size": 5634, + "sha256": "e62ace0db05e8594e54c9543bf890571b41076964cc55abe95b6134247ed51f3" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/block.json", + "size": 1287, + "sha256": "a33fa4d59ed36493ef740032868db73f4d5de50733f49a84c1bfae41f18ad86a" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/style.css", + "size": 170, + "sha256": "55788a5518dee794a80ca8f0af7ad7de0e45973312536823905b6425a1e2f64f" + }, + { + "path": "wordpress/wp-includes/blocks/block.php", + "size": 4186, + "sha256": "ed0a6fa50e80c39d93433cd2970e6f3387c803f95cac343708d6285806e12bff" + }, + { + "path": "wordpress/wp-includes/blocks/block/block.json", + "size": 609, + "sha256": "d5be7dc53e9ff71dec02b2600ec3b84e8015722b06f698e90c9bdeb08682becb" + }, + { + "path": "wordpress/wp-includes/blocks/blocks-json.php", + "size": 199238, + "sha256": "6d8ee22f03c47c64189cc1bf7fae346d6bb7e23eb87eace33999ffb88e305a6d" + }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs.php", + "size": 19057, + "sha256": "183fe241f040dbfe49302c2453f8d077dc6cad011431cd0a2551a0010220f2dc" + }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs/block.json", + "size": 1634, + "sha256": "376fd7ec4814fa13c6dc63f8befeab3b66e5a766577d825daa97b682feddc9b1" + }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs/style.css", + "size": 453, + "sha256": "641a6aab4c35378f103949a709e62295adca302aa911f953ff84525a2f071345" + }, + { + "path": "wordpress/wp-includes/blocks/button.php", + "size": 1752, + "sha256": "2dc5285f2c31e3407b62df1de8aea78c1f545200b1898b650ca452e541cb0f3c" + }, + { + "path": "wordpress/wp-includes/blocks/button/block.json", + "size": 3128, + "sha256": "e865e767ce9f22c92648cb42e8c4de518cd30c8afa0a05e1ac3be5a8bb31577d" + }, + { + "path": "wordpress/wp-includes/blocks/button/style.css", + "size": 2536, + "sha256": "c7e252900f3f4fbeb86fe61a3dbc353a4ab822d8d12f778e8abbaa9e4b77f4b7" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/block.json", + "size": 1633, + "sha256": "c8d119c01a6e222271686923813ac537e6a57e8f25c4d884e39b70e0d8bf0c2a" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/style.css", + "size": 1734, + "sha256": "4125178351ab6d5d5fa8c8c2aefb8852542cf9b37684aa8c0a11d039d58ab7d2" + }, + { + "path": "wordpress/wp-includes/blocks/calendar.php", + "size": 6075, + "sha256": "3c799ca830852934eb8e4779c328459756cc283cff7fb43da695473b51310926" + }, + { + "path": "wordpress/wp-includes/blocks/calendar/block.json", + "size": 1063, + "sha256": "714a902598f6e55269e6aef94f572a1f9d445c2e172a398b259415ae4526aea6" + }, + { + "path": "wordpress/wp-includes/blocks/calendar/style.css", + "size": 770, + "sha256": "b6b70363351519ccf5b7a50f0f2114d23118930a1d0e7d75bc58327cb0bfa33a" + }, + { + "path": "wordpress/wp-includes/blocks/categories.php", + "size": 4938, + "sha256": "b7cb718e6291fde0e467d1cff89b89ce092ca8ebf7ef1332ac65ef732313026f" + }, + { + "path": "wordpress/wp-includes/blocks/categories/block.json", + "size": 1966, + "sha256": "57cb1effb888c776747488f25061097fefe4081677fc79b9dcc077ea5e2cac92" + }, + { + "path": "wordpress/wp-includes/blocks/categories/style.css", + "size": 479, + "sha256": "89c1882329fdd85ed5bc4b7d918fa71603e0c68b9790ab6904310405cb5fda34" + }, + { + "path": "wordpress/wp-includes/blocks/code/block.json", + "size": 1426, + "sha256": "b09486298523a4d2299c92b266cf4b5592e54b9de1024c60477e47ebb5fa011d" + }, + { + "path": "wordpress/wp-includes/blocks/code/style.css", + "size": 251, + "sha256": "956a519062fac48120fbbc2aa64481f28ea9129d32fc094761ebcf182cf20a10" + }, + { + "path": "wordpress/wp-includes/blocks/column/block.json", + "size": 1616, + "sha256": "08e45a5eae95aa8469dec57845b49e3b46414d59f11506c9575d6825b18ac614" + }, + { + "path": "wordpress/wp-includes/blocks/columns/block.json", + "size": 1977, + "sha256": "7d93c8f8643ce66aac6a4a4f883e98bbf48a205a06e6d39aa3ccd7704e9ce608" + }, + { + "path": "wordpress/wp-includes/blocks/columns/style.css", + "size": 3649, + "sha256": "a8aa7eeeea6aa93b4e93dc27dbd834692851fa5487d6e48d60ca567bf316f4e4" + }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name.php", + "size": 2133, + "sha256": "8c9f447c7949cbc8b11b84a438fef70d0e02b69d60a6f95a306c527972b8ca56" + }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name/block.json", + "size": 1457, + "sha256": "dd64754200ff448a1438a444e96204349321627dd9ce48288b7d01c63ad99b2b" + }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name/style.css", + "size": 59, + "sha256": "3f21ab638df66e871cd971f3c79519bb44a106d14d12d74cbb7a9bb3099e0aeb" + }, + { + "path": "wordpress/wp-includes/blocks/comment-content.php", + "size": 2459, + "sha256": "5f04299de24f7829d1cdef6ab1af9e6d48c26029793d59a52800775b7c16ba5a" + }, + { + "path": "wordpress/wp-includes/blocks/comment-content/block.json", + "size": 1284, + "sha256": "76a358bf31e1ece76adc9a3495acbcc653340f53206310c5e499026c11fb979e" + }, + { + "path": "wordpress/wp-includes/blocks/comment-content/style.css", + "size": 149, + "sha256": "59c3178a1a5263525975714af4886e8318d955bf4612a611ea75a35d11dde473" + }, + { + "path": "wordpress/wp-includes/blocks/comment-date.php", + "size": 1863, + "sha256": "40259d8a76ca5ef065be6e78688211c81b8a8e715455f6dc7309e333ee71b18f" + }, + { + "path": "wordpress/wp-includes/blocks/comment-date/block.json", + "size": 1412, + "sha256": "421cab4d6d6bf7c0925a2b192afd25b8b4aaab472e59354c78910eac98f78ef9" + }, + { + "path": "wordpress/wp-includes/blocks/comment-date/style.css", + "size": 52, + "sha256": "bb31f3c69fa418fe892f0039d90fae36e6046d0346cca3908ac0c13ae6882b2d" + }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link.php", + "size": 1713, + "sha256": "6cf03842644cf0e493bd1fbb22690a1de1cfc01523bd3059ee8b296f5b6b4080" + }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link/block.json", + "size": 1442, + "sha256": "6149b9801b1ee80e8e6043457fa0bb2e95cdf6ba35e3342a7e089115b68c1e58" + }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link/style.css", + "size": 57, + "sha256": "fa5b8b36e43feb48da7e58ed1d9ccb447bc6d0934c5f95ec6e6b90b4e61cbe98" + }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link.php", + "size": 2075, + "sha256": "506f7f0f97d376893978747b52e2b4656769eb05055c8ed84bfd1d05a0da6b5f" + }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link/block.json", + "size": 1211, + "sha256": "50ae3ce36e011f4f775c36ea570ecdf2088bf80dce0d7c9c172deb775693286f" + }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link/style.css", + "size": 58, + "sha256": "a4b0205f164113f04bcbbd283f738b2f3a7613dd8543b6fed9c2c51b359ae4b4" + }, + { + "path": "wordpress/wp-includes/blocks/comment-template.php", + "size": 4494, + "sha256": "657f23eb8f6923f2398931f56f3666ca59ee13ffde47d42bbd679634f19e4186" + }, + { + "path": "wordpress/wp-includes/blocks/comment-template/block.json", + "size": 1201, + "sha256": "860be0857f206676cf4a261b53b4aa8c81bacb1846bb32034748573aa7f781a1" + }, + { + "path": "wordpress/wp-includes/blocks/comment-template/style.css", + "size": 515, + "sha256": "94087a95e0e1d116a9927da11fef35c99fa0a07292071d8c35a73db8228e7377" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-next.php", + "size": 1924, + "sha256": "d072d7308cff37329f841f2a72af1ff21d31f1b6d401b78013e3ede7085956c7" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-next/block.json", + "size": 1029, + "sha256": "adce36082a9168edac1b95881663b1b5821a485c1c1253f1eb5cf02474657797" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-numbers.php", + "size": 1632, + "sha256": "6169f05473f510f93d68ab7a74dc45a9371da168f099dd87b9cbbc0179deedc7" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-numbers/block.json", + "size": 1089, + "sha256": "2f8fb03b8eabcc1627e1199c597cc98e3a29a7d940e227af75658bfcc55851b4" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-previous.php", + "size": 1790, + "sha256": "72f420661d76d9b3c31173f5b92931c5981edc1074ea41be42c10e4af7bfc7c0" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-previous/block.json", + "size": 1041, + "sha256": "35fd1e505fd6116a9014cbd006effd4cce02fce94e1604fe7b17f4bf9241d883" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination.php", + "size": 1261, + "sha256": "42f072d7d8de8925d435fd876b6642a2194cc46128a2a39205ddfdf62902055e" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/block.json", + "size": 1575, + "sha256": "bab20f184d9770cd46cb6827ccb409f17d8acaabef2bbdc88a7d74b3680f206e" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/style.css", + "size": 841, + "sha256": "99240f8ea0cdf3ab264dbf0c23970a40cb1b4c96ad46ec3febe15a2e0b9e39a8" + }, + { + "path": "wordpress/wp-includes/blocks/comments-title.php", + "size": 2787, + "sha256": "5ef75869cb2d0838f645242cb6e3618eddef94cb4d73fb0233f0641dbc2520c0" + }, + { + "path": "wordpress/wp-includes/blocks/comments-title/block.json", + "size": 1490, + "sha256": "e847288c0ffd7294e5e59f93ebe014a703b6edc08337f0b3b60e84564f31e6e0" + }, + { + "path": "wordpress/wp-includes/blocks/comments.php", + "size": 6784, + "sha256": "6df96404f63d9fee3523f586a3d629542c67d223c967e22272b6afb3b299ad2b" + }, + { + "path": "wordpress/wp-includes/blocks/comments/block.json", + "size": 1421, + "sha256": "f169878850b95af5834d2522620f21beeb13c06124d45517e8cef5251a0aea36" + }, + { + "path": "wordpress/wp-includes/blocks/comments/style.css", + "size": 2882, + "sha256": "1dae5a6ac721ee55340aa269746185ca245984395026ec47c5956ef428474656" + }, + { + "path": "wordpress/wp-includes/blocks/cover.php", + "size": 7422, + "sha256": "892a1c67e8c38d8a8eb7f2377159f310a235da40106116cae635e78ef1f52416" + }, + { + "path": "wordpress/wp-includes/blocks/cover/block.json", + "size": 3039, + "sha256": "b72e63a53251f35cc9d6bb048d2824109cd4e4a1f6212ba03b81291b1f3c6e2c" + }, + { + "path": "wordpress/wp-includes/blocks/cover/style.css", + "size": 22791, + "sha256": "83076b1c83d5a153b7e78f0b09de8a0814870c0422fee85ee50b118637bde467" + }, + { + "path": "wordpress/wp-includes/blocks/details.php", + "size": 1498, + "sha256": "fada6713631f6f65875b29dc8e3dae9188e5f50d14185fa05d77dc8975d4c0d2" + }, + { + "path": "wordpress/wp-includes/blocks/details/block.json", + "size": 1711, + "sha256": "24232f6ff091aec0243e2da999ed1ee1ad8ad8d3fbb3d091cb0141e5cb80816f" + }, + { + "path": "wordpress/wp-includes/blocks/details/style.css", + "size": 97, + "sha256": "b6de946e5946f6c0b65a3fd2fbf0253cd8b58b713cbf3000cd630b48c39e9ff1" + }, + { + "path": "wordpress/wp-includes/blocks/embed/block.json", + "size": 1054, + "sha256": "adbe144e10b11413126fd6bd59337c124a081ef4da379715a1532c1ea4505b13" + }, + { + "path": "wordpress/wp-includes/blocks/embed/style.css", + "size": 3618, + "sha256": "10aac666cfe9be0692e7cb14e1178b86902d93350c66dce6092eb24755a6c8f6" + }, + { + "path": "wordpress/wp-includes/blocks/file.php", + "size": 1879, + "sha256": "5ac151e0d57a9176940c3f87c1b9f026fe29bf05d72650eb5e19c91a9a99d6d0" + }, + { + "path": "wordpress/wp-includes/blocks/file/block.json", + "size": 1880, + "sha256": "556a6d9380fa2de72ef6a8f4451b85aef249676fc8bb9d1c30db5e5addb98c53" + }, + { + "path": "wordpress/wp-includes/blocks/file/style.css", + "size": 792, + "sha256": "c73d18ee55e8bcbb3984fe4df039f2bf1dd4fdadaaa4bab36bb71cc19506648b" + }, + { + "path": "wordpress/wp-includes/blocks/footnotes.php", + "size": 3818, + "sha256": "70f81f15a1b9f07d76d473fd7a598edc67429fa10cd9444e93c26c38e0415e8f" + }, + { + "path": "wordpress/wp-includes/blocks/footnotes/block.json", + "size": 1430, + "sha256": "2604dfb4b47045dd94edf3396b47401c6e6f0e7feba9067a220610975230a4e4" + }, + { + "path": "wordpress/wp-includes/blocks/footnotes/style.css", + "size": 342, + "sha256": "352c94210682bf52d3630c4165fec4c589e9a5425374139eb7a8a6519e327f5f" + }, + { + "path": "wordpress/wp-includes/blocks/freeform/block.json", + "size": 519, + "sha256": "f3b4aa36e4b63f9c435eff38b041edfd046faf8f12570ffd190f2ca6867d1372" + }, + { + "path": "wordpress/wp-includes/blocks/gallery.php", + "size": 9067, + "sha256": "cc4deda9f473dc40ea693e7e75f818abff95c7dc5cdfbcb8f3baf9dc271c02f8" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/block.json", + "size": 3361, + "sha256": "a5c552d96d35e7e67f45bf076566bd38f4b9ab294f3e08cec0b7c4bd7491bb8c" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/style.css", + "size": 19507, + "sha256": "41b3026ece5754f41a87ce6e70f5548cf818453ac9159ab81c28a2eb05ba6b51" + }, + { + "path": "wordpress/wp-includes/blocks/group/block.json", + "size": 2079, + "sha256": "b98ed4476f2f53248d3df4f718a006ddd011dbb62d2a996f87b19f148eaa9f00" + }, + { + "path": "wordpress/wp-includes/blocks/group/style.css", + "size": 133, + "sha256": "16464a7cf2a3e7dbc446e5b48f4a555c66f3b27022fcaf6f5f147efe383236d0" + }, + { + "path": "wordpress/wp-includes/blocks/heading.php", + "size": 1301, + "sha256": "12a912dfa0a34f0cd0b34a97deaf04db03355c202088fd5cd23623357722fa65" + }, + { + "path": "wordpress/wp-includes/blocks/heading/block.json", + "size": 1784, + "sha256": "de2b28fb6a00552e9c57cc03b0249cdaaf73d4da57bcc570c6a42f87fa59f06a" + }, + { + "path": "wordpress/wp-includes/blocks/heading/style.css", + "size": 1976, + "sha256": "2a1ad02383103c53d74e2cadd9d1f273b87eef0424ae9b8398481254a6400501" + }, + { + "path": "wordpress/wp-includes/blocks/home-link.php", + "size": 5438, + "sha256": "c430b7f825b11cc431052dcf44b7f65629ea7157329384cb35af7617b0f6f07b" + }, + { + "path": "wordpress/wp-includes/blocks/home-link/block.json", + "size": 1170, + "sha256": "ac3ca13caacde9841f58453cb5e8010420627e47c5aa1621cc985911a2796d2e" + }, + { + "path": "wordpress/wp-includes/blocks/html/block.json", + "size": 593, + "sha256": "08d6b98694e200e999b60558156038cb96ac56b4ddd431ce5bfdbe5686c9c43e" + }, + { + "path": "wordpress/wp-includes/blocks/icon.php", + "size": 4139, + "sha256": "adcda24333ddcf14c6ed3a2ec2893bfcc193448ed05e63d5d7f25e74007a2f68" + }, + { + "path": "wordpress/wp-includes/blocks/icon/block.json", + "size": 1447, + "sha256": "cbf6fa910e1e5690a188e6781d6c83b3f433c4ec74f7725064aaa672269deb64" + }, + { + "path": "wordpress/wp-includes/blocks/icon/style.css", + "size": 328, + "sha256": "b49f67ec6e13ade10bb859a1a954723ce0455d58d69d4188e1bf4de982ba0999" + }, + { + "path": "wordpress/wp-includes/blocks/image.php", + "size": 17672, + "sha256": "b310d49e5fe06cb9896daa92f8d5ea4f894cf6f752ed75e55f7aba833acbf709" + }, + { + "path": "wordpress/wp-includes/blocks/image/block.json", + "size": 3068, + "sha256": "b93fbf057972409133d39259123a6a2694c65c2071bdc10a59a4be7847ef737e" + }, + { + "path": "wordpress/wp-includes/blocks/image/style.css", + "size": 12485, + "sha256": "6d3a9dcc001736ed2e447e981ad97f1951e5083a5abb04e11a44f8cff847e109" + }, + { + "path": "wordpress/wp-includes/blocks/index.php", + "size": 5111, + "sha256": "2d814c8352559a1dafc7ee1717cf1de7cd0c396c5c2284423398d4601bee747f" + }, + { + "path": "wordpress/wp-includes/blocks/latest-comments.php", + "size": 5488, + "sha256": "c1212f5046fb70b9f4f4168fdf5b2cbf550476a83b8f5ab56e91633616996cb0" + }, + { + "path": "wordpress/wp-includes/blocks/latest-comments/block.json", + "size": 1395, + "sha256": "b97f5ea91e130e88fb3d6affc14d3c2c2990d41fd2336f096c55bd5d6e9f7745" + }, + { + "path": "wordpress/wp-includes/blocks/latest-comments/style.css", + "size": 1454, + "sha256": "ccacc36732e7a5b896f5e8408a857fb4171dfba86271cf3ecc8d8fe087fd4dc2" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts.php", + "size": 8843, + "sha256": "ad38e84dfbebe58bc4d1e3b3807167de4e67c17546f9ef98f9f5e5ca8bc47f3d" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/block.json", + "size": 2575, + "sha256": "d4829e6b82f35377378ee9448d504d46187b077d9ce15598d19d9f513132ff66" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/style.css", + "size": 4072, + "sha256": "44b5cd8579c4c24ddbcb1a6830a9c3d4366f4099b4574235304c9bc1850dd1bc" + }, + { + "path": "wordpress/wp-includes/blocks/legacy-widget.php", + "size": 4011, + "sha256": "60d2b63a888f03cc416d00356c25fae2bd90e62692462c60f685a6e28fcd6e08" + }, + { + "path": "wordpress/wp-includes/blocks/legacy-widget/block.json", + "size": 556, + "sha256": "30a6efdd317d6d830ccfe9f917585a80514d65f94e3a9f40ccb5a370b4708376" + }, + { + "path": "wordpress/wp-includes/blocks/list-item/block.json", + "size": 1471, + "sha256": "552d08e221626e4f7aba0485f385714501d19fc6b234f28ae9e28a9c56cd8886" + }, + { + "path": "wordpress/wp-includes/blocks/list.php", + "size": 1273, + "sha256": "05852008a63e7761cb7903c6308d9686ce4add4af1c8bab90d356cbe3fe1bc7e" + }, + { + "path": "wordpress/wp-includes/blocks/list/block.json", + "size": 1901, + "sha256": "25ed613d2f8fa322a2d93e631641cc6f4cc9bb1837a74adbf6ecc450e8e659f6" + }, + { + "path": "wordpress/wp-includes/blocks/list/style.css", + "size": 892, + "sha256": "d989fe25818d80ba8ade0c57d1fde3598db66204f62785cbcf2ba45c0862700f" + }, + { + "path": "wordpress/wp-includes/blocks/loginout.php", + "size": 1664, + "sha256": "c4d3636cfd1793a88c19d2a53b34d1c3da964e7f19f95203c2253145cc156cc6" + }, + { + "path": "wordpress/wp-includes/blocks/loginout/block.json", + "size": 1318, + "sha256": "6a7432bf153a2718d6fba527658857170ff15d6e6756e1516b027dc5fbeb21fc" + }, + { + "path": "wordpress/wp-includes/blocks/loginout/style.css", + "size": 48, + "sha256": "40e1b3c2a16921b461cc51fa77c58a00e824903744073ce9a1077420b6ae9633" + }, + { + "path": "wordpress/wp-includes/blocks/math/block.json", + "size": 986, + "sha256": "a6014ac32c09c1d68f70be29adf67e3b7b689b308df8a17c54cebffa29546ccd" + }, + { + "path": "wordpress/wp-includes/blocks/math/style.css", + "size": 60, + "sha256": "15bcbf060ae81ad9c9a9d6a0acb997fa914c515c98ad885d00101143290f664b" + }, + { + "path": "wordpress/wp-includes/blocks/media-text.php", + "size": 4298, + "sha256": "93539e243a3c81718884bc349e36ce01d7ec9984d240c91cf888ea86c2d026fb" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/block.json", + "size": 2928, + "sha256": "8bbf956f4de9717599dd21305cb4f0b717961cec075ba47c27b2f889d593dde8" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/style.css", + "size": 3529, + "sha256": "41f2006567e3d1194a50da59a37fd4dabfce2052c1bd26002bf53639ebafa9e3" + }, + { + "path": "wordpress/wp-includes/blocks/missing/block.json", + "size": 700, + "sha256": "85debd239ebd18c6cac4f01dae2532e1caf9b8950fbf0b8d1d381c4f847a9aa7" + }, + { + "path": "wordpress/wp-includes/blocks/more/block.json", + "size": 703, + "sha256": "99f9231441fafa79bbb8b1e482cf47f17c18033e3cac013a07503d8009988121" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link.php", + "size": 16405, + "sha256": "92d2ee3755ecc4bc395e669d61a7320ac540c070f2a7374552ae0d02901e4ecb" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/block.json", + "size": 1812, + "sha256": "a8914cbdd227ee91d158657ecc291c43a411fcfdeb34d4817afe84a262d829e1" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/shared/item-should-render.php", + "size": 1533, + "sha256": "0d201ace632f254baa129c3f702cd2d3b7e0e34f6337aa7b63929871012aac22" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/shared/render-submenu-icon.php", + "size": 465, + "sha256": "f82783dc60953740dd1c36aec572f537e4a85742f3312a4c8d972d18d3826d79" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/style.css", + "size": 1146, + "sha256": "2f6d2eb23297e0dba1dddd95c96dd64c1887747e76f2a6665a9579910a0b5431" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close.php", + "size": 1765, + "sha256": "a58f3a0d0143a5a19c5391d17aa537c023040c38dc48b6cc1633c2e6c36e7dca" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close/block.json", + "size": 1141, + "sha256": "fc91fbbe5a06dfdb1d1ad3cabf060d59ee5a603b68ad917d8937959d2b39a7c5" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close/style.css", + "size": 554, + "sha256": "7acd0294355c4196bf0d5f1634044e06400ad112fba41879768cd60d911c6a34" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-submenu.php", + "size": 12584, + "sha256": "af6a033fd8d0b6d7bb0d4c7f9a5f6782c279d445c2524f27b4d142ac24dc82ed" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-submenu/block.json", + "size": 1826, + "sha256": "10788e7533cc449af6e817eb01cfbdac57bb0b5e959658f48bb042564552289a" + }, + { + "path": "wordpress/wp-includes/blocks/navigation.php", + "size": 64446, + "sha256": "3401aa1bf4b2c21ca65ecbfab4a573f4e7b858c7a13cd1ee86ce14a21047ee11" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/block.json", + "size": 3478, + "sha256": "df7eeaa48bddd4986a4b76cc8e3e85ad24b29d06dbaec9122d69cad6db2a57ad" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/style.css", + "size": 24623, + "sha256": "1fbab5e859243cd1aa473124e829d79b5bfc14e0fead7c67626db9941e94be63" + }, + { + "path": "wordpress/wp-includes/blocks/nextpage/block.json", + "size": 554, + "sha256": "dffa28ec92eadcba753882479125249f163f95af1f541da72790e8c8017df1a2" + }, + { + "path": "wordpress/wp-includes/blocks/page-list-item.php", + "size": 361, + "sha256": "c14c8d9016249a325cd1c5ef6d864b62bd3297da6689b8f8613148591d0c64d6" + }, + { + "path": "wordpress/wp-includes/blocks/page-list-item/block.json", + "size": 1150, + "sha256": "430127d2e5e0dc1219d4b3a89f408731d60b06d27ae60dce66dec9b857d8adfe" + }, + { + "path": "wordpress/wp-includes/blocks/page-list.php", + "size": 15096, + "sha256": "9c42819d5c4f53286c4b918c3dce352278819d9cda9f223c79830c9f1b1c6c0e" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/block.json", + "size": 1786, + "sha256": "267146092d9c9df8d89e4957e37a13bb00f74b0a949ba73097a6ca2145138319" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/style.css", + "size": 452, + "sha256": "983455af96b98b594c470eb940247da2a5f6fad60d284ab994590cc762f6562f" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph.php", + "size": 1139, + "sha256": "37dbd34f7436ecaa4a257cff83920b15e865581194d9d971c84ce81936c79d86" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/block.json", + "size": 1887, + "sha256": "70a5e1bc941b4adb1591dccbe8e1095ecbfd1f7baf6c2a7004d9f9c047d31c80" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/style.css", + "size": 1562, + "sha256": "f37a4910749fd26b4bdeae81449d6b5074078ac339fb18f7ced5a952d9b00cea" + }, + { + "path": "wordpress/wp-includes/blocks/pattern.php", + "size": 1788, + "sha256": "42c91230096cbd323d791e9f58ff6c769484ef3748114a102b8e06819f038d02" + }, + { + "path": "wordpress/wp-includes/blocks/pattern/block.json", + "size": 434, + "sha256": "ab62550c1fc5ce56179dd0fe5210626aaddccf98104f8ff5838772128d28f5e6" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography.php", + "size": 1524, + "sha256": "2942da34d7a0733808337b9f40f829a1999989ea7c8677902b17f5156fc7a71a" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography/block.json", + "size": 1259, + "sha256": "3c4c84cdc86e978a90e2a019071fc770b77c9dc048fda11ee86f0e75d10e0658" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography/style.css", + "size": 61, + "sha256": "ab347926fa395caeaefd27a172edae49cdc647c0509fa423bc060476fd055a1f" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-name.php", + "size": 1951, + "sha256": "657bd5a852e6c28cbd92d47630aea417d70bdbfebc91321bd9386329924471c8" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-name/block.json", + "size": 1465, + "sha256": "9d9606a5c3688d90c1113d0484f42bc939fd6ae4c7b15cc5645642a602ab6645" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-name/style.css", + "size": 56, + "sha256": "bd38a1cb30550b25db4bc5cecdc715836bc3a80651966e3e0268e90e91d26dc6" + }, + { + "path": "wordpress/wp-includes/blocks/post-author.php", + "size": 2734, + "sha256": "4739f8dc21799ce0d93b8d867312c3f50df0ddeb2f8984a8de36086543c17eae" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/block.json", + "size": 1957, + "sha256": "54e5e45b68a627fb2fd1eeea0225cb98084b66df122e025b8ee33271da905ba3" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/style.css", + "size": 436, + "sha256": "29c743c6d57a11ba64d6b347bd8b4d696c29f38eaa1548d5410663a99716aac9" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count.php", + "size": 1275, + "sha256": "38df962dafae532c15b7b4e037df1b5ce5ce1321ef0055bc794b82f4ea2675a2" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count/block.json", + "size": 1132, + "sha256": "1d189828d9c04786d60885e321939c19e6606356df7132eacd4c38317db8160e" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count/style.css", + "size": 59, + "sha256": "ee2dc72a10a48bc6ccc1d7d4bb2e2d449c01cd082ffee6ab95eb9e6ef6011678" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form.php", + "size": 2805, + "sha256": "5c981e3f2336a43c36e4ee86437737fc687719dbac5b910eccc7c0cd8d7ea760" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/block.json", + "size": 1340, + "sha256": "c7b457d0ed2f01f98632dddf66936ac38826201396636c13feca75a7cb14c2f6" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/style.css", + "size": 2234, + "sha256": "127b185692bb444a67029469ea8d517b9b8c7f81d2d54d634a4e1788d39352ac" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link.php", + "size": 2237, + "sha256": "0108dfa15a1afdb89e5b4d366b6979d7b682378281ae6930ff7fbc44d7ac5154" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link/block.json", + "size": 1289, + "sha256": "57aa85ac73a92a5458c4d75e1cd504aeecce8f42227a010328cc1a204f308d97" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link/style.css", + "size": 58, + "sha256": "e74d4a8ed66f08eefa6f205ea57bd4042a148da12ad6819614d6152e73bbc1a7" + }, + { + "path": "wordpress/wp-includes/blocks/post-content.php", + "size": 2347, + "sha256": "f53f107b62386fffc420e2e1fa28137959bcd6bd66c834c9989a3a7a7fe55e28" + }, + { + "path": "wordpress/wp-includes/blocks/post-content/block.json", + "size": 1757, + "sha256": "f2cde6429ca1c317319193594320b587b46b771507fd3cf4e5218ae9a3311645" + }, + { + "path": "wordpress/wp-includes/blocks/post-content/style.css", + "size": 48, + "sha256": "4a95959fb196e41d1fbdcc7e21bace395eda401e928c2064e3751408fe324ba2" + }, + { + "path": "wordpress/wp-includes/blocks/post-date.php", + "size": 3655, + "sha256": "cc48c8ffacc9eaefea057b53210aa83e83e456c72356eae73af1087aee9b85c5" + }, + { + "path": "wordpress/wp-includes/blocks/post-date/block.json", + "size": 1463, + "sha256": "03480b28239717509d7645055d0321e166330300ea70fa3e143bc25140b95585" + }, + { + "path": "wordpress/wp-includes/blocks/post-date/style.css", + "size": 49, + "sha256": "b242f6f5e2d87611434f2e96edc80421d57d7288b9a591df2380349caf80560a" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt.php", + "size": 4315, + "sha256": "3e6ca9a5eaf99f058e1b35e5b3ba5e68b50d713282a038d1914f12c16e3e1d06" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/block.json", + "size": 1587, + "sha256": "673589fb0c180a6307c17945ac01b656a019c7bdceba2becf20f468ac448d0de" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/style.css", + "size": 389, + "sha256": "1abd3ef89a38a5048b2ee79ee26a17c538b8906e81efaac73a9d1c10a251fdaa" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image.php", + "size": 9248, + "sha256": "bf650b1074cb7238e4fdccdeefe6478a7a3e2fb141de58230f6723e9cd4bf4f4" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/block.json", + "size": 2457, + "sha256": "fac721751b76cb27ca90823cf051a866c27c8ed63f0ec74fcba528dd4d32f197" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/style.css", + "size": 2050, + "sha256": "f0d6ba32be7201238ebea9fb76c061886db7b78354144dd65fef2361f1f6178b" + }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link.php", + "size": 4807, + "sha256": "85f2a155a859c3d350c826b45b5bd3a5f413bedf74f26e67789be54e55061d9e" + }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link/block.json", + "size": 1358, + "sha256": "c93ed40906e3d6d58c2cf003e42c27ace62e3571f4da5d91f1e3b2ac143cdd4e" + }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link/style.css", + "size": 742, + "sha256": "7bf8823266d0c3e5c940c8bf4f8eabdfaf54e0e42e5a2b8a8fa537441d319760" + }, + { + "path": "wordpress/wp-includes/blocks/post-template.php", + "size": 5687, + "sha256": "17efe99ddcb481053f03a99e4ed0aaa1f7c659e0857b7fd85e28a015779af64b" + }, + { + "path": "wordpress/wp-includes/blocks/post-template/block.json", + "size": 1589, + "sha256": "5fed346b6318eb89cc80375b42e252ce7c02e946db39b83708d7d87a05d78ced" + }, + { + "path": "wordpress/wp-includes/blocks/post-template/style.css", + "size": 3572, + "sha256": "c5d42ac6d78909c5bb4ba4c9b49bb07e9242aee38871e16e42b0c85fc7f5a472" + }, + { + "path": "wordpress/wp-includes/blocks/post-terms.php", + "size": 3640, + "sha256": "95d610b7ca22cb91add7721926294833a9a154a7caaab859aa586079e9c6eee4" + }, + { + "path": "wordpress/wp-includes/blocks/post-terms/block.json", + "size": 1533, + "sha256": "41372d6e5b1390bf1c9f920306162f2d23dd421819407725bcada76a1b34b228" + }, + { + "path": "wordpress/wp-includes/blocks/post-terms/style.css", + "size": 132, + "sha256": "735f2d2e6b6c081b4fa4d01b10a119bbf5a4b828910cd287c6de9fdded8c6fae" + }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read.php", + "size": 6411, + "sha256": "ff369002ad2bfbd3e30f4a5e6eff9a8777b78ab1473ee3338a1f98ce9bafe11d" + }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read/block.json", + "size": 1413, + "sha256": "01fcbd102049849a3f6d49fc6db38169e09cd40d91d57039400c28346d16aa99" + }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read/style.css", + "size": 57, + "sha256": "3ac4f47140117207ea9324d425d4576644e7aa8a7932539e1bb2509023c256cf" + }, + { + "path": "wordpress/wp-includes/blocks/post-title.php", + "size": 2138, + "sha256": "eadc66b7efdcfbf26c921a3dc03d121676efa3a95567cfb28183751f550461e3" + }, + { + "path": "wordpress/wp-includes/blocks/post-title/block.json", + "size": 1753, + "sha256": "7a8fdec350a478c6d3710f9e9c939298957e82c943e9c1734b549ff206afe300" + }, + { + "path": "wordpress/wp-includes/blocks/post-title/style.css", + "size": 308, + "sha256": "1ab285b72f9f05f4bd6b0fc77ae17359df17b7f75c351d337d052853da737bab" + }, + { + "path": "wordpress/wp-includes/blocks/preformatted/block.json", + "size": 1337, + "sha256": "d5a9fef98fd49f0820ef74928e1f4e0a775103ad1778f3078fb70edfe17bbae2" + }, + { + "path": "wordpress/wp-includes/blocks/preformatted/style.css", + "size": 935, + "sha256": "ba55d9b5b02e949b2d815b39323b8759bd3406e3ecdf6658ada0d775125edb79" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/block.json", + "size": 1923, + "sha256": "70b1e1612a9e3cd61fba13852fbcb213c3b31b87aaf2f75f3e6bb52d583748ba" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/style.css", + "size": 2075, + "sha256": "2f224de46c66cf5999467afff581a1d7af111467c50120453a3dcaaccae4f9dd" + }, + { + "path": "wordpress/wp-includes/blocks/query-no-results.php", + "size": 1847, + "sha256": "5e9282ded57f0873b343f8d58a903f6bbf4a69bc1c679c1193c9577692143f5f" + }, + { + "path": "wordpress/wp-includes/blocks/query-no-results/block.json", + "size": 919, + "sha256": "94e9adcc893717da88b73fdcc61294ebe274998984d7695a9b68350c263eecaf" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-next.php", + "size": 3676, + "sha256": "cb53ee496aec273943f89e524e84a813ff0211936652958419b91333439b857c" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-next/block.json", + "size": 1057, + "sha256": "963fab0f112a0aeef34ecc983820a71ce20c19871a2508c79a2097e5ca408fea" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-numbers.php", + "size": 4700, + "sha256": "7dadcabef82dc95f5e74836c08214b65020927bab6223a4e595acf3370ca07b7" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-numbers/block.json", + "size": 1112, + "sha256": "33b446f19ff6ad294d7408991b2a80075e7fe390e44291bfe8906536f32fc04e" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-previous.php", + "size": 3473, + "sha256": "c933b8563ffab5b4156723c6cc758857129ef9912508c78b9050795191e9c8fc" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-previous/block.json", + "size": 1069, + "sha256": "abc3ff398f45aefedac1a101b820f8d476c783c049a84d65e1d698351e00d8ad" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination.php", + "size": 1176, + "sha256": "1c5262645ef185c420884c1dd593007af92b886affcc31321ab4a9fb8ad4d5f2" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/block.json", + "size": 1597, + "sha256": "069e71a12010875bf01e9c20a8edac228f7f22e30d631dc8688fc0ba759f3684" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/style.css", + "size": 871, + "sha256": "88258068d6814f43bfe37268aebaad94ec5f113c9c093c38216e00c8546bfc11" + }, + { + "path": "wordpress/wp-includes/blocks/query-title.php", + "size": 2771, + "sha256": "b30c6b2ece6297a38bb6a6f21fe19da9f404ca273d7a93f80b5df6e412793fe4" + }, + { + "path": "wordpress/wp-includes/blocks/query-title/block.json", + "size": 1582, + "sha256": "09edff84de4545607bb3f8d1092188f2942ca7fbbcb4968c271abaceeae49013" + }, + { + "path": "wordpress/wp-includes/blocks/query-title/style.css", + "size": 51, + "sha256": "f0e4ae006b910c0adb01575011233dc99897f441834c01825f38fbe8d5d4b3c2" + }, + { + "path": "wordpress/wp-includes/blocks/query-total.php", + "size": 2471, + "sha256": "314217d54f714e044980168d93561da7ff4afd5c6f34ffcd4665513c7db75dbb" + }, + { + "path": "wordpress/wp-includes/blocks/query-total/block.json", + "size": 1352, + "sha256": "e8c7b369d9626fae70b7259927ed45a5edba6cff1c4bb606f0bfabef2af6696d" + }, + { + "path": "wordpress/wp-includes/blocks/query-total/style.css", + "size": 51, + "sha256": "22ac232d4b704be23ca34a46b4b3f8425c9959981e5125b7d8cd7b4733b540d2" + }, + { + "path": "wordpress/wp-includes/blocks/query.php", + "size": 5690, + "sha256": "504b7e72806ab6ee919a94441cedaf8be26eb649291fee726fd84ff788a37edf" + }, + { + "path": "wordpress/wp-includes/blocks/query/block.json", + "size": 1302, + "sha256": "f37a436a5c1d84be14dcd8a8f114a817a00b743d73a69253352384997a9fbecc" + }, + { + "path": "wordpress/wp-includes/blocks/quote/block.json", + "size": 2223, + "sha256": "ec56d2ece476b32f38f924f5cc2d9b0f8542951222318efb92f284a5b4b366f9" + }, + { + "path": "wordpress/wp-includes/blocks/quote/style.css", + "size": 765, + "sha256": "1ef9387c601a42e7670e2d5b8f633d7fd988fa478f984fdf6dc58f3ef1924414" + }, + { + "path": "wordpress/wp-includes/blocks/read-more.php", + "size": 1830, + "sha256": "54074647517c00bccf51f8523452018f47232157107ae93b583380aca1e0546b" + }, + { + "path": "wordpress/wp-includes/blocks/read-more/block.json", + "size": 1304, + "sha256": "d3463857ed8cf4f81b10131b77a65816759b6e998828131881570f26c36f79ca" + }, + { + "path": "wordpress/wp-includes/blocks/read-more/style.css", + "size": 309, + "sha256": "ab266f741297164cdd48c300dabb8b28ac90ad0aea107d8fd69a80941af6f0f3" + }, + { + "path": "wordpress/wp-includes/blocks/require-dynamic-blocks.php", + "size": 4978, + "sha256": "4469028d2d597517e748801ff244627f22676fc1269239e761d6d8ec50c9cf9b" + }, + { + "path": "wordpress/wp-includes/blocks/require-static-blocks.php", + "size": 520, + "sha256": "b9e5253a3976c1d0937287dc01896b7fa4ef3d24ede59c580257a1eb9b45d206" + }, + { + "path": "wordpress/wp-includes/blocks/rss.php", + "size": 4609, + "sha256": "31cb092ad3a6fccfb307591087ee641ab0f18efcc8744049303fc8bab9767d72" + }, + { + "path": "wordpress/wp-includes/blocks/rss/block.json", + "size": 1447, + "sha256": "62cebfa01ba00823a04fe97c6816c9a4eb06099e1bbb93da5fca2a9bf0302aa4" + }, + { + "path": "wordpress/wp-includes/blocks/rss/style.css", + "size": 2587, + "sha256": "f7212946b6975af4537d873e0a4254cf68d4a9724edc45c359fd3161514f6917" + }, + { + "path": "wordpress/wp-includes/blocks/search.php", + "size": 23817, + "sha256": "8d0be4b7499634bcc8b1c15607a497012180e0f8f5a3614abb01c32dfd840de9" + }, + { + "path": "wordpress/wp-includes/blocks/search/block.json", + "size": 2038, + "sha256": "0e44d6c23d8d2919a23b368b1ec9c752a1b55617e1d96a5c8a9bf9d621327f05" + }, + { + "path": "wordpress/wp-includes/blocks/search/style.css", + "size": 3573, + "sha256": "cddd940a20ec40a6bc1f1639a1fb987d5d29e32964fa068108560f93a2948531" + }, + { + "path": "wordpress/wp-includes/blocks/separator/block.json", + "size": 1146, + "sha256": "4ce92b86014172f7a64b34413a2ab2edf4b70dcc506234c8080aa4cdecb6127b" + }, + { + "path": "wordpress/wp-includes/blocks/separator/style.css", + "size": 558, + "sha256": "f6df036d7b1ecd466096904f74e77c3a4c2ddb3a13db1525e8790036629dde44" + }, + { + "path": "wordpress/wp-includes/blocks/shortcode.php", + "size": 735, + "sha256": "bf1d2660e045133ce924a0106f6b9a19db10b96de09a388000a845724d484fac" + }, + { + "path": "wordpress/wp-includes/blocks/shortcode/block.json", + "size": 531, + "sha256": "df8a0168d38b39c78830e78f8a7b19458eee7a92569796a6a4dfbd6b78b17ccf" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo.php", + "size": 6334, + "sha256": "b767019ec49dcc0cad687d7054edca04a93332f6982fb3a58c9ecfca8a2da9c4" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/block.json", + "size": 1500, + "sha256": "256b7bde9287e86c1a796d55734ccd65452542b47d15f211bc5c587f0be5dccd" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/style.css", + "size": 534, + "sha256": "3bad81efcf5dba56449a58602c31624570143bf96e9e126fd60aa60cb73bc2f5" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline.php", + "size": 1203, + "sha256": "141c6227c9dc32d2dafb089b09174f1bfe95032c63de746cf45710d75e1967f8" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/block.json", + "size": 1688, + "sha256": "280bd79b2b67c4c80d6ca61a7a98e6c33d7b11725ee73e33796b2b62975530bf" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/style.css", + "size": 52, + "sha256": "0231f5f37c6f7d0c8cfc3e41352a0e37ce568082529aa9952a938f628da8f8dd" + }, + { + "path": "wordpress/wp-includes/blocks/site-title.php", + "size": 1864, + "sha256": "e223b7a9573068010bd55825ac0ae34d8520588f898ed714f1ebd7b0017dcb99" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/block.json", + "size": 1797, + "sha256": "d55c4519c96203156623fbca64fbba8744254e2ed2ee9772f19e84f39b75f2d1" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/style.css", + "size": 275, + "sha256": "9d899520dea0df0c3f1dfd86a13ddda45f010c7a00a82f5596effd35983e4083" + }, + { + "path": "wordpress/wp-includes/blocks/social-link.php", + "size": 67602, + "sha256": "0d92033bd57a45da247b565b05699033fb0252e7ddbd1b1cd4f549b193bbb87b" + }, + { + "path": "wordpress/wp-includes/blocks/social-link/block.json", + "size": 822, + "sha256": "ada66d26623f10e9fcd4a5ae99305931e51e6d65e8e58494e9c7d8a42ea6c8ac" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/block.json", + "size": 2403, + "sha256": "46fb82a9096a1b61530a490df7402b65b043f3e6eda9adff156060b8dcc515c9" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/style.css", + "size": 12982, + "sha256": "f0a9b59e1800da133b39ba933df08417364762cc29f5bb916453b2573584b1fb" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/block.json", + "size": 677, + "sha256": "47b184ba081ebe8bf94626dd8cf53a83de3dcb0d2367c4ca1dd98e4860c45631" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/style.css", + "size": 35, + "sha256": "d622c5eb5ee6345a9c554a9cc91fa56bc543839f2a48e40f6f0b814bea428f8b" + }, + { + "path": "wordpress/wp-includes/blocks/table/block.json", + "size": 4508, + "sha256": "40a9197d177d02d46143b84453e057afbaf7d9d4898fe4592329b64d6babd06f" + }, + { + "path": "wordpress/wp-includes/blocks/table/style.css", + "size": 5080, + "sha256": "a386d9d775c2c00b8ea7208ec2df1d4de91aa5a658be975da0770af7bce006b4" + }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud.php", + "size": 1590, + "sha256": "6c67d54d9a8c32d5b956eac9378730f9b2138c1fa9fd7a1e30685753015c3186" + }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud/block.json", + "size": 1421, + "sha256": "a5fe7791e8f1cc1f23c499c9fedd0f613914de71ba3568ffabfeea780b1d672f" + }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud/style.css", + "size": 607, + "sha256": "b67fce56649938d490d5f8df5884e4a4d6114d954469b74d08cc5f105a30f3a7" + }, + { + "path": "wordpress/wp-includes/blocks/template-part.php", + "size": 10446, + "sha256": "9c4ceab1a1e0b7b4055b3a9ee9f54197ad6d3887ff49df612dafe9f105c25367" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/block.json", + "size": 672, + "sha256": "4f0be35529b24372d70c636cb7a251bdd6dac654020bd9d9a25cab34a66946b1" + }, + { + "path": "wordpress/wp-includes/blocks/term-count.php", + "size": 1843, + "sha256": "937040d80bfb8f4ba9a702984b763b89d648cabdc63153e4cfa7bb6a72bf7124" + }, + { + "path": "wordpress/wp-includes/blocks/term-count/block.json", + "size": 1298, + "sha256": "0e2b3d58fcf3f49558875c34cfe999ebdd5c00bc2fd365d9bb9208376815433b" + }, + { + "path": "wordpress/wp-includes/blocks/term-count/style.css", + "size": 50, + "sha256": "cd61e48f387bdf1c4e76beb5017b6386c9d3389c9c9059c344a86285f56fd590" + }, + { + "path": "wordpress/wp-includes/blocks/term-description.php", + "size": 1770, + "sha256": "0a08ecbba72bd36ac13a69d6c16f9fefd961d75c0f8670a284ec623c26ddddc8" + }, + { + "path": "wordpress/wp-includes/blocks/term-description/block.json", + "size": 1264, + "sha256": "59d0ac9a7b4ead4f12f2c3dc45a9d414813d2f786ce092005e2f6891eb4d7602" + }, + { + "path": "wordpress/wp-includes/blocks/term-description/style.css", + "size": 224, + "sha256": "f23691c6b81750187694528332d2fa626d3e98a8126eae658f81ca715d12a966" + }, + { + "path": "wordpress/wp-includes/blocks/term-name.php", + "size": 2038, + "sha256": "96451470d29e92d6876a2f0ab69d03dce2785ca0bc263b7184f52545c10a8cff" + }, + { + "path": "wordpress/wp-includes/blocks/term-name/block.json", + "size": 1461, + "sha256": "0c277931f34fbb3ec24a36e83512b78eb70031cf04b3547b5bdb1eee49cb42ed" + }, + { + "path": "wordpress/wp-includes/blocks/term-name/style.css", + "size": 49, + "sha256": "87a73237e41b708e51c70f7f6149331f184a055677f53d1d45c369d0dd17a226" + }, + { + "path": "wordpress/wp-includes/blocks/term-template.php", + "size": 4518, + "sha256": "bf3506d7251b22e87864d59125bf4339eabeeee6f70161334b61d48fa5bb079e" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/block.json", + "size": 1466, + "sha256": "aeeab09a60449dedd1d7f79ddb9cb49de5048213e1bef505821226fc5ea1ae34" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/style.css", + "size": 143, + "sha256": "416f2fcdbcd6f85aae3dae35aa56d52526e0109bf6d42047ab21c98d45ab4b34" + }, + { + "path": "wordpress/wp-includes/blocks/terms-query/block.json", + "size": 923, + "sha256": "df746c802b2997116f87d00004a3013b8b9d3bd764aee8a69a48dba4c30197ca" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/block.json", + "size": 784, + "sha256": "436202addcbe99d429d86755a5f379b0fe1db2c450945902f522243baa44852b" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/style.css", + "size": 537, + "sha256": "8502e85cf12eae0cdb5b9521f342eff9d254b61890f4ea063230ef27ff74431c" + }, + { + "path": "wordpress/wp-includes/blocks/verse/block.json", + "size": 1728, + "sha256": "674e5ebbd6328629c006c2e2fa18399b7c92a5917349f4844d7370e418dcb5ac" + }, + { + "path": "wordpress/wp-includes/blocks/verse/style.css", + "size": 191, + "sha256": "716c59cff6b649296364afef015f917aa99832aa0b1d3d4d6f58540174af0155" + }, + { + "path": "wordpress/wp-includes/blocks/video.php", + "size": 2733, + "sha256": "cd52665baa5bdf045fc944b153a52ca0a7ae4fccef923de5312a146bf09156ef" + }, + { + "path": "wordpress/wp-includes/blocks/video/block.json", + "size": 1964, + "sha256": "932b32d9ec75bbfd5afd1aeb8c8d55987ae3b9e2900a1ad4545c6747b660b355" + }, + { + "path": "wordpress/wp-includes/blocks/video/style.css", + "size": 2092, + "sha256": "b673f8c6c9b931f78e5221bc297309f2be1c5745f82ee4c9acd4600cac326b93" + }, + { + "path": "wordpress/wp-includes/blocks/widget-group.php", + "size": 2438, + "sha256": "c981be68de03d7d3697f9820dd929f5de1c00a93eac7838b462a30eb4a172a48" + }, + { + "path": "wordpress/wp-includes/blocks/widget-group/block.json", + "size": 400, + "sha256": "a2753779dc4b5e09c6c52e073178fc93f981f3dc1d051d1c96afef0c3bf81835" + }, + { + "path": "wordpress/wp-includes/bookmark-template.php", + "size": 12768, + "sha256": "7fe0dd67f50a833989d143759d1f98deb92f51df9aec69ea57bbe6e3290b689a" + }, + { + "path": "wordpress/wp-includes/bookmark.php", + "size": 15427, + "sha256": "8c582091676d15dbcdda128607163ba9b9612fce5573a05e25b155e274ec0669" + }, + { + "path": "wordpress/wp-includes/build/constants.php", + "size": 275, + "sha256": "a79cb7387dfcb0160a768f6dbd553e13845c7fb7474eec04e741218a6c2509eb" + }, + { + "path": "wordpress/wp-includes/build/pages.php", + "size": 369, + "sha256": "022937e8056cb37bae3d28f89caf847530ce5fa9c262000bbd7d3cefd3328d39" + }, + { + "path": "wordpress/wp-includes/build/pages/font-library/page-wp-admin.php", + "size": 9157, + "sha256": "a3e67b9f3593d4b8dc9609fc3563a48f135eb3e131b8912e33b550b6a83c5fe8" + }, + { + "path": "wordpress/wp-includes/build/pages/font-library/page.php", + "size": 9311, + "sha256": "a435f9644a7381c9af07fd7003df858bb0e96d95007a66ed9b3d0a093b7c69dc" + }, + { + "path": "wordpress/wp-includes/build/pages/options-connectors/page-wp-admin.php", + "size": 9445, + "sha256": "6ce8a8b1207ee43f92ca4d0e479825cd7cc5a080190498add3dc38a36cf23392" + }, + { + "path": "wordpress/wp-includes/build/pages/options-connectors/page.php", + "size": 9593, + "sha256": "b5ee7521b47d45a8ff8477bb6e209ce13dc2b597ba412af2dbe2aac2a9a2cde2" + }, + { + "path": "wordpress/wp-includes/build/routes.php", + "size": 4890, + "sha256": "c4d1033efea199f060013f76d9d51ccf410b971e9d2fb7a136b24ff130300fb6" + }, + { + "path": "wordpress/wp-includes/build/routes/connectors-home/content.min.asset.php", + "size": 486, + "sha256": "cb333a52c9c8ed0dc0c521642ee7bb57afa6bcbab292a9e8985a5838f0fba104" + }, + { + "path": "wordpress/wp-includes/build/routes/connectors-home/route.min.asset.php", + "size": 119, + "sha256": "c1b6e2adc6c49be5b5b2667d1519ac9422d6e6899dd5c83b89eaefb48cead959" + }, + { + "path": "wordpress/wp-includes/build/routes/font-list/content.min.asset.php", + "size": 506, + "sha256": "5864a91c5852ec55c7ffc3c999bb5d33f6427dedb41d1857550045dc0391ff5c" + }, + { + "path": "wordpress/wp-includes/build/routes/font-list/route.min.asset.php", + "size": 92, + "sha256": "ce9b599e3bd312c9bba3b7fe2b27d3e7cd0aa6e6555180feef158dcae225dd56" + }, + { + "path": "wordpress/wp-includes/build/routes/fonts-home/route.min.asset.php", + "size": 172, + "sha256": "fa731dcb5e9eca74e445952ec2ed2da0fa443e6af1f71e75d21def4758f46fb3" + }, + { + "path": "wordpress/wp-includes/build/routes/registry.php", + "size": 619, + "sha256": "2fca1dbc94a4c1b6a0d4c2749b11792ebefa354c741a5f99a875d097ee617d07" + }, + { + "path": "wordpress/wp-includes/cache-compat.php", + "size": 11021, + "sha256": "eebd7161af3ae4feabbcff8cf0206ada14a539635448050f066b1e6d7a2c345d" + }, + { + "path": "wordpress/wp-includes/cache.php", + "size": 13486, + "sha256": "a219c2b57cc9990b560c6b497c877db443b7e29239645a6a79b2d76839c46e19" + }, + { + "path": "wordpress/wp-includes/canonical.php", + "size": 34645, + "sha256": "7437d2a223331170810aa737204be512454c564f8645908a2b25a652862c177d" + }, + { + "path": "wordpress/wp-includes/capabilities.php", + "size": 43633, + "sha256": "12e26e9aede307a7ad1eed11137f0ac0ca7db1c9a841ef77d3f83f960c7a7fee" + }, + { + "path": "wordpress/wp-includes/category-template.php", + "size": 56985, + "sha256": "91fce055f9ff07695b1458e5824d037707c37ddb70e5b0ebad201ff68c32d085" + }, + { + "path": "wordpress/wp-includes/category.php", + "size": 12834, + "sha256": "77de9617bcbfed550e79375df211f4c55c72e5a1d0d88b6b85dcb3fec7ed69f1" + }, + { + "path": "wordpress/wp-includes/certificates/ca-bundle.crt", + "size": 226168, + "sha256": "b6e66569cc3d438dd5abe514d0df50005d570bfc96c14dca8f768d020cb96171" + }, + { + "path": "wordpress/wp-includes/class-avif-info.php", + "size": 30008, + "sha256": "ac2268407c70c54a4a9f9de8a01d4343801358acbe30a1b8ecbb39265fdfea56" + }, + { + "path": "wordpress/wp-includes/class-feed.php", + "size": 539, + "sha256": "f0f421421068a098bf29e8b08ae16886be635b298aa4221f6c42fe5509b3d01c" + }, + { + "path": "wordpress/wp-includes/class-http.php", + "size": 367, + "sha256": "0882fa684a028543f18568149920e7ed12e1a47fd646e5bf8f08c97ebed5d239" + }, + { + "path": "wordpress/wp-includes/class-IXR.php", + "size": 2616, + "sha256": "df0440a9e8656a4059e7d656d81cacfac142eb6960cea430f6a5a185b6a8ca54" + }, + { + "path": "wordpress/wp-includes/class-json.php", + "size": 43676, + "sha256": "2f884295bb916ca0de10463ec45be172691a9d36ea76687c4a012c3aa5b83c74" + }, + { + "path": "wordpress/wp-includes/class-oembed.php", + "size": 401, + "sha256": "a8fde550b396868e5ece83b7cdc0884a970e8104481b431d54df4559c615bf53" + }, + { + "path": "wordpress/wp-includes/class-phpass.php", + "size": 6771, + "sha256": "eac68d6268d6704c8ca3195a731a6cc476e6dbfc6a604adb784886e3a0b10221" + }, + { + "path": "wordpress/wp-includes/class-phpmailer.php", + "size": 664, + "sha256": "0dfbc6a7b33c833e61559bb196dcbbccf2389f5813ca7ba0bb2fc8048745ec9f" + }, + { + "path": "wordpress/wp-includes/class-pop3.php", + "size": 21121, + "sha256": "1872ab2151b091b419d3e354852e7c179c3879cb5046c5dd1b6ca35b1df29220" + }, + { + "path": "wordpress/wp-includes/class-requests.php", + "size": 2237, + "sha256": "67ed9f72f86b6c9900f08362e55b4f5dc34b7c68d2a9ea8d8161c03f7df212df" + }, + { + "path": "wordpress/wp-includes/class-simplepie.php", + "size": 453, + "sha256": "ed208689fcf1f49977e9c5c9ae315e425720b30e515ac3791fec31af338490f5" + }, + { + "path": "wordpress/wp-includes/class-smtp.php", + "size": 457, + "sha256": "521e45083d2d43eb70fd7985643fd3847047a2060a743cf9d0635e96eb369563" + }, + { + "path": "wordpress/wp-includes/class-snoopy.php", + "size": 37715, + "sha256": "84fe34f1549d11b015d919350ad4599e2af16ca7e630fe4eab3b79f87e1f0299" + }, + { + "path": "wordpress/wp-includes/class-walker-category-dropdown.php", + "size": 2469, + "sha256": "11f75679d1423037546f27f7ab7c01408e2b7ee6888c6d96381395b4085bfbe9" + }, + { + "path": "wordpress/wp-includes/class-walker-category.php", + "size": 8477, + "sha256": "64d2515295d9cb9e631fe5cd01a5953c79d2b84f174d1e1de1951e4663aa35ab" + }, + { + "path": "wordpress/wp-includes/class-walker-comment.php", + "size": 14221, + "sha256": "8e36b1266934fbd8d2dff6df7483e738165994a4bb8f0e9419dc0f175de42cf9" + }, + { + "path": "wordpress/wp-includes/class-walker-nav-menu.php", + "size": 12044, + "sha256": "8e36fdcdacb40c17e230bd276c3aff69240e04be161991677f4196dffb33b3ae" + }, + { + "path": "wordpress/wp-includes/class-walker-page-dropdown.php", + "size": 2710, + "sha256": "1d827b89d763ff0456dd82983c1ad0b0ece666b72139f0a7dae8997b400576d0" + }, + { + "path": "wordpress/wp-includes/class-walker-page.php", + "size": 7612, + "sha256": "3b7d41b58a55aa130624ffef6dca837a5a61e7152c630b8aed2c6c3e3bb7660a" + }, + { + "path": "wordpress/wp-includes/class-wp-admin-bar.php", + "size": 18004, + "sha256": "d93e3f1fec8bec9752fc675be85640a7e958bede1636da1cad07c8cbdda5f5e0" + }, + { + "path": "wordpress/wp-includes/class-wp-ajax-response.php", + "size": 5266, + "sha256": "822bcef632a5a72f19c08dcc89a897d8845a710ef5b741b90b2561f0390f04f1" + }, + { + "path": "wordpress/wp-includes/class-wp-application-passwords.php", + "size": 17099, + "sha256": "266e15ec38652acaee5e3fd6103fc1275d2d6ea5c54f8926c1c4ddf688559658" + }, + { + "path": "wordpress/wp-includes/class-wp-block-bindings-registry.php", + "size": 8263, + "sha256": "bfa589aa1711c3c92eb5f6adf4340a6a8d353ccec52f4d8252cb844929e63183" + }, + { + "path": "wordpress/wp-includes/class-wp-block-bindings-source.php", + "size": 2992, + "sha256": "ed059bb0c3d6e30ceb556eeb913555880d54f43e8cede7f1bc2804ba2faab370" + }, + { + "path": "wordpress/wp-includes/class-wp-block-editor-context.php", + "size": 1350, + "sha256": "83ff9a23b84d970bdf928f565a545acba106bd69f7c19bee65004a56a00615cb" + }, + { + "path": "wordpress/wp-includes/class-wp-block-list.php", + "size": 4713, + "sha256": "0d27514f494f2e9cc8cefcf7ecbddd398eee9bbcb5145514a2c472934bf8f156" + }, + { + "path": "wordpress/wp-includes/class-wp-block-metadata-registry.php", + "size": 11846, + "sha256": "2b8319a1ecc6131ad7889fbfa5895dc533f08b46dfb4987d2930f219d207a9b0" + }, + { + "path": "wordpress/wp-includes/class-wp-block-parser-block.php", + "size": 2555, + "sha256": "7e21f483cf0c9deddd2144e026842bf906c72983908f7bd163c0716e8f6f325e" + }, + { + "path": "wordpress/wp-includes/class-wp-block-parser-frame.php", + "size": 1994, + "sha256": "3a66c88fa2397a842884908d83d1d03e5cb9cfb23fbb6d3cdf573bcd1520f0ae" + }, + { + "path": "wordpress/wp-includes/class-wp-block-parser.php", + "size": 11520, + "sha256": "dc641c682a703efa3909990bb32e66e3bfd98a1fa1793c192a71f63593664487" + }, + { + "path": "wordpress/wp-includes/class-wp-block-pattern-categories-registry.php", + "size": 4383, + "sha256": "cc7fbd48ee9d71cd0371de09a570968234be4333af95977fe94ebc8eaccababc" + }, + { + "path": "wordpress/wp-includes/class-wp-block-patterns-registry.php", + "size": 10312, + "sha256": "1a3e95b9148d4711f66c5777d2712f01e326e4b6e6e2cfbc2cbc81cde39ec03a" + }, + { + "path": "wordpress/wp-includes/class-wp-block-processor.php", + "size": 69959, + "sha256": "a7371a1b2bfac66db0daac29bc8b7eb943c735e6cd9d267eacb6de299d09aa5d" + }, + { + "path": "wordpress/wp-includes/class-wp-block-styles-registry.php", + "size": 6419, + "sha256": "1a58fa7cdb03cf5f9e7f8e08b8a9a46304faf12aa9ec9f339595367050e087d8" + }, + { + "path": "wordpress/wp-includes/class-wp-block-supports.php", + "size": 6551, + "sha256": "ea2d81f7287aebf87861ea1641a191679256b68317a5a6ac7cfb8d3513e836b5" + }, + { + "path": "wordpress/wp-includes/class-wp-block-template.php", + "size": 2033, + "sha256": "530239e214d2504816c297b2ccf901359919045457cf2cd0a1627876b2872097" + }, + { + "path": "wordpress/wp-includes/class-wp-block-templates-registry.php", + "size": 7080, + "sha256": "45f22aabadbdf46b17d8f477f36ae9c9823d90e8118872cc03498bbd2ed04835" + }, + { + "path": "wordpress/wp-includes/class-wp-block-type-registry.php", + "size": 5030, + "sha256": "b760f8ea6b79932d2b8132bf5f7db1ae6f447ee0e4f85e6689731a434a1a3df8" + }, + { + "path": "wordpress/wp-includes/class-wp-block-type.php", + "size": 17233, + "sha256": "55c8ee65e17aa9fcd6ff27e97fdbe084f4dd818f156a3b4bd428d24ff6d0f233" + }, + { + "path": "wordpress/wp-includes/class-wp-block.php", + "size": 24720, + "sha256": "108a02be5d2b0df9411c5b7b5fb02dd0c489a74ee2361c124e66498f5ecc285e" + }, + { + "path": "wordpress/wp-includes/class-wp-classic-to-block-menu-converter.php", + "size": 4026, + "sha256": "4cda9471c71d7798544bc36371d6aa50ee700990062f5ff7ea195e5d03104619" + }, + { + "path": "wordpress/wp-includes/class-wp-comment-query.php", + "size": 48631, + "sha256": "12e4cb4d7bc822eedac4fd82c5011e66e2919754dcecdaba0fef52a952bb4a3c" + }, + { + "path": "wordpress/wp-includes/class-wp-comment.php", + "size": 9371, + "sha256": "67216778a97fc448fb404e7cafbf74e7027ca965d082e88f2a7c58e9a3d0f5a7" + }, + { + "path": "wordpress/wp-includes/class-wp-connector-registry.php", + "size": 14409, + "sha256": "6716d6d664bb9fb4d4c18c1b7b59e31c69064126040951fe156be58fcd9ed0a7" + }, + { + "path": "wordpress/wp-includes/class-wp-customize-control.php", + "size": 26119, + "sha256": "a2aa87526c4ee1deb087209869f605fdebabe0597bf81150ba0f695feaf9c7fa" + }, + { + "path": "wordpress/wp-includes/class-wp-customize-manager.php", + "size": 202881, + "sha256": "a2abf1aa9152a86286e3108e1b7cdad1326388273360189f16928bdcd5c221bb" + }, + { + "path": "wordpress/wp-includes/class-wp-customize-nav-menus.php", + "size": 57968, + "sha256": "74acd64925c481b712295482b0bfc21646c5c32a074471e702a757d491d5e40f" + }, + { + "path": "wordpress/wp-includes/class-wp-customize-panel.php", + "size": 10710, + "sha256": "c4c300b410769baf6924f91819af56c5c8b37165de7dd16ade67390e27c4f510" + }, + { + "path": "wordpress/wp-includes/class-wp-customize-section.php", + "size": 11209, + "sha256": "4468e6cd26e8d98bfa269e9a8cef701f5fea9a5fb5b9cf8f541cd25e7be99e42" + }, + { + "path": "wordpress/wp-includes/class-wp-customize-setting.php", + "size": 29963, + "sha256": "4d890ddb36a5c7a1e233d89a0385a5cfa0ba31bbd16f2f1a7621b4bc879bd685" + }, + { + "path": "wordpress/wp-includes/class-wp-customize-widgets.php", + "size": 72594, + "sha256": "debd7589087b222ffef64a12eeebb04175a46764dcc7d3ffa8c551c9daef469a" + }, + { + "path": "wordpress/wp-includes/class-wp-date-query.php", + "size": 35970, + "sha256": "903d70e399388560d76ebe68264df839922ebabd7e05c7a32345c5c53f7312a4" + }, + { + "path": "wordpress/wp-includes/class-wp-dependencies.php", + "size": 17089, + "sha256": "1a7e9f3ae68ac987d1979b41eee800740ede85d0f7431fb4ec0a1b58f7b65051" + }, + { + "path": "wordpress/wp-includes/class-wp-dependency.php", + "size": 2653, + "sha256": "41cbd8c13705edc829caf39fcbb55e3ebf5ae0338a82fe6320f30c2a361a93f7" + }, + { + "path": "wordpress/wp-includes/class-wp-duotone.php", + "size": 40910, + "sha256": "1f2f984d6a72b60661e9421c925645253e4fc4c78c9d4fac804168100958e6f3" + }, + { + "path": "wordpress/wp-includes/class-wp-editor.php", + "size": 72228, + "sha256": "611d3fc57ec150e5873fdb8d28667ccfd1543e411e77b4ce04d2d22e55dfab56" + }, + { + "path": "wordpress/wp-includes/class-wp-embed.php", + "size": 15908, + "sha256": "a8359656527313e546b0201afffb29fee092813ebbf557cd3d75b0e0f1a3c18d" + }, + { + "path": "wordpress/wp-includes/class-wp-error.php", + "size": 7502, + "sha256": "fe0e54bcb095f3ebb6493cbd558f927234e1bff3f643b2ca00f7e2d74309400c" + }, + { + "path": "wordpress/wp-includes/class-wp-exception.php", + "size": 253, + "sha256": "dc952b0d42f31798c84d0bd37b68beff7e8e66c3d88ee94d5d5d206a342fa6ef" + }, + { + "path": "wordpress/wp-includes/class-wp-fatal-error-handler.php", + "size": 8150, + "sha256": "0c88f945e4a32b6cd504b6ca09993f895d09c400b899bec8a8919a85b36c3a54" + }, + { + "path": "wordpress/wp-includes/class-wp-feed-cache-transient.php", + "size": 3304, + "sha256": "516553579a3d4afabaf4cc046416fa9edb2ab491ef499fd4a0f073a1acc78c38" + }, + { + "path": "wordpress/wp-includes/class-wp-feed-cache.php", + "size": 969, + "sha256": "bd6dc9a019a5485b66a0c6ae404071e8dec3d69e45e03d460155870f5bd8e7a3" + }, + { + "path": "wordpress/wp-includes/class-wp-hook.php", + "size": 16636, + "sha256": "a66fe7372af72876fc702e50943f9a2a8dff4ed7394163e07491b80a06e27f1d" + }, + { + "path": "wordpress/wp-includes/class-wp-http-cookie.php", + "size": 7269, + "sha256": "d0b26c3bbd217273eb1b5074519194b804f47bd5712a8472bfd3bb5210d25607" + }, + { + "path": "wordpress/wp-includes/class-wp-http-curl.php", + "size": 13261, + "sha256": "fc1422e39b6e68c961ae001023a62f21565ea5941a6d1ab0d42e309eea734288" + }, + { + "path": "wordpress/wp-includes/class-wp-http-encoding.php", + "size": 6689, + "sha256": "1ddccf64c212ebad750e2087ae71041ee068f165bc29e5afcbfe96318e8c8212" + }, + { + "path": "wordpress/wp-includes/class-wp-http-ixr-client.php", + "size": 3516, + "sha256": "5496933fce8986c2120073fd3b9605b2895ae524bb566af852397bbde6c34a39" + }, + { + "path": "wordpress/wp-includes/class-wp-http-proxy.php", + "size": 5980, + "sha256": "2acfbf49118490c07c53a6f712d7146be41bd729bff43a6bcf960bbf0f2594fb" + }, + { + "path": "wordpress/wp-includes/class-wp-http-requests-hooks.php", + "size": 2022, + "sha256": "79856866e8ea82fd98d825cf0405918974957eadecc3a68cd9fe42b924f9a9de" + }, + { + "path": "wordpress/wp-includes/class-wp-http-requests-response.php", + "size": 4243, + "sha256": "0db148a63abbbf8bd2087ae82752574244ed6893f997456caf1aa537646533d1" + }, + { + "path": "wordpress/wp-includes/class-wp-http-response.php", + "size": 2977, + "sha256": "6d2a98f222a7b45812cc549b7f32f330aa9389f5ef0d4870987a64c0ae4d4895" + }, + { + "path": "wordpress/wp-includes/class-wp-http-streams.php", + "size": 16764, + "sha256": "d4b013a567e642d602a14d97803357f30a1ea4e41450b5eb0e9e3e99e21312be" + }, + { + "path": "wordpress/wp-includes/class-wp-http.php", + "size": 41648, + "sha256": "031eca4a468d625c82fe75b732a791ae4331fa63b5b243ffbee71930e64152ee" + }, + { + "path": "wordpress/wp-includes/class-wp-icons-registry.php", + "size": 7857, + "sha256": "fd1d7ee2d0cc922a9f470f37036d4572f48d3cec85cb1a7c0f4281e2f253a292" + }, + { + "path": "wordpress/wp-includes/class-wp-image-editor-gd.php", + "size": 20705, + "sha256": "73ae13d5b8da6cf38996c5defb78b4b0a7bfafcf2c93ac4e4f808486cf280931" + }, + { + "path": "wordpress/wp-includes/class-wp-image-editor-imagick.php", + "size": 36977, + "sha256": "fa0f91e91a56965eccc9aadc3d8978ff47d8f34c72c99e758ebcff3731bf3fac" + }, + { + "path": "wordpress/wp-includes/class-wp-image-editor.php", + "size": 17418, + "sha256": "831de08c859d866f367c5a4a92215a7cac2ab39101a18063ed7a5f37f043af2b" + }, + { + "path": "wordpress/wp-includes/class-wp-list-util.php", + "size": 7443, + "sha256": "90171523d0b89315e0cba603b03dc177998de8f3e9895a956cbeed42955a085b" + }, + { + "path": "wordpress/wp-includes/class-wp-locale-switcher.php", + "size": 6776, + "sha256": "c80dce58ad081ad14c6c67efa63a78c964d03b79866906e88241ba428301f668" + }, + { + "path": "wordpress/wp-includes/class-wp-locale.php", + "size": 16848, + "sha256": "379d5ebc2a3434ec22459f5d9e032a1c1f2986f5485cf4eb4a8bebf2f160d856" + }, + { + "path": "wordpress/wp-includes/class-wp-matchesmapregex.php", + "size": 1828, + "sha256": "05b09e4e58db2508c90231ca678351b833f535692c7d07e681e99b299c9e0762" + }, + { + "path": "wordpress/wp-includes/class-wp-meta-query.php", + "size": 30507, + "sha256": "22763447b928ceacc9a3f0cb835c9dc094b168ba1f8f8088319bd0381c8dce70" + }, + { + "path": "wordpress/wp-includes/class-wp-metadata-lazyloader.php", + "size": 6833, + "sha256": "c1df481157f06db91ab7f81fe68981db206beaa38721e6cab49a064110219467" + }, + { + "path": "wordpress/wp-includes/class-wp-navigation-fallback.php", + "size": 9193, + "sha256": "74a7ffb2584b68ac4af851055aeae3cb9703635db1ff707a3f4885a274d14978" + }, + { + "path": "wordpress/wp-includes/class-wp-network-query.php", + "size": 19714, + "sha256": "3bb215c5098210f513ac805eb680e84d10b2f61ed4694ca0ed8a3f47076e9af6" + }, + { + "path": "wordpress/wp-includes/class-wp-network.php", + "size": 12296, + "sha256": "9d4427d96e30b4d64e3c8ed21e0088b8a8f2f6ebc87a44d858eb9ec3587bb78f" + }, + { + "path": "wordpress/wp-includes/class-wp-object-cache.php", + "size": 17524, + "sha256": "1857b774bd9cfbafbd50a1b1424f7df637d202cf3f2253cda9798d4ad56f3cf0" + }, + { + "path": "wordpress/wp-includes/class-wp-oembed-controller.php", + "size": 6905, + "sha256": "6cce56b04bab7d6e6eb4100223c097f5c3d843c069b9853336ce4e5d8bc8769a" + }, + { + "path": "wordpress/wp-includes/class-wp-oembed.php", + "size": 31603, + "sha256": "e393199542d6a35d421a9966beb6e169a8495dbeafcfe11924a86346ad2ad43b" + }, + { + "path": "wordpress/wp-includes/class-wp-paused-extensions-storage.php", + "size": 5067, + "sha256": "b01790a325583ce28622201429af2d90072f0868db21b80414340ea1c783ca53" + }, + { + "path": "wordpress/wp-includes/class-wp-phpmailer.php", + "size": 4348, + "sha256": "4033b95890bc5f228381f55c89cb538ac02ad624f2db35758a98cc9b56b8d33d" + }, + { + "path": "wordpress/wp-includes/class-wp-plugin-dependencies.php", + "size": 25182, + "sha256": "2c986c21047d71b3c68ab1fc9e94a78975650607325bf5e8965ed2ee7e07d942" + }, + { + "path": "wordpress/wp-includes/class-wp-post-type.php", + "size": 30672, + "sha256": "f6c85fc833545d80d1c389709ac0881600b15a5211f6538471fa317d09584da7" + }, + { + "path": "wordpress/wp-includes/class-wp-post.php", + "size": 6483, + "sha256": "5906db266f6b273f621c4043767320b883eaf827b1b1f833a201bfa9bcea7bc2" + }, + { + "path": "wordpress/wp-includes/class-wp-query.php", + "size": 163427, + "sha256": "85d8a29a79c5f1e91bf1a9d75433bef14954a1233ea72b6a441431cd7b16d792" + }, + { + "path": "wordpress/wp-includes/class-wp-recovery-mode-cookie-service.php", + "size": 6877, + "sha256": "0290d5e6b418968e1bc03b2618593a1822e148a6e19e4b7accd35aefd58f6b59" + }, + { + "path": "wordpress/wp-includes/class-wp-recovery-mode-email-service.php", + "size": 11166, + "sha256": "5571fbaf0f127adcd8418f3a5d6b7d7b7550d721095804af5216f9109cc0711b" + }, + { + "path": "wordpress/wp-includes/class-wp-recovery-mode-key-service.php", + "size": 4914, + "sha256": "61d85a8e341a64a2e064edb3493ceaf2acaf0ef72d03a566c163433b4eb4729c" + }, + { + "path": "wordpress/wp-includes/class-wp-recovery-mode-link-service.php", + "size": 3523, + "sha256": "999f257e2e696474340758028f7dd8adf58e28fd36e7e0f8c84e7ea63eb2e740" + }, + { + "path": "wordpress/wp-includes/class-wp-recovery-mode.php", + "size": 11453, + "sha256": "f23a6fb3bd2c32dbf9d324d8274598ee47e7f3293b307f5f02b16fda8c9c42fc" + }, + { + "path": "wordpress/wp-includes/class-wp-rewrite.php", + "size": 63693, + "sha256": "e197e1f2a78b4b6ffffd1687bdedf0c254d2a8cf013f6d8c8e55c0ac53facd3e" + }, + { + "path": "wordpress/wp-includes/class-wp-role.php", + "size": 2523, + "sha256": "9ab9ad4a29fa3bc9e03c4f9a61fc91b407dbecb3d1f6521f706deaca5ca55b3e" + }, + { + "path": "wordpress/wp-includes/class-wp-roles.php", + "size": 9321, + "sha256": "b116231e11cafb3698a39ace2663b69ee7488e4128a0c2ec250f2414094aba12" + }, + { + "path": "wordpress/wp-includes/class-wp-script-modules.php", + "size": 40599, + "sha256": "57ed2b19d0a3e29ca502757da2ea4b869ca3a50859b183401f8ab5e8b234fcc1" + }, + { + "path": "wordpress/wp-includes/class-wp-scripts.php", + "size": 36789, + "sha256": "9101babac5e96713d697b0dc6d30b70dedf01bfcfec5f9fb46602b5f096c384c" + }, + { + "path": "wordpress/wp-includes/class-wp-session-tokens.php", + "size": 7319, + "sha256": "0708de7171949675139eda1e476935763f65e8a3a0cf40fc534dfffa00fb04cb" + }, + { + "path": "wordpress/wp-includes/class-wp-simplepie-file.php", + "size": 3552, + "sha256": "f41391177074bfdadd2264bd916496b1e60ab4aeba5896c46a29defbfefb6ca4" + }, + { + "path": "wordpress/wp-includes/class-wp-simplepie-sanitize-kses.php", + "size": 1910, + "sha256": "9d2575c139f2e85b0c505623e2ef887b02c93b1d30dbc23312b9603e48f155ca" + }, + { + "path": "wordpress/wp-includes/class-wp-site-query.php", + "size": 31482, + "sha256": "2da02c34ae38b92bf3fe7f8f69e40c4e11aa9391986e50a2f3dece668a744570" + }, + { + "path": "wordpress/wp-includes/class-wp-site.php", + "size": 7459, + "sha256": "29317614309b1cc198b3755c11829d329303c20c619f209a971dcb568c1bba1e" + }, + { + "path": "wordpress/wp-includes/class-wp-speculation-rules.php", + "size": 7554, + "sha256": "fd3c436c393c11cb726bcb2ff859eb9c59a90ec3b72037e1d944cde288e90a0f" + }, + { + "path": "wordpress/wp-includes/class-wp-styles.php", + "size": 13356, + "sha256": "9c40562eaed2dd3f282dca01fa15f00d2fc4cac0223bc9e3152c53604dc5e132" + }, + { + "path": "wordpress/wp-includes/class-wp-tax-query.php", + "size": 19577, + "sha256": "22eb4845078ec33afd9e5123c88817002542ce38f14ade73cf0a498ad5236c39" + }, + { + "path": "wordpress/wp-includes/class-wp-taxonomy.php", + "size": 18559, + "sha256": "055b2da70596c7d78efb51892b6e078940a35e6c9f0963a16eb43915f87a0805" + }, + { + "path": "wordpress/wp-includes/class-wp-term-query.php", + "size": 40751, + "sha256": "af18376c0aa11cac728190fac6dfe59770a1827fcfeda13ec95b53e4a8c70fbf" + }, + { + "path": "wordpress/wp-includes/class-wp-term.php", + "size": 5263, + "sha256": "4bceedaf4397db32016cc8e8032d4c02c2d1dda2ad966cd56a6b99d75c7f32ef" + }, + { + "path": "wordpress/wp-includes/class-wp-text-diff-renderer-inline.php", + "size": 979, + "sha256": "eb7be4557c6f9cd77c430c3212a09325c97a9ba0addf4747a48a26739d3bd439" + }, + { + "path": "wordpress/wp-includes/class-wp-text-diff-renderer-table.php", + "size": 18932, + "sha256": "5c3a6b80c0ead9655290915a3aa860df0ddd6dfe3f25c328000d3e841a43d98b" + }, + { + "path": "wordpress/wp-includes/class-wp-textdomain-registry.php", + "size": 10481, + "sha256": "c2c44ce46450364208968b731102497fa3dd3323edfd0357bfdeabc98dc6b40e" + }, + { + "path": "wordpress/wp-includes/class-wp-theme-json-data.php", + "size": 1809, + "sha256": "235d92ab6a7b610f52541a2d973ff256b21760cc8ec3bd4675d5c831d9b4debe" + }, + { + "path": "wordpress/wp-includes/class-wp-theme-json-resolver.php", + "size": 35692, + "sha256": "8c1eae3025d000dfe828e76f2630caa54084c619b1bf7d1e627108531b433bbe" + }, + { + "path": "wordpress/wp-includes/class-wp-theme-json-schema.php", + "size": 7367, + "sha256": "367fc9d2f13ce136f1334e0e8e0da063812f437c46a0734ad0a76423ba3b35c7" + }, + { + "path": "wordpress/wp-includes/class-wp-theme-json.php", + "size": 173639, + "sha256": "549d50cd230c2308e9828ea8a4c4ae91e5511184ad96e1365ccd5ad02cb51cb8" + }, + { + "path": "wordpress/wp-includes/class-wp-theme.php", + "size": 65761, + "sha256": "ef90d3824a80485a7163e2582ff2c44cc1b8269980e87570f060fe6a82d3f623" + }, + { + "path": "wordpress/wp-includes/class-wp-token-map.php", + "size": 28618, + "sha256": "ddcf4f86ac3a37cb2c7d833bf2c40e709ec589be05fc6cf83cb6e87a1161b93f" + }, + { + "path": "wordpress/wp-includes/class-wp-url-pattern-prefixer.php", + "size": 4802, + "sha256": "bac44ba2478e3bed056f6e3c043b36003daabe497ed287bcf1532a4aa222bcee" + }, + { + "path": "wordpress/wp-includes/class-wp-user-meta-session-tokens.php", + "size": 2954, + "sha256": "bd32717507322f0e15234d546c4938c8e2d092d7166d4055f8ec6815ed09daa8" + }, + { + "path": "wordpress/wp-includes/class-wp-user-query.php", + "size": 44102, + "sha256": "ea9ffe56294efba4a7c2f0075dc6e406942661aadb53a16911a94cbe899c49a6" + }, + { + "path": "wordpress/wp-includes/class-wp-user-request.php", + "size": 2305, + "sha256": "c4d49a59c41793c2aab4b5c9820bf1d2a4936abf2245858a4168e82a72705230" + }, + { + "path": "wordpress/wp-includes/class-wp-user.php", + "size": 23016, + "sha256": "5628d507a0979e34a439ad80caba9c5127df53bf8150d86c93b71b1e3d1a5543" + }, + { + "path": "wordpress/wp-includes/class-wp-walker.php", + "size": 13322, + "sha256": "9ae48531bc60636947cfc163671e354746ee05693169f643ee539660f8665755" + }, + { + "path": "wordpress/wp-includes/class-wp-widget-factory.php", + "size": 3347, + "sha256": "e7ec63244377a3a5bbe6821872d1e367f83bc73cc20ddf6d7b4e316b6b3933b6" + }, + { + "path": "wordpress/wp-includes/class-wp-widget.php", + "size": 18417, + "sha256": "41a1eb7497340c36e11a95b8bd08fb881ad1bbb50c4c6f687e0551e1b17bbc02" + }, + { + "path": "wordpress/wp-includes/class-wp-xmlrpc-server.php", + "size": 215020, + "sha256": "d65a9667e32d9deb7d17eaf71c66091f4f7b990374bae8bf787e1e7026e53703" + }, + { + "path": "wordpress/wp-includes/class-wp.php", + "size": 26371, + "sha256": "cfbef652f7093d5545a8365bf48fec96649786952ee6f99c55cf3902012bed2f" + }, + { + "path": "wordpress/wp-includes/class-wpdb.php", + "size": 118638, + "sha256": "99a30801ae7ed3ddb6faf5feef3b564210c8dc681fc42b8cd1ea7da0904218b5" + }, + { + "path": "wordpress/wp-includes/class.wp-dependencies.php", + "size": 373, + "sha256": "7a8a767a76a510db6394042572c47bf15bd1ebe486fe595bb710f5aaebf1be99" + }, + { + "path": "wordpress/wp-includes/class.wp-scripts.php", + "size": 343, + "sha256": "40f9b80530bc0e78638344db4c0d2e3ce1ea6165fd66c1a3e71eaee602934c6c" + }, + { + "path": "wordpress/wp-includes/class.wp-styles.php", + "size": 338, + "sha256": "a6b986fd22a610e6f758b4e6bf845d3ad932d8324d0e260e3f8f8655ce54448b" + }, + { + "path": "wordpress/wp-includes/comment-template.php", + "size": 103211, + "sha256": "a8266487acbc0b4940f3091b85c14d46fc27dc8077720785818113f1ae2d00c5" + }, + { + "path": "wordpress/wp-includes/comment.php", + "size": 134085, + "sha256": "77f7874661ff4bf687fc1d2c7bdc84a02264f6bda35fa901e38afd5801b2dd74" + }, + { + "path": "wordpress/wp-includes/compat-utf8.php", + "size": 19554, + "sha256": "3bf48363bbce945aa08c2a8a73abce77a52b810171f2698a3d6924e31d1ab34c" + }, + { + "path": "wordpress/wp-includes/compat.php", + "size": 16063, + "sha256": "3b87e422b612fdf23d26e600552aa1f0444c8db49b2d079538a9e460db0a4025" + }, + { + "path": "wordpress/wp-includes/connectors.php", + "size": 24080, + "sha256": "bb0bacd849fb5185dd5dbe4b7e44489397be4a572bfcd13bdc537c2fc140bea6" + }, + { + "path": "wordpress/wp-includes/cron.php", + "size": 44996, + "sha256": "9a19fc154c01bd6a0adb46aa093c2d85cf42a7e2214c9f80f350f0099aee1e32" + }, + { + "path": "wordpress/wp-includes/css/dist/block-directory/style.css", + "size": 6213, + "sha256": "c0a3e729efc64aa5afd07fbe4b5d57abf6326584d4eea17d7df363c627fb0711" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/style.css", + "size": 133659, + "sha256": "4ccb0abb73d8b09a1655304febc60ec032f0910221cab9d898d6320fd61e2c3a" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/style.css", + "size": 153282, + "sha256": "c7b754541e5b0a410f0ad6566516db8a08b897fd08117f158d53a7f18404a060" + }, + { + "path": "wordpress/wp-includes/css/dist/commands/style.css", + "size": 6357, + "sha256": "881687c799b5140228e3cbab215b66cd0274e34e91e285c18e48db94acaddad0" + }, + { + "path": "wordpress/wp-includes/css/dist/components/style.css", + "size": 117568, + "sha256": "e7fd1949b0bc808891e9ad540caa58cb61b8050718f6d1272d470ba679281880" + }, + { + "path": "wordpress/wp-includes/css/dist/customize-widgets/style.css", + "size": 8581, + "sha256": "453b46b535b95241a846bcf57eb1146999c75c662b9952eb733e0042a8ad1f80" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/style.css", + "size": 15702, + "sha256": "617716501008f893bb49309859ffdccba1360ee4d72e80e3121c5ea7525c2fd4" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-site/style.css", + "size": 153528, + "sha256": "1ee7783958c45849823f1b293682da34a95131e0737f3e097e606d2b4dcbef2f" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-widgets/style.css", + "size": 27721, + "sha256": "0f827afd1731e93659f7e2b23e4e0fd89c2eeb618db4f0cfdb1dccf6cd6b85b4" + }, + { + "path": "wordpress/wp-includes/css/dist/editor/style.css", + "size": 177745, + "sha256": "27335f13b0dc36ecb157503ba083f607af1f121f5f00ddd50ec9f365897443f6" + }, + { + "path": "wordpress/wp-includes/css/dist/format-library/style.css", + "size": 1670, + "sha256": "3750c59ad3a6f1f6d4527187f97ddcd33e493fed3592695b3144c51ba347d210" + }, + { + "path": "wordpress/wp-includes/css/dist/list-reusable-blocks/style.css", + "size": 2388, + "sha256": "0ad8372107a4225d6191493ba3c7901e3516dab0e31b616c246ad559a5fbac1e" + }, + { + "path": "wordpress/wp-includes/css/dist/media-utils/style.css", + "size": 6089, + "sha256": "592f73b607df347c9e23fcf565c34bcc9902b2e1e9fa1d55ba0ad09b39622695" + }, + { + "path": "wordpress/wp-includes/css/dist/nux/style.css", + "size": 4491, + "sha256": "18fd382c0a9b23137fae87f2f36cf4a762884f16ef448ab8d5e18f5dccf503e9" + }, + { + "path": "wordpress/wp-includes/css/dist/patterns/style.css", + "size": 3415, + "sha256": "5267e0f84580acb1ea27cf7a39a20c426799b26642f11bd7b68c4e4a2cad9b52" + }, + { + "path": "wordpress/wp-includes/css/dist/preferences/style.css", + "size": 2969, + "sha256": "a70cca260c27d6010047b32bfb1d36da1c2873b1b06773e069c5d00369c2c4b4" + }, + { + "path": "wordpress/wp-includes/css/dist/registry.php", + "size": 2971, + "sha256": "23b1439710b2abee71593ce5d55ef069d3f901c6f52472bb9b15d225e81ef5dd" + }, + { + "path": "wordpress/wp-includes/css/dist/reusable-blocks/style.css", + "size": 2411, + "sha256": "5370b38fe9984a1fa0cda86fbbad828a20f700c19da937a600f3e5514f293297" + }, + { + "path": "wordpress/wp-includes/css/dist/widgets/style.css", + "size": 7936, + "sha256": "883823a9dd8c1d4e2197e161455a4257033d7d39c56722905d5e437df2784da1" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-background-image-control.php", + "size": 1284, + "sha256": "cad5ac81c7cfebbf614c3e5ec5a8d58972a400a6f53f86b091ec0c5335b63bb4" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-background-image-setting.php", + "size": 782, + "sha256": "26d81702d9e91d0d605f119dc83e8e211b853437d2c9b52083098e719e5571f9" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-background-position-control.php", + "size": 3032, + "sha256": "0b58e6d333f2c9ca78fc4f5727f6938576158c3034535897912e0c7429763525" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-code-editor-control.php", + "size": 2317, + "sha256": "4b81784a0a2cfa8c94982c9673a473accd359a01241f944ebb897c6f93519d9d" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-color-control.php", + "size": 3209, + "sha256": "f5572594b833d700775ed2d6e0a33b2b37e1b6d0ed74ff201092a49af9bd8957" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-cropped-image-control.php", + "size": 1459, + "sha256": "54a6edf492b8ce8c923901e4e79020d50fc6b4d3b9dd34d5df97cacf9839d896" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-custom-css-setting.php", + "size": 7693, + "sha256": "d0552a8b841953749cd422e9ec46070cda44ef4737902c4cac1d2ef3f426f0f9" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-date-time-control.php", + "size": 9446, + "sha256": "87ae2ba5dc4040b41feed9bd4889d354a0b1c0ac0d58ec508ce4d35f780a7244" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-filter-setting.php", + "size": 733, + "sha256": "8ced4c4100bf20b1820cae29ce2bb6e1aad0d3b39de6c59ef117beab42dde745" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-header-image-control.php", + "size": 8313, + "sha256": "aa19b68968812a1aa37b83850ea0bdcaa1c0d751514d585eb61bd076dfab7fad" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-header-image-setting.php", + "size": 1838, + "sha256": "91dd71276f9a1b580b665e5ce8e2b97e4f425514f15e8e44d9c9e8c7f9099156" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-image-control.php", + "size": 1211, + "sha256": "7518716f32e9a61bad4e3147b36d5d38f9c12efa19c803723a0620ba35ec60ce" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-media-control.php", + "size": 9466, + "sha256": "967ea412810ae7ff236e39df040ce37af7577f35c41c966fdd46c703fce9c4b4" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-auto-add-control.php", + "size": 1122, + "sha256": "bea73e889f6d9549ed697cf3ff223395d537cec2fce37106ed8e572855c7bd1f" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-control.php", + "size": 2128, + "sha256": "0051e729307bd1a9c6d9bbcb98e34704ac5e67715315753c948d537cd0eb3975" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-item-control.php", + "size": 8157, + "sha256": "3196235075f67a680c4a6473874e850a8a23cbb780f373c26c6504a0efb669de" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-item-setting.php", + "size": 28075, + "sha256": "0a871dcc2601d3b4e62f0fc845b77cfd2d76a7f5ef59c68a83e866cc8f9193ca" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-location-control.php", + "size": 2299, + "sha256": "be3a7a55b122ee12fbd02319ba0e59b0c90e48f080c95eb4c246f28a59551a74" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-locations-control.php", + "size": 2817, + "sha256": "b921e1db51a7e51affc585b1d385f9b6dec0ec27ad30a00b6ea83d96f71c98e1" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-name-control.php", + "size": 1131, + "sha256": "4e7466018dcb5fbd09729a8b0543215086877c0f69f43e09eed7017f28a94ef6" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-section.php", + "size": 716, + "sha256": "8a094a80d492272b228d233ea44bea38031268ca1c1f742a1161eb0f26ddb1b7" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menu-setting.php", + "size": 19128, + "sha256": "7ccfed9452aafdded56adb55fde6a38306a06fb4e74584cb5350a216d61c52b8" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-nav-menus-panel.php", + "size": 3309, + "sha256": "9ec8c90f3cc34346fe8c88e894c642387be123e65284bec796b03b3fd5835999" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-new-menu-control.php", + "size": 1710, + "sha256": "319f7fe4ea3a8f48f85b97cc360d13750673caa79ae22be0d00c1b56f0c89ae0" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-new-menu-section.php", + "size": 1693, + "sha256": "06ba70726a8d0b6aea0cb6ee8eaf5a7289c3a8c6677d6b35e3942687b15aa64e" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-partial.php", + "size": 10543, + "sha256": "aa4e6b7ad230107ee323d51531a1f7c14c12dd8886ab20df5d75fec649d17074" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-selective-refresh.php", + "size": 13843, + "sha256": "99cb09c063844f87d454edb90006fbe7aa364ed4e78345503fb2a534f4fcaa21" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-sidebar-section.php", + "size": 1059, + "sha256": "0cd74a4ddeb9251788a2588fbefb586c87da1d6b320263f60fe64070f2430523" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-site-icon-control.php", + "size": 5163, + "sha256": "332d6feda684a2c0fa0c5fc185c598b4d320d4b4de78f8b9f4ceba65a5484300" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-theme-control.php", + "size": 11889, + "sha256": "d71f6b0b561248e6d8d7844aceb0b8019f075a8104c8bbf5430a195369efc55a" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-themes-panel.php", + "size": 3385, + "sha256": "b42b78f191e447c528211dfb3b909c99e00a9d9ab1e68784fb775199130bb14d" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-themes-section.php", + "size": 6955, + "sha256": "5a4be2ffba784d971a4b673a5a0a13bb93ba6078f5b1beb8df47210f9ee09235" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-customize-upload-control.php", + "size": 1197, + "sha256": "b92fffbb6fdd0320cb2a18a9ae342f9dae512c02787e5b2c74ca8a8444e85805" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-sidebar-block-editor-control.php", + "size": 686, + "sha256": "23bbb362d70cb4f11acb5b811a63e85138bf4f8a93b6cf3601628c302f9c78f9" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-widget-area-customize-control.php", + "size": 1719, + "sha256": "41b6cbe29c8444f966f2575b424dcae77c5da517637576ae50e6d6fbd664ef53" + }, + { + "path": "wordpress/wp-includes/customize/class-wp-widget-form-customize-control.php", + "size": 2646, + "sha256": "549f10c7e269ea5919a0ca6389b273e6b09fabff2ccb2bbe106eabd3ba6ae0f4" + }, + { + "path": "wordpress/wp-includes/date.php", + "size": 400, + "sha256": "a909be47ae10b8c401a2713c790ee3b656c8cd62f1f4b39c73b0e20e6aa78f0b" + }, + { + "path": "wordpress/wp-includes/default-constants.php", + "size": 11365, + "sha256": "95c3a9ac07e13ced91017b059c3d38c1a3d827ba0293ff162da83e3d9bee4e72" + }, + { + "path": "wordpress/wp-includes/default-filters.php", + "size": 37417, + "sha256": "fc79d24c0264c7afdc22fc8ff907bbe09ae7edcb65becefde8d474b8caa03f7b" + }, + { + "path": "wordpress/wp-includes/default-widgets.php", + "size": 2295, + "sha256": "c5f82bc23d38a667a607be7f4bc30f9d4baa20b6cf3babaaafda09a83c74adb4" + }, + { + "path": "wordpress/wp-includes/deprecated.php", + "size": 193977, + "sha256": "a0096570cb00b2f04b362b6a9af46851bd48a80f332392dfcf004de077205159" + }, + { + "path": "wordpress/wp-includes/embed-template.php", + "size": 338, + "sha256": "5bedb63acacb3a3fa7c2d27a144ddc7b08ff84b00c84941b36911acedd33a5e8" + }, + { + "path": "wordpress/wp-includes/embed.php", + "size": 38809, + "sha256": "9b4c8ac50d3a42556a971426726e8d4ad881a88c05a87ea28d69492fe39cf5be" + }, + { + "path": "wordpress/wp-includes/error-protection.php", + "size": 4092, + "sha256": "d7fdd84586d1d579c9b311e4007929d190a5b4b3a5e3deac1beddfb1a54a1477" + }, + { + "path": "wordpress/wp-includes/feed-atom-comments.php", + "size": 5504, + "sha256": "726ec80a75978f1eb0b63aa98bb9e55282739e2c47b8756d9475c07de9681165" + }, + { + "path": "wordpress/wp-includes/feed-atom.php", + "size": 3121, + "sha256": "db17263776ea3291303ddcb5004b58fecd5f00ef107b56710ee2b2b6b77e9595" + }, + { + "path": "wordpress/wp-includes/feed-rdf.php", + "size": 2668, + "sha256": "da3b03eb0e15fa0985b00744f5bdd45d105ea8b474e4098c9266fa35554597f7" + }, + { + "path": "wordpress/wp-includes/feed-rss.php", + "size": 1189, + "sha256": "dac5014f327da3f8dea8ea61bcecc47318c0752df72872ee5b8bb1cc1c9e4496" + }, + { + "path": "wordpress/wp-includes/feed-rss2-comments.php", + "size": 4136, + "sha256": "2d0dab01787ea4b11552b982beb38c12be04e1dec5150216f9e3737c5c748c67" + }, + { + "path": "wordpress/wp-includes/feed-rss2.php", + "size": 3799, + "sha256": "266c408a89aa1b102ab851fa16c7207c0d4af57b5acff610d589f02b4c88ec32" + }, + { + "path": "wordpress/wp-includes/feed.php", + "size": 25189, + "sha256": "db032afed0fbc3b0841d0133b11ee6a7819d0572640aa22aa4f74687a4f28036" + }, + { + "path": "wordpress/wp-includes/fonts.php", + "size": 9790, + "sha256": "b8aaa6ef585baf93646b5926fa4646cd6a4a8d5cabdcd2f89a0229be1596c22e" + }, + { + "path": "wordpress/wp-includes/fonts/class-wp-font-collection.php", + "size": 8892, + "sha256": "847f4ca1525b981fa557225bdf236351a963baa5af3a63b03c4ad31c47e7001b" + }, + { + "path": "wordpress/wp-includes/fonts/class-wp-font-face-resolver.php", + "size": 5652, + "sha256": "023c4201de6f178bf9e9454876c37d553fa727cac6a5317a4d20419cbcd8e595" + }, + { + "path": "wordpress/wp-includes/fonts/class-wp-font-face.php", + "size": 9170, + "sha256": "8f1324428edd6f894133f9c52d72064b2f7cd62f558698ed8c48c5e261b615bb" + }, + { + "path": "wordpress/wp-includes/fonts/class-wp-font-library.php", + "size": 3499, + "sha256": "f1bb3795d84c5c394c3841fc0b5d60c1efa4c67202d66136ed714701ea3831e4" + }, + { + "path": "wordpress/wp-includes/fonts/class-wp-font-utils.php", + "size": 8888, + "sha256": "13484cc515cc818691e011f5b5444c8bf8b3ccdac1ace841f84850db7941d6b4" + }, + { + "path": "wordpress/wp-includes/formatting.php", + "size": 354920, + "sha256": "4e6d1740465e06fc2bbf6deaf666792e60f8c71964d5e5f7b9a6400261144a79" + }, + { + "path": "wordpress/wp-includes/functions.php", + "size": 290326, + "sha256": "1da1a76b610bca873081ed4cbc68b5dce303df0fad16570e6abae1d50b236faf" + }, + { + "path": "wordpress/wp-includes/functions.wp-scripts.php", + "size": 20492, + "sha256": "7f1c8945f97711927196d6e01216443b43b70bd1530c5013d53f20238757cad4" + }, + { + "path": "wordpress/wp-includes/functions.wp-styles.php", + "size": 8654, + "sha256": "1fe0a2c891154fa6e6fdfcabe91646b28d8428fe3a18e0c136392b6865de9866" + }, + { + "path": "wordpress/wp-includes/general-template.php", + "size": 174934, + "sha256": "7c2fc542b298c5d3aac6e4e86fbf25e10e3accfe4fe9b16ceffffe5154c36722" + }, + { + "path": "wordpress/wp-includes/global-styles-and-settings.php", + "size": 20780, + "sha256": "45b501595e73a72b29719b141ff27b8c8b926409052f3e6ec6c5e957bdbaddfb" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-active-formatting-elements.php", + "size": 7275, + "sha256": "0c59f6ba6037ae16810235bb08221b5dfe107c403fb4f81ab03c38508a7a88d0" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-attribute-token.php", + "size": 2789, + "sha256": "1a3dd4adfd4c86d0fc2210c4872a4eb192dd9fbce1f946ccf6826155179b973b" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-decoder.php", + "size": 16684, + "sha256": "0e6dac39cc7c0a317b590cab5826db911e0c9ce870acc50fd482fd52d853d4db" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-doctype-info.php", + "size": 25382, + "sha256": "007027921e6e948c6eb6a3a9e710c5f53cdfc549ae269da944ff5988abca03da" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-open-elements.php", + "size": 22513, + "sha256": "60c24725f771f06cd34d97b6732705f2b1cf34c7f136aeb541c9f60ed897809e" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-processor-state.php", + "size": 11346, + "sha256": "b11f1743de3e2a236f6531a72fd278c459f9d6a518927cb9f04fb8602be8a873" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-processor.php", + "size": 215460, + "sha256": "1cc9b0db834b505ff9cf3771d1527f7e6be653a7dd6a9de60a627c9a54f380df" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-span.php", + "size": 1099, + "sha256": "b702cd418b898a373316e71d22e51ccf60943275ccc96a8f39e76c3f51b86174" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-stack-event.php", + "size": 1653, + "sha256": "ae251a92564e60c45aca690c476af9c19fa2dcece0d771e0dfd406f993e5c049" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-tag-processor.php", + "size": 167607, + "sha256": "0e5756430bd64308766474522d313ffcf74bd7c6b7908a82449889fc09dc8792" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-text-replacement.php", + "size": 1409, + "sha256": "fa5d1dd7457cf6e884f713e674d4174b67f3ae0039e06c239defdfa84f8b9620" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-token.php", + "size": 3410, + "sha256": "c9f718e6d79dc58d2bc185ef12ec584a88bf6eae2cce31f862be9ba96806084e" + }, + { + "path": "wordpress/wp-includes/html-api/class-wp-html-unsupported-exception.php", + "size": 3606, + "sha256": "e82869802ff119d4b34c654d8be60c2f9459a481567b04db960ed8c2e0fc658e" + }, + { + "path": "wordpress/wp-includes/html-api/html5-named-character-references.php", + "size": 80163, + "sha256": "2a3941baab9845abd796d5243dee4e2e4b6824c2bc6a4b9b9ebf8a71604275ec" + }, + { + "path": "wordpress/wp-includes/http.php", + "size": 27255, + "sha256": "7a63a77f4756080479635163a819db92f3367d9d45df3964eedac1e477c3b552" + }, + { + "path": "wordpress/wp-includes/https-detection.php", + "size": 5857, + "sha256": "d55a1af8e28ba498d7f5dd07f95210f53d8c717ea215f044e2edb7ce30d492e1" + }, + { + "path": "wordpress/wp-includes/https-migration.php", + "size": 4741, + "sha256": "e88d72e563571324c4d52faa87454659e12b9c3b2526b2bf658bcff3cb952427" + }, + { + "path": "wordpress/wp-includes/ID3/getid3.lib.php", + "size": 55371, + "sha256": "564b966339ed18d7e65dbddfe8852e274cb73458c34901227cc2a890c01f52e1" + }, + { + "path": "wordpress/wp-includes/ID3/getid3.php", + "size": 81242, + "sha256": "cfb84ed7624486c527ab8126dbc1503a7a448fd8577f8b13fb6ecf769fda6c9f" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio-video.asf.php", + "size": 136815, + "sha256": "045a6053550022cdc7e2d5508c1e99ac680372cda9532bfb0ac59811f6a4d85e" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio-video.flv.php", + "size": 27132, + "sha256": "9b6581ab40baa538dae84ffc055ff111ab67b16ea2da919890d2491ef057a91b" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio-video.matroska.php", + "size": 112016, + "sha256": "d90f2d9db3938373228febc1d96b1292d0fe8bc0da5c75afdf01c08a2ec0d0b1" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio-video.quicktime.php", + "size": 172638, + "sha256": "1795add5fe254d5c158af0b1d7914bc7bb8b2267049ef2fb7a2edd139e3540fe" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio-video.riff.php", + "size": 140115, + "sha256": "d282d4335a252ec8af44e570bc7943c89c8629996c8cfdbd76a64e18a0902c6a" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio.ac3.php", + "size": 39384, + "sha256": "9da50e3a352229ee185230f171da91f086e1006c368d3f9a9d2731cdfe2fca0d" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio.dts.php", + "size": 10886, + "sha256": "5a169c187a4c7bb0c5c8a8028f2989265cecf5486bb6cb449e0735ab7d4303b2" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio.flac.php", + "size": 19797, + "sha256": "278a814ddc023de7e24f98bef17797a2125b08a722a930392295fa92739737fc" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio.mp3.php", + "size": 107012, + "sha256": "d8240510da9b7011848f2c45fbcaaba2ecfa9bd10ca6d0e1212fc09078ac84b7" + }, + { + "path": "wordpress/wp-includes/ID3/module.audio.ogg.php", + "size": 43761, + "sha256": "6c20143e9e2a958f3546fed41941ddc90a3faab20888a8bab13a7b0a4591b7ad" + }, + { + "path": "wordpress/wp-includes/ID3/module.tag.apetag.php", + "size": 19076, + "sha256": "52727cc041d8205c2d61de3f02f3784dce28cba65bf596e736bedf53484bbdea" + }, + { + "path": "wordpress/wp-includes/ID3/module.tag.id3v1.php", + "size": 15178, + "sha256": "de1af4b3774c570583cba427a2bfab39f3f2025d75538d688949aca8abff9aa8" + }, + { + "path": "wordpress/wp-includes/ID3/module.tag.id3v2.php", + "size": 154826, + "sha256": "951c3efaacbcab2137cdb0078461e695fa7f5378d2f69ca9aac34a9a3426a659" + }, + { + "path": "wordpress/wp-includes/ID3/module.tag.lyrics3.php", + "size": 12036, + "sha256": "26bb5443a3420c8d8d6d5981bd06722c3e48eb874b1a7e551fa3246ba3e58be1" + }, + { + "path": "wordpress/wp-includes/interactivity-api/class-wp-interactivity-api-directives-processor.php", + "size": 7764, + "sha256": "725bddf76f0ddbce21f602e02d728165a825d88f8324e81761becad9faa26230" + }, + { + "path": "wordpress/wp-includes/interactivity-api/class-wp-interactivity-api.php", + "size": 50608, + "sha256": "d47be554766bac49ea7910f40119ba7dda137163b860c48be4b8af56f2c07941" + }, + { + "path": "wordpress/wp-includes/interactivity-api/interactivity-api.php", + "size": 5034, + "sha256": "762b3becc984afb354e4e9a5b39d4855e8bb0c0ffc3f3acd680b18f62072ebb6" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-base64.php", + "size": 414, + "sha256": "b6af5e8592b8fe0c367bc30a41ef22551bf4ae199496302d04885169b6c69525" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-client.php", + "size": 4749, + "sha256": "5b397dce6855ead746fa820268b8e80ab578057a90e8df4bd95358630378e95d" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-clientmulticall.php", + "size": 1263, + "sha256": "762e7c5f3146d9508ba4323f6419112d64898714a2c51453fb835e072d8e13b6" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-date.php", + "size": 1691, + "sha256": "b2e1772879cf36db6f539d6186c035936d0031afb87f8f3c4c216d16ed13a4d5" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-error.php", + "size": 854, + "sha256": "0f7091d217124ee407e0f5c14f5e9a6bb96dc4962041dd8bb68d519e4ab0acaf" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-introspectionserver.php", + "size": 5366, + "sha256": "d1fd93bb5f882ea1e6a10f41659412bfbf3f23a26c8e94595aae9e525bba7723" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-message.php", + "size": 8409, + "sha256": "0b6a1854e5655083eb7b555308ae291921320c9f30812b988cc6f8d7fcaea8f9" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-request.php", + "size": 927, + "sha256": "fe2f8ffd6aeb5d3f69d68cf8f456271a2267bfd18e9675731c687e431201fc1d" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-server.php", + "size": 6768, + "sha256": "20e64cd2c9696cea901035f04f34c9d21726b2894dfbbf8e45f59b13e8c8fa4e" + }, + { + "path": "wordpress/wp-includes/IXR/class-IXR-value.php", + "size": 3790, + "sha256": "e8e3b954b66a2b0f0e17f2e79774cd93c6a10f05b72599194d1d712d983a69c7" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/a11y/index.min.asset.php", + "size": 83, + "sha256": "ea070d10d687f809f145d8259649c389b6f1ca5db5485bcc39066059d5c02f4e" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/abilities/index.min.asset.php", + "size": 103, + "sha256": "f41397210339e11103e0bae0f9ef916af428b57b228580166ef83b83cd748431" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-editor/utils/fit-text-frontend.min.asset.php", + "size": 180, + "sha256": "035bd14f2bf4354b74c2087f226a3bf336195e73ef1c5e2361e1a36c32a81585" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/accordion/view.min.asset.php", + "size": 180, + "sha256": "6492790aa1df07b0eb39c53a29479c4e1c95515c97770ca4a35eeb821c05a844" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/file/view.min.asset.php", + "size": 180, + "sha256": "a93a25d466a0e3302102486a456d8d71fa737b5cab4a1ed18ee31e1e61dada15" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/form/view.min.asset.php", + "size": 83, + "sha256": "86febfb51c8b6e144d79aa0fb824aa6f2e5b912fa3fa44f5cc9e53ecf8bd0990" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/image/view.min.asset.php", + "size": 180, + "sha256": "4843224081f3c1a928d6fc04b28531c5183c1b50aaba9c830aa327217844f05a" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/navigation/view.min.asset.php", + "size": 180, + "sha256": "829e6174b76db62f347e4a05a1903e163b767029d6fa38e21c14c7a9c7642c48" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/playlist/view.min.asset.php", + "size": 180, + "sha256": "389c66b5bb136f43a6044e46ab65153151fe779c14a69aa21ebb4e42805fca73" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/query/view.min.asset.php", + "size": 253, + "sha256": "d9351a98e669350c7c7e7c96ad1390a1ea7fcdd3d88e6eb65d5b7fd1ef42d786" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/search/view.min.asset.php", + "size": 180, + "sha256": "547ad8d5f6a4b7faed317ff570c259a93557104fc793020eb5b8276fb4456f3a" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/tabs/view.min.asset.php", + "size": 180, + "sha256": "44a9dd1dc9fbbcd8ab1756e5b21123c359758dc04af63cf7370957302b247318" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/boot/index.min.asset.php", + "size": 576, + "sha256": "b28f56bf8258d8a2749fb7674f1dc26d6e6e42b6b735a7c963d1aed5f9ed71e3" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/connectors/index.min.asset.php", + "size": 174, + "sha256": "9c27cc56ba9628729f22779bd116343185cb5ecfffefcc1be4c03aa02b193119" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/core-abilities/index.min.asset.php", + "size": 200, + "sha256": "7deaf1b0dab7b5ce14d1efc60269153643300c2adb24b7ed9502cbddd8b48ecb" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/edit-site-init/index.min.asset.php", + "size": 232, + "sha256": "2fc8805afd16d9111b44e089537582bf9492160aa6955c1d97ccd077f6ae8138" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity-router/full-page.min.asset.php", + "size": 188, + "sha256": "4da5561b69544f95ff48fa0d47dd8e5704c0834074de6d0f5fffb39c4bf3ec8a" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity-router/index.min.asset.php", + "size": 237, + "sha256": "4263be9c8cf25e140fbc55dbdd2b1ac94f8d08a25e077eb6bacb0a9e18ef217a" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity/index.min.asset.php", + "size": 83, + "sha256": "8945eef2859b2faca7f838e3421a229aa20b834e3166928836e11fb42b068f63" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/latex-to-mathml/index.min.asset.php", + "size": 83, + "sha256": "787d096e0fcc4ee0ffda54dd1b4f7150cfb48fca97929ac829dd1e99c79dbc57" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/latex-to-mathml/loader.min.asset.php", + "size": 183, + "sha256": "8846f044b68900f4d65bded2513c287b88dd82ca459dc7d80e34bea2eb8cc048" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/lazy-editor/index.min.asset.php", + "size": 254, + "sha256": "c7649d7f89e7f75415ccaa658de4661bb4b63021fdf12a5840aa61d43ae1d4ae" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/registry.php", + "size": 4104, + "sha256": "3db6e2e86b03ba58bcf07601e1d0249cb865edf22c3bd626c6a25a4e46a46499" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/route/index.min.asset.php", + "size": 143, + "sha256": "39d829506c4b30f25974052612922f8d0d0d2bf37b7f58d0a3db17c0ec2a6390" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/workflow/index.min.asset.php", + "size": 331, + "sha256": "cf98938d8df8ba74c7078e60b5fe60043871d5f7e6c82f96855ffb40bb2ffacc" + }, + { + "path": "wordpress/wp-includes/js/tinymce/wp-tinymce.php", + "size": 1042, + "sha256": "f6d4537829d35aeddad02d7ce1e33b037f43b7d6363243430cb911a85c9ea0bf" + }, + { + "path": "wordpress/wp-includes/js/wp-emoji-loader.min.js", + "size": 2890, + "sha256": "eb14c6387dfeedb3e8802eb27c8095e3f179aa8bdcf62486d948d8baedf37676" + }, + { + "path": "wordpress/wp-includes/kses.php", + "size": 82971, + "sha256": "bbcde5a3ba9e199947d72d065186273dd2c7755133376ba096c50400d0f499e4" + }, + { + "path": "wordpress/wp-includes/l10n.php", + "size": 71415, + "sha256": "43c9af6b687ee3eb92608fea4697ebe7b62b3d45490a7657efce71033ac82ed7" + }, + { + "path": "wordpress/wp-includes/l10n/class-wp-translation-controller.php", + "size": 12971, + "sha256": "e8a2d90c35c23780ef1a7de8e2995d625b7b6d20d9543438056be52fd4abfbec" + }, + { + "path": "wordpress/wp-includes/l10n/class-wp-translation-file-mo.php", + "size": 6282, + "sha256": "cead3a67453c73ff5064761ee7cb5b65cb2ee76a1bf389bdfcd15861af7c4ae6" + }, + { + "path": "wordpress/wp-includes/l10n/class-wp-translation-file-php.php", + "size": 1813, + "sha256": "3cd74773ad308df0f34654204e3cfebcc7b768ea3692e6751441fb9c1b8d5cc9" + }, + { + "path": "wordpress/wp-includes/l10n/class-wp-translation-file.php", + "size": 6281, + "sha256": "0a61f309cb1bb4327e796ee50ec624e502cad79222e36f5753e75c1007fe38c8" + }, + { + "path": "wordpress/wp-includes/l10n/class-wp-translations.php", + "size": 3810, + "sha256": "c3b72085e2134154489e8f12446f7ca313394d3d86abb4b50db6626beda6e764" + }, + { + "path": "wordpress/wp-includes/link-template.php", + "size": 160147, + "sha256": "7a3161d051b78c6eb5128b31e5f255f8c39b2ebfe162045bc48c8c1483cbbf35" + }, + { + "path": "wordpress/wp-includes/load.php", + "size": 56475, + "sha256": "3efd0934bd8a454a8d7076fbbd21504ea097a753125a48e64b8a1dad380d357a" + }, + { + "path": "wordpress/wp-includes/locale.php", + "size": 162, + "sha256": "0226059f58e6c074af93d3c764d68303270a5b55c503ef83614fa70f953d2def" + }, + { + "path": "wordpress/wp-includes/media-template.php", + "size": 63275, + "sha256": "aa9337e775d301d6edbb063129e3f7061a40a0322208bf4c57da836017e02c64" + }, + { + "path": "wordpress/wp-includes/media.php", + "size": 224932, + "sha256": "98375dee094f883930b92e3f9c9a078d9327cf7a3b85ad58c58d5c139022652e" + }, + { + "path": "wordpress/wp-includes/meta.php", + "size": 66739, + "sha256": "b1b49d1d3ddbf10d0f17286f53c30ce9582c636a4679f9a0ef92ee5ebb020a3e" + }, + { + "path": "wordpress/wp-includes/ms-blogs.php", + "size": 26331, + "sha256": "6803f4f666bcbc4e4479d79a06b3220a0ed9601893f4c95aa74f0d580a2c6922" + }, + { + "path": "wordpress/wp-includes/ms-default-constants.php", + "size": 4921, + "sha256": "c02130a6494102bcf567c14bef0378a750f1b9286cff85aa5a6be750e32d52aa" + }, + { + "path": "wordpress/wp-includes/ms-default-filters.php", + "size": 6636, + "sha256": "73c74a6893ef6cf2ce3683b10058077121bca17452495d668d9983db2d7c5f0b" + }, + { + "path": "wordpress/wp-includes/ms-deprecated.php", + "size": 21750, + "sha256": "236a9e298d66527b7e625ecee283ffc3474a7139bfc81df9b4059246c2629a65" + }, + { + "path": "wordpress/wp-includes/ms-files.php", + "size": 2857, + "sha256": "2703ed1a8fd9e6887aa64c3d06df9aba6b2e0a349ba627780dd8cfa7ecf1fa84" + }, + { + "path": "wordpress/wp-includes/ms-functions.php", + "size": 91842, + "sha256": "fb0edc671849a3561032981c5896811b9774d5a433f5fc3e0962e25d53780b51" + }, + { + "path": "wordpress/wp-includes/ms-load.php", + "size": 20038, + "sha256": "68303eb63594f268bbccf3d47d884caccd960d3a5e7afbae508713f7356f5a12" + }, + { + "path": "wordpress/wp-includes/ms-network.php", + "size": 3782, + "sha256": "14abdab01759573d3db21e105451ad3da65dc3e522292f753a1e002dd416f3f4" + }, + { + "path": "wordpress/wp-includes/ms-settings.php", + "size": 4204, + "sha256": "38865d6939823fe7acf48e6254807cab18fc78af77065cc2e9d07443e07ca037" + }, + { + "path": "wordpress/wp-includes/ms-site.php", + "size": 41729, + "sha256": "7c8fc3f104ac5608151b25265aa07620466da0c2d5f0ef819c76e8e8925d76f0" + }, + { + "path": "wordpress/wp-includes/nav-menu-template.php", + "size": 25990, + "sha256": "38b4e201499a218c5bdca55c234d2d2320709a900bc52ca54e8132ca08165061" + }, + { + "path": "wordpress/wp-includes/nav-menu.php", + "size": 44269, + "sha256": "ac0b7d23b3839121ca36c39480a6ff5eeca5b22c97ae69b75da74428e69ecadb" + }, + { + "path": "wordpress/wp-includes/option.php", + "size": 105079, + "sha256": "d1daad9a1d19ee438eb6e3b6698b42a3682f6f7ab9626be02f2157a238ea3e2f" + }, + { + "path": "wordpress/wp-includes/php-ai-client/autoload.php", + "size": 1410, + "sha256": "7fe20d44fd6013fd202cbe1edbd6ec489bd613e61c5511eaa74b45901dac69a5" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/AiClient.php", + "size": 17360, + "sha256": "8ac30c972f5408e8c9d8859ac76132f052586066199448eb239cd31613d865d7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Builders/MessageBuilder.php", + "size": 6586, + "sha256": "c19552fdccee11c49013e2ef56f84106bb31810995b1f66ba056ca439b84f947" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Builders/PromptBuilder.php", + "size": 55107, + "sha256": "00c04e0adf9e5bb254d002eb81ac408dc32ae38f3b02ff67330a4b94146cd209" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/AbstractDataTransferObject.php", + "size": 4733, + "sha256": "10a0f33e7619104a911be581700912aa827d26c683cde71e7ccf692ef4289b5d" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/AbstractEnum.php", + "size": 11376, + "sha256": "2175f8012fd5d6dbbedc4d71bfc0fd113d5da5000ca3b2ad528157bc2b4c2791" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/Contracts/AiClientExceptionInterface.php", + "size": 343, + "sha256": "13c73c7ffd7581bc8ab3ac52ceb0f41bfea3de0dfd21daaf5743693fdae980fc" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/Contracts/CachesDataInterface.php", + "size": 354, + "sha256": "8446bf53f5ce98cf477c898324ce5b1ada04d959c3612e6d8d6bde331a959dd5" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/Contracts/WithArrayTransformationInterface.php", + "size": 1051, + "sha256": "78a7d960e54abfc91f09db1aa8ad82df2797fd3369a5a6ecf5b4793c2579cb55" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/Contracts/WithJsonSchemaInterface.php", + "size": 606, + "sha256": "2b22fa57e034fcbbe7b4e2e5da539cdb4ff10020b8d721ac9b95200b601eb345" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/Exception/InvalidArgumentException.php", + "size": 487, + "sha256": "57693526f197ca902bbb38ffcba4b51550d174824c339fd4d1c64f1a9e86344c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/Exception/RuntimeException.php", + "size": 445, + "sha256": "33af8df289b4ac4c99bf772f573c8cf47c0b26a52d8fea637898721537ce5d0c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/Exception/TokenLimitReachedException.php", + "size": 1422, + "sha256": "c40d215b8f26cd69e26757ec6a620e763d2bb48a362255763cb751bf24445991" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Common/Traits/WithDataCachingTrait.php", + "size": 5106, + "sha256": "829df534c137bc44ef2e7ff0f00ddbd148e31995239a78990e1e7cfa04ba4dfa" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Events/AfterGenerateResultEvent.php", + "size": 3307, + "sha256": "0cd5b921a268a1d281ba045c334cdb6d53ba4208886e40e4827f598f4f049db7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Events/BeforeGenerateResultEvent.php", + "size": 2729, + "sha256": "b70e0c94fb375b686acc5441a14cf3fc5a1af53db376e9ac8c2a43b2bc91dde7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Files/DTO/File.php", + "size": 13526, + "sha256": "233ba2e64c7a2472975f16921b2b8cd5203b325217f11c8684d0ec384ccffe64" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Files/Enums/FileTypeEnum.php", + "size": 728, + "sha256": "7bed300f9bb03c9d2a9e3907cb8ab851ca4dc38368a958287abcd21fcf6d9511" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Files/Enums/MediaOrientationEnum.php", + "size": 984, + "sha256": "d5e028b9a076100976777a1d327764f5bc789fc629b6dcf505ee292be9ae561e" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Files/ValueObjects/MimeType.php", + "size": 8044, + "sha256": "a2012e5922f1b50658251c03a553a8ae6f3186f9d471886d2ab1fba81dd369a2" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Messages/DTO/Message.php", + "size": 5668, + "sha256": "1cfd955ec081fdb099083473df7b015b1872ebffc93f9996eab3b907c4584826" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Messages/DTO/MessagePart.php", + "size": 10797, + "sha256": "987c8b0a39a3518d48dd144cedcc0583e8a8f5c4a9f4f8d09016cb125c3f588e" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Messages/DTO/ModelMessage.php", + "size": 868, + "sha256": "f88592619e3d888838749fdf5973a1fb4f0bfe4939e81cbf52c25d9132b9b89e" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Messages/DTO/UserMessage.php", + "size": 812, + "sha256": "6c626969935adb006ea79b2f71143487103e99457e7b8ebce964e992a2edcba3" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Messages/Enums/MessagePartChannelEnum.php", + "size": 692, + "sha256": "aa7703b90ddd1794ee5763548a148c5a0102850d2aab4e69f22d0a7132fec8e4" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Messages/Enums/MessagePartTypeEnum.php", + "size": 1145, + "sha256": "18f5e749e46c00a9d4ddbc18d4964f4bc5ce46927ee1c85467fea76ec8eff520" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Messages/Enums/MessageRoleEnum.php", + "size": 676, + "sha256": "4917c5fbad489b17422da8f35b147a402b3b59e907c21838ddefc55bc6b8a8e7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Messages/Enums/ModalityEnum.php", + "size": 1287, + "sha256": "895d07e9097702b9f7ab834968173cf9b8a0c1d4c83c8993db386f1a2b773af8" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Operations/Contracts/OperationInterface.php", + "size": 779, + "sha256": "bfad4bdb3a50bb114190b24c89c9779664ba29e619ac94eb32fc875532eb4778" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Operations/DTO/GenerativeAiOperation.php", + "size": 5210, + "sha256": "7a6e8556d0a12f0f3c096ddd7e719e1a9500f34f31983d90ff17fe8b3772358f" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Operations/Enums/OperationStateEnum.php", + "size": 1347, + "sha256": "311409d80d9f438c8998e3f689d91b9c53cfcec5a42ba451988292e20deb032c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/AbstractProvider.php", + "size": 4108, + "sha256": "a94c600189afdfa04c056f4b957d9f243bb560efde32dab6d23896973e1f26fe" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/AbstractApiBasedModel.php", + "size": 3225, + "sha256": "f29ac0d5269e01334af72f45151c4b214c8ef4b1af180bcbf93d4d62ccfbcf06" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/AbstractApiBasedModelMetadataDirectory.php", + "size": 3317, + "sha256": "a8e3e30cd7d0a1e7e11c4699dfa70825a7a0cda9cade04f5908a424132ac7255" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/AbstractApiProvider.php", + "size": 1537, + "sha256": "70286bfcc197fcef75aee182d7f18d4a764ed54bf6c3ebe0ad1241efd44d8ce7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/Contracts/ApiBasedModelInterface.php", + "size": 1041, + "sha256": "34b213ee4ab8955d94c02868115856056b1043dfb0408e9898082819c224d1f4" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/GenerateTextApiBasedProviderAvailability.php", + "size": 2368, + "sha256": "033f26dec2073f7eb24e78d54af99b79bdfdb4895e1670e57d7f5baa59a6e3ba" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/ApiBasedImplementation/ListModelsApiBasedProviderAvailability.php", + "size": 1798, + "sha256": "a35d77473021d85494e558ee1de9e360c748c682264eec4c3c21d4abc9dfff95" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Contracts/ModelMetadataDirectoryInterface.php", + "size": 1255, + "sha256": "5b1ad205c9696054f1be46b6a9d3902c4c552842bc7d9ae4c38ad18047e4ee0b" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Contracts/ProviderAvailabilityInterface.php", + "size": 558, + "sha256": "b3952b2981049464f40d306d5acf0abea09a453601f16e037b12503456a99725" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Contracts/ProviderInterface.php", + "size": 1746, + "sha256": "d26b422655b5a91cf016876eb074a15ad0805e581f89ca9677053b8e1d02165e" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Contracts/ProviderOperationsHandlerInterface.php", + "size": 850, + "sha256": "0fd5848b4fa0098bb0d0eae62ee2563e05e4b5e116a86cf38c31d11ffd9bac19" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Contracts/ProviderWithOperationsHandlerInterface.php", + "size": 669, + "sha256": "db4c8ad3baa2783c6bda416c683faf68b86f57bfdd2fb5d6fd386165c14c2b44" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/DTO/ProviderMetadata.php", + "size": 7924, + "sha256": "a6451e4a1374ebf58c95e0e6f0bab067325a2d04f455d79a7111357f0677e722" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/DTO/ProviderModelsMetadata.php", + "size": 4196, + "sha256": "4d7d0c7b4a188f6d23616971d9663af37a43591f4b3893a09c802434518cff83" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Enums/ProviderTypeEnum.php", + "size": 955, + "sha256": "27c2f43a73c95a463ba9b838d2b69ba798008e6fdf6170ea2bf05f18b3faa7ce" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Enums/ToolTypeEnum.php", + "size": 757, + "sha256": "c08a0a0d26e97e0ccea67a1b5eeed4e502c421f3e16d140f7d4cf765f19e3d76" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Abstracts/AbstractClientDiscoveryStrategy.php", + "size": 2927, + "sha256": "4c3f5fb5c5f5d69cc75ed64474f2579e0b723e580d6c17c5fc430a7a6236bbc0" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Collections/HeadersCollection.php", + "size": 3849, + "sha256": "99e9dbd74ad471bd9801e1cb99955d7736798259ad9e599345bde14da0f2241f" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Contracts/ClientWithOptionsInterface.php", + "size": 1025, + "sha256": "537bf583d5d5182ef0a4261edffe6eb4449589226d90828b652665e3efcc53be" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Contracts/HttpTransporterInterface.php", + "size": 860, + "sha256": "47f9aeddbf50f690622232df3bb32bedaafe4e53022421b6cf73f139ad128070" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Contracts/RequestAuthenticationInterface.php", + "size": 621, + "sha256": "3a75caa6a1b578e8fe297a7c2fd0472b9219de5686a4e8f8edb11eb44f154aa8" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Contracts/WithHttpTransporterInterface.php", + "size": 813, + "sha256": "56c04994ccdf40c33c2eb6667f78466dcfad8c309cde203b4e9d9547de9107e4" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Contracts/WithRequestAuthenticationInterface.php", + "size": 864, + "sha256": "9fb3865efd26a4c1f2ad1179516a9456c76e00d9b20429234d4ac783145ea8b4" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/DTO/ApiKeyRequestAuthentication.php", + "size": 2383, + "sha256": "482a49bcd0c6bdd98fba16a8cd384e1a459f29aeb862a3b48802f7968dcc2128" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/DTO/Request.php", + "size": 12327, + "sha256": "0f284ca2fc945d60ddc2f8ef6c53878a950cefe4c39ddde5811a0d1fed7263fa" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/DTO/RequestOptions.php", + "size": 6483, + "sha256": "52118c117b26a229ce7396d26c99c22516b649063fdfe76941f77b60d6fce228" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/DTO/Response.php", + "size": 6108, + "sha256": "c79beef336e3c188fb40df27329ea0d768c50c6dbfc846e8d9fa819db14ee03b" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Enums/HttpMethodEnum.php", + "size": 2536, + "sha256": "3cea63bc8900a11b66fa0426cfd380a5879d684fa9977ab497c5e0ac20a8cfb9" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Enums/RequestAuthenticationMethod.php", + "size": 1252, + "sha256": "f013679a8b0bd3d1210bf38f4a82847ec9546ab29d9619ed35b851e5642081e6" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Exception/ClientException.php", + "size": 2477, + "sha256": "719333869a12ef728f2ce0cdabe71a8e1dac8a6a5792c30bd4b6465bf998ad34" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Exception/NetworkException.php", + "size": 1866, + "sha256": "2e622e718235b19f59e79ba51b6a257134968892e128a63b235363b339073e26" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Exception/RedirectException.php", + "size": 1845, + "sha256": "375e3c60d890a89d4caeca8cda61e7b88eab673412862d407986d83e8888acf0" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Exception/ResponseException.php", + "size": 1569, + "sha256": "c79f52ce0c09933ee41111d3e33f8546c94bcc39ec9cc74daa63ca606bd056ab" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Exception/ServerException.php", + "size": 1813, + "sha256": "4222b760c3c9e281f8ea47bf6022570e49183b2a92a2acb16acc53641eb6e494" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/HttpTransporter.php", + "size": 10908, + "sha256": "7f39a278c005b7bba5bb0ff832c5cc90e8b95a00c095dee2380c923b24fd201c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/HttpTransporterFactory.php", + "size": 1046, + "sha256": "0546eeb39a558013858587c635a3294ef279857b37240a2fbdde8d36de0914df" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Traits/WithHttpTransporterTrait.php", + "size": 1094, + "sha256": "a1d102cadcef8ed24db168320d9174860d8e7d9c04e6d252f410d36b4f74a722" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Traits/WithRequestAuthenticationTrait.php", + "size": 1201, + "sha256": "af0600e33cc1db5405ef235b4879e69c3149709015d76a28155ff4b592cdb553" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Util/ErrorMessageExtractor.php", + "size": 1893, + "sha256": "69a054876eab28413329e3e73c9942641e9ec30304577ed710c51219d4f82547" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Http/Util/ResponseUtil.php", + "size": 2150, + "sha256": "e1b27d157360490127f317a4d7f7dccae4a9a837067938665819628ae912f3b1" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/Contracts/ModelInterface.php", + "size": 1263, + "sha256": "f99cc852f5a266cf67fb16b3d8031e4f055b4871e93d16bb2eba9f1df84dd38c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/DTO/ModelConfig.php", + "size": 30372, + "sha256": "747f2a2100114c55bfcb8a1250f5b0b4c9ee1d4e53a16e782389fc618ecc5b9b" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/DTO/ModelMetadata.php", + "size": 6136, + "sha256": "0f7183a2d6b5a6e004854693b5d159d358bf0ecfdb22cb95c879a37eef95beb1" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/DTO/ModelRequirements.php", + "size": 15689, + "sha256": "9c771d2eaf9209418d94e52fd78b4efbfcdc1de5dd7f1eb457247e6eaa4dbe61" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/DTO/RequiredOption.php", + "size": 2891, + "sha256": "0dc117aea5f6929dec56ea4b0531202fa238989c5a93b213bea5d83e44daee6a" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/DTO/SupportedOption.php", + "size": 6153, + "sha256": "55b1db007f56ba0e054a13f97782812ba414a42177b1c3d7839eb626ddbde59a" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/Enums/CapabilityEnum.php", + "size": 2578, + "sha256": "5b3434044c1b5ebe5c80c185a32dcd3cae5aa57363409fcad7b0021fb344fdb7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/Enums/OptionEnum.php", + "size": 5929, + "sha256": "e92fdf3041b3aacca88489a31049ca7d2e7bc72986fffc993953a2091c0f2d17" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/ImageGeneration/Contracts/ImageGenerationModelInterface.php", + "size": 738, + "sha256": "e2f8c397939e0370d19f8b30fa2c51522a7c773e11c2f36ce576479567bc1c13" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/ImageGeneration/Contracts/ImageGenerationOperationModelInterface.php", + "size": 798, + "sha256": "58dffec0814a0abc2ac9b407f75b4a62158628cac1ca7baafe1db624955e22b5" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/SpeechGeneration/Contracts/SpeechGenerationModelInterface.php", + "size": 744, + "sha256": "0a689d2273d817f4975ad86a5c88e07699a2c1e4b8318663a8f7396537025f6c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/SpeechGeneration/Contracts/SpeechGenerationOperationModelInterface.php", + "size": 805, + "sha256": "d7c74846562250b1fb58e56cfae09bdd34a891cea2dfc2d362cf6837f874e3bc" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/TextGeneration/Contracts/TextGenerationModelInterface.php", + "size": 736, + "sha256": "7e86db2241a967be06da8a977e2625729dee28ff2cfbd5619c5b054245b57a2c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/TextGeneration/Contracts/TextGenerationOperationModelInterface.php", + "size": 789, + "sha256": "c2e6ff48d08bd8de488e68eaded0bed9f03c193363c5ede985d669fb092f3a54" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/TextToSpeechConversion/Contracts/TextToSpeechConversionModelInterface.php", + "size": 764, + "sha256": "a496c44cf23dab4adcc8448a8a3645980061d80beef8f3940c240ff2bdcde618" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/TextToSpeechConversion/Contracts/TextToSpeechConversionOperationModelInterface.php", + "size": 855, + "sha256": "d539853cb49a832ca72028dc8e05f58a9386dbc64223631e75ea589289fdd913" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/VideoGeneration/Contracts/VideoGenerationModelInterface.php", + "size": 733, + "sha256": "ff080810558f4800314a20097ed565e3ae121f6eaba0f88af0d618b5b2f9f625" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/Models/VideoGeneration/Contracts/VideoGenerationOperationModelInterface.php", + "size": 797, + "sha256": "8681d087b8c318584791b81308e556748e173accec81c1047229efd21d331bd7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/OpenAiCompatibleImplementation/AbstractOpenAiCompatibleImageGenerationModel.php", + "size": 13275, + "sha256": "f5eda658471bf6b8ce5cb51c38f94a9712ab09d14d6f098d3638bed4081f70e7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/OpenAiCompatibleImplementation/AbstractOpenAiCompatibleModelMetadataDirectory.php", + "size": 3323, + "sha256": "d55208d6033034a11535f68f38bdc6b3732d3319ff1d9019d9ea58e8b75e97e7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/OpenAiCompatibleImplementation/AbstractOpenAiCompatibleTextGenerationModel.php", + "size": 25247, + "sha256": "df077e374d1e0a78790a8eac0b5bbb6087548dffd5ce9949d8330d36a3e78041" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Providers/ProviderRegistry.php", + "size": 24080, + "sha256": "56251f9168d805bfdde67f58c9d4f59ec98d151442b9ce154b80f056dcf94ed3" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Results/Contracts/ResultInterface.php", + "size": 1401, + "sha256": "0df21d95b4b941b00d9ca79e8b849798d0b3901c265edb0f7bcbe8ef8ce92081" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Results/DTO/Candidate.php", + "size": 3495, + "sha256": "4d16cce92e053d9b948f3314159e15921c7e465ac3e2de625746d4ea8f341c5d" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Results/DTO/GenerativeAiResult.php", + "size": 13807, + "sha256": "909ae7428fbee4b71aa1dca66263916cfe4bc80ac8f9a520c4bdaa92421bd552" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Results/DTO/TokenUsage.php", + "size": 4880, + "sha256": "cf2020516572463036032d1d093387b25aa50d437966dc6d3df64e62f9f76ead" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Results/Enums/FinishReasonEnum.php", + "size": 1422, + "sha256": "77655406d0636f11313a59d6b3cef82cae17cb1292ef40e31602c3d5f783e2f1" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Tools/DTO/FunctionCall.php", + "size": 3675, + "sha256": "2fc5d11ea140a6e321af2a6004f54c9e3b61bba419046c30173a163e29a48d62" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Tools/DTO/FunctionDeclaration.php", + "size": 3589, + "sha256": "c91ac9213408e6d321ab9b00b47b3017ef72e62b29af4f4f2d0a6a15cb6e44b3" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Tools/DTO/FunctionResponse.php", + "size": 3787, + "sha256": "ca39b9cdbdff8d4a4f075d675aef9433f9ae7cc999295fd437fc466303bb6e11" + }, + { + "path": "wordpress/wp-includes/php-ai-client/src/Tools/DTO/WebSearch.php", + "size": 2882, + "sha256": "ab818cd1d8d55d38e869b0977a9d245cf258311ac8a454fff237b556a9b3e7da" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/ClassDiscovery.php", + "size": 7082, + "sha256": "219a71f9d91a907c04a62706e4ea6b335a4c5d9def0cc546d88df3911c9fbcf1" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception.php", + "size": 235, + "sha256": "f8b380832371a41cdcc03041ee8be9f2a75f9332e0d395e332a4346b98d156a9" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception/ClassInstantiationFailedException.php", + "size": 340, + "sha256": "f8b0a2db9058b72d0f029a8de493971ee7a18ebbe97cb4966e2aa615e2b2e87e" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception/DiscoveryFailedException.php", + "size": 1241, + "sha256": "2ecca0417088d9ac1a5f71abbcb3b81b9107972ce0188138c28076211d841905" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception/NoCandidateFoundException.php", + "size": 1155, + "sha256": "e5b69225cd35fa27839c7a23dc3b20b418574666f69eb9e5f722991c627ef733" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception/NotFoundException.php", + "size": 409, + "sha256": "960ecfeee8e25964a219ae9395d5c6fc1d391e5f7237dc9f779e9e7e56870062" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception/PuliUnavailableException.php", + "size": 263, + "sha256": "fc7774c39874a757fab61bb2e1c9987e36f0d6ce569f604df93b16d8037de771" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Exception/StrategyUnavailableException.php", + "size": 419, + "sha256": "4aad19d444aad54fdaf3a099e563622aade3de08f7633ff5d8aa695c1f6e97df" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Psr17FactoryDiscovery.php", + "size": 4081, + "sha256": "8b5cfccceb90b928bf2e15ff6576912b4dc104dbef1f3ffd782d6cbf98fa079a" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Psr18ClientDiscovery.php", + "size": 1038, + "sha256": "12ea56ae41a37059d222695476fa1a77261b1ed7c7360ec0a73e3f75608d98ef" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Strategy/CommonClassesStrategy.php", + "size": 8530, + "sha256": "5a79c94115e0a4e0720d06e6b3cf7aec751ae218f73ae2f6a180e39faab0ee7d" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Strategy/CommonPsr17ClassesStrategy.php", + "size": 4141, + "sha256": "abca79801f800f818344b78fb3661ab39401c2ffa11b6f6626a6d88a0470ce2c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Strategy/DiscoveryStrategy.php", + "size": 674, + "sha256": "998c30e106a6beb9ff10ae6d6e172a8f541296c3d9fd2babadd6fc2ba5400b92" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Http/Discovery/Strategy/PuliBetaStrategy.php", + "size": 2381, + "sha256": "08c6c9d5420352a0022f9c41646d08d1523b339645ac16961604443456f61567" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/Factory/HttplugFactory.php", + "size": 2393, + "sha256": "3f4c7a79022325ce2de95c258aafd6171a0e9da2b3b55b69ee51b29b67f64007" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/Factory/Psr17Factory.php", + "size": 3838, + "sha256": "cfda7de04ca4e782cde97d0b6ae804e254dea9677d6cac18260b858a9c6b6593" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/MessageTrait.php", + "size": 7466, + "sha256": "a2649775faad3c54f50df13c5ce2d04fbb7b56d94f9fbbc9fb432893502e1668" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/Request.php", + "size": 1504, + "sha256": "b56df63c6f5a8acd0b40397372bee89732222f3d65966f3b90ca3dfeaae7d4a2" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/RequestTrait.php", + "size": 3041, + "sha256": "fe610bf799b36384ee1d6fea0b6af4188140a0463cd9c3b1d2b945f576fe61a7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/Response.php", + "size": 4335, + "sha256": "c1b48c456887d46672412c75a7f127224a48053839a259c30b2c471cceb3aaa2" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/ServerRequest.php", + "size": 5069, + "sha256": "5b2f7ceed15295883545659ae539a4a8bc64f5d743abcc23cfe5717289d8021c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/Stream.php", + "size": 11117, + "sha256": "9ed57840d10beed9647646eecf006cde645fb63ce227471f801e626488be0c1f" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/StreamTrait.php", + "size": 1596, + "sha256": "5fe3cba24e52d1d1c2f8143a8540b76e43b0785e0eb996fdcdf583a86718ad6e" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/UploadedFile.php", + "size": 5544, + "sha256": "88c49259a014fdcba10395aca06ac44d86b21fe8d3af7f1dfd67f4369027a8fe" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Nyholm/Psr7/Uri.php", + "size": 9737, + "sha256": "d8acfb89b7346c6db344eb794071c04b3f2da59375cf2edbc35f272830b83db9" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/EventDispatcher/EventDispatcherInterface.php", + "size": 463, + "sha256": "e8ae3b10ec1aa7fc61e7ee051827f5d6b9067873fe903554c9d25b0b7dbe4ae0" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Client/ClientExceptionInterface.php", + "size": 202, + "sha256": "721a56bab538a75adc235c85eb38bc3ba4dce036a764385c8b40ef1178eb5d78" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Client/ClientInterface.php", + "size": 592, + "sha256": "59b4e1649fd02a557bbf43fd1e58295ba3ea79fd0769174576d1bcd1f1c3cb6e" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Client/NetworkExceptionInterface.php", + "size": 719, + "sha256": "3bc010f6ba70342333029e1db5a732164948c655df39bd9be23f633a7c111248" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Client/RequestExceptionInterface.php", + "size": 647, + "sha256": "66c38c6a1f09d7637eb5353945ac269c1b0ced1107f21e2eb2edd32eb99072e6" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/MessageInterface.php", + "size": 7123, + "sha256": "aac88a14f24d558393c7f5d2440930a1f7ec1a1b5a9f2cbf1557c67fe65d305c" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/RequestFactoryInterface.php", + "size": 530, + "sha256": "fea759f2b45dfc00685f756cac61991e855278020ff3f90cd815400b5c2e8514" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/RequestInterface.php", + "size": 4945, + "sha256": "45db61228d553ab0700778d73bd3d6bceec034ba2398c7b4e62de5624c032668" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/ResponseFactoryInterface.php", + "size": 577, + "sha256": "07047ef28920969a5a9df6f5ccdd28ac3437e2160065997f258495da92bc75fd" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/ResponseInterface.php", + "size": 2663, + "sha256": "78503796e80aa5340f37ffa4dfc0b374f74fb1e91fc317217751668d355d32c9" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/ServerRequestFactoryInterface.php", + "size": 958, + "sha256": "828f68e323eae130345332cd99087542395f01bbcfbedc8f4c9ae7265ae61fd3" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/ServerRequestInterface.php", + "size": 10317, + "sha256": "3c1e3aabf022cb9af56eed7eca051bc8a37d9f7be04245a61f76fbbeb49f1d7f" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/StreamFactoryInterface.php", + "size": 1447, + "sha256": "1d371d27184033372ce972cb1e420f6fad7067bdf2c3942fe16177f492404bc9" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/StreamInterface.php", + "size": 4878, + "sha256": "6da9732405cf6700e5b8b832052055bde7dfe9b3bea397237b500bab79d538f7" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/UploadedFileFactoryInterface.php", + "size": 1113, + "sha256": "ed3f7860e546e90f95256b70af3535bb3391fb60418ebceaf9f8645fbd4236ea" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/UploadedFileInterface.php", + "size": 4758, + "sha256": "c8ec419f9ac21a1f27412686fc488c93d943ca1eecbbd3697d0b9acb9eeda737" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/UriFactoryInterface.php", + "size": 356, + "sha256": "90bdabca819182152ef910731ccd4ee141617ba029816c4b89913eba2238facd" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/Http/Message/UriInterface.php", + "size": 12847, + "sha256": "3e39123cb47dcfc4510145864e2f9d3188ade576319c8567b6d386693f767afc" + }, + { + "path": "wordpress/wp-includes/php-ai-client/third-party/Psr/SimpleCache/CacheInterface.php", + "size": 4632, + "sha256": "ad900e6871d4dada795a1125a40ee89295e912b7c02c4aadff810ed186b86f29" + }, + { + "path": "wordpress/wp-includes/php-compat/readonly.php", + "size": 1253, + "sha256": "b528bf0a74ea9cb280347ca11224759cfe277cbc7de93f34817490fe661df94f" + }, + { + "path": "wordpress/wp-includes/PHPMailer/DSNConfigurator.php", + "size": 6883, + "sha256": "429f5f0a962864ec7b67efd572c0f85cc0f33fd4585413d8dc80d41c8562399e" + }, + { + "path": "wordpress/wp-includes/PHPMailer/Exception.php", + "size": 1256, + "sha256": "22ab858ae438d98f58f41f38ad2191d1b0d59570aebea0463a7948cfae1021b7" + }, + { + "path": "wordpress/wp-includes/PHPMailer/OAuth.php", + "size": 3791, + "sha256": "44b2c9a3b6e538a65b90261251f197d700e2eb90d51d4944fb80577263210fb4" + }, + { + "path": "wordpress/wp-includes/PHPMailer/OAuthTokenProvider.php", + "size": 1538, + "sha256": "f2318ea1b2841b2481b636e205c281a733f9de944a71ae403e0b59fa71219572" + }, + { + "path": "wordpress/wp-includes/PHPMailer/PHPMailer.php", + "size": 194030, + "sha256": "01038c4104554fe7fdb958eb4653883c5cf93af76a3c8797794001c35c1bf553" + }, + { + "path": "wordpress/wp-includes/PHPMailer/POP3.php", + "size": 12452, + "sha256": "a3123fffbe5fe132f6d02fba7ad46e520ce4059e46aaccb923fd5b8d63bc1708" + }, + { + "path": "wordpress/wp-includes/PHPMailer/SMTP.php", + "size": 53628, + "sha256": "f7c8210e4047871f9f0b0639d7387d5574738fb9984f744d58d85378da77c68e" + }, + { + "path": "wordpress/wp-includes/pluggable-deprecated.php", + "size": 6324, + "sha256": "0255796d9c69aa97e97a15c81314c81f084b2442d22059db0e06552b47ebc5df" + }, + { + "path": "wordpress/wp-includes/pluggable.php", + "size": 127558, + "sha256": "c50d95cc208711745a7a8931584155169b50c8d91473a5e731ac22efb3fa30db" + }, + { + "path": "wordpress/wp-includes/plugin.php", + "size": 36501, + "sha256": "2e06902ae7d65d7dad37cbafd8e8feff83e4aeeff3a7839885ce7fe4f0c94d68" + }, + { + "path": "wordpress/wp-includes/pomo/entry.php", + "size": 3863, + "sha256": "3d503be0ce77eb150f9e7452d2f098b517e9ff9773c310024956719b16d8e5fc" + }, + { + "path": "wordpress/wp-includes/pomo/mo.php", + "size": 9539, + "sha256": "fbdc17701234e5012fbf7433426ce99f1ef115a2fc247bd80f57d3d51455bf49" + }, + { + "path": "wordpress/wp-includes/pomo/plural-forms.php", + "size": 7641, + "sha256": "5eb291837c007827515a72bcd5ba793d0f22702468beed34746375ded232027f" + }, + { + "path": "wordpress/wp-includes/pomo/po.php", + "size": 15344, + "sha256": "9e4b73829fe5053e84df4dc1f7134856e6f7e84c8cce1163c9fc295931ec77e1" + }, + { + "path": "wordpress/wp-includes/pomo/streams.php", + "size": 7934, + "sha256": "43124129774bd52561ff27834c6771bd141299a69b57abb6cab378344550b28b" + }, + { + "path": "wordpress/wp-includes/pomo/translations.php", + "size": 12743, + "sha256": "9aa073438aafe5a4ea0d4f60fea9bd40a1f153653cea3e7567135e25e5099b34" + }, + { + "path": "wordpress/wp-includes/post-formats.php", + "size": 7070, + "sha256": "160a927cafe77d4288375beb614b0309615d2037a1a019e506ccb09aabec983e" + }, + { + "path": "wordpress/wp-includes/post-template.php", + "size": 68615, + "sha256": "4c17d6d6ce1a9daae6731eb50d3357c0769dd0e842e8d9b690314d5d684b1aa1" + }, + { + "path": "wordpress/wp-includes/post-thumbnail-template.php", + "size": 10879, + "sha256": "5c9a3d21c1ed0989afae3ae75c8135b082e95dd6a67911e5ce64c8e1a8934266" + }, + { + "path": "wordpress/wp-includes/post.php", + "size": 296525, + "sha256": "2e6ac612f82a1aeb04e1e1de0c7e2a186e5f997f67e942026bfe9b211108811c" + }, + { + "path": "wordpress/wp-includes/query.php", + "size": 37095, + "sha256": "f7425e5ff70e4021db8cc00086f64a30f28e496642b89faeddc273b3a6b61712" + }, + { + "path": "wordpress/wp-includes/registration-functions.php", + "size": 200, + "sha256": "473b3f80b6df696a48f645b0597e1c8203092dfec01f8a0084c4569cc848bbd9" + }, + { + "path": "wordpress/wp-includes/registration.php", + "size": 200, + "sha256": "496ba5d503da33870405ebda4e505badd25f02ff306f3d177ea58e058186f81b" + }, + { + "path": "wordpress/wp-includes/Requests/library/Requests.php", + "size": 261, + "sha256": "397c790baa73fc768dbe7a3c2473180d0468a4bfcd6b1f58ea1b6ff31eea2e39" + }, + { + "path": "wordpress/wp-includes/Requests/src/Auth.php", + "size": 860, + "sha256": "55ff2df53f03e60ec04f264ebb82d190e84867fdba28df4bd67d31921f895960" + }, + { + "path": "wordpress/wp-includes/Requests/src/Auth/Basic.php", + "size": 2541, + "sha256": "ab1c5a7a4ddb93747fbdf8d3bb50e93fa77a38762a22ed5226ca85e1fa8dfa54" + }, + { + "path": "wordpress/wp-includes/Requests/src/Autoload.php", + "size": 9335, + "sha256": "e5eb035b64d0fd4b6b30f582c207955dd426c4faabf3695b83be09482febb514" + }, + { + "path": "wordpress/wp-includes/Requests/src/Capability.php", + "size": 652, + "sha256": "0d7c9dc6d64eeb226b77af8248775ed3903af655ffffd6796d14704e0f68802b" + }, + { + "path": "wordpress/wp-includes/Requests/src/Cookie.php", + "size": 15389, + "sha256": "0ad287a5fdda8700eb3117c6637cdc60069a895fb780b8296ac8c809b756baf4" + }, + { + "path": "wordpress/wp-includes/Requests/src/Cookie/Jar.php", + "size": 4363, + "sha256": "1a7e0424bffb20752ee8b8dc807bab6bf8d02257e31f23989e72f7da54e3252f" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception.php", + "size": 1114, + "sha256": "442a7f6f0c2f8b7b06149dc5f0aeba4eef946f84b7f2ad8907bb621d7d281066" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/ArgumentCount.php", + "size": 1460, + "sha256": "a6632c1a12b579783b3b3abe7f0d45d79b3c295d139146faecdd35c6622709b4" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http.php", + "size": 1542, + "sha256": "6d9dc436afb2a8658a0c8d8a89aac2c92fee66190579e1f7a625621a5ebd3ec9" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status304.php", + "size": 460, + "sha256": "d542d2012c80a832a11179e9de5f2e579f91d99f4db8a4d79aab61ff7cd538e4" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status305.php", + "size": 451, + "sha256": "e1309028d5881c1574e46df1e599854c7f4cc4c1c9f1e4a041ac7f6dd4bb9eb9" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status306.php", + "size": 460, + "sha256": "ecc19b8458bc7c8d0cc69fab293c265ad41efed11f4b9366c10d40468267ab0d" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status400.php", + "size": 457, + "sha256": "66c435f25e14672f05694e29369366bd250ed373527aaa36b7b3bf29ed1b1a89" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status401.php", + "size": 460, + "sha256": "e74cca6cef841a444c6fd368398e72615f3decb5dcd8e6b0f4152ce44a7a5fd3" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status402.php", + "size": 472, + "sha256": "43c6d092f6f3724fc02eaccc03dade942b6eb521ce1fc84747b557f51eea0133" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status403.php", + "size": 451, + "sha256": "2c54292073012c2a4ce855e3bfa6f24aa252e6dae9c27aacd352e408d1aa5409" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status404.php", + "size": 451, + "sha256": "699b6626f7d05cbf609a9b7d01190be70c8247eaf7ea0f9a76fb43e8a85a52a2" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status405.php", + "size": 478, + "sha256": "bd8027b14eeeebe6c09a8dc7a2e6369649312e2de9e1dc10cb109288df97e33d" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status406.php", + "size": 466, + "sha256": "6a17029352c63b5ac0f3500d2c9ca1148ab9f29c981db5b9dc3948249b4de625" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status407.php", + "size": 511, + "sha256": "f70a269a2fab6df136f81679e0c1acd404e114b729226ea6ee1e567c58d1b63b" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status408.php", + "size": 469, + "sha256": "e31fc6ed62d86dd9bc9818b2953532c8f1ab552fe29e943260633a04caf97f8e" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status409.php", + "size": 448, + "sha256": "c1bd72ee2ba72bbb2f9b90e63799cff0a57a7d22400429203e8fef76df0f54ef" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status410.php", + "size": 436, + "sha256": "74287f0d729910f25eb742ef2a528ff5fd34847c95f02139eef1fd66fd0564c8" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status411.php", + "size": 469, + "sha256": "f48d82b8a6bbe29b5944b9ae43b86d2dfc9b00822786f72a03fd6b43675a744e" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status412.php", + "size": 481, + "sha256": "c9b3f2cc9e8bef48a7ec2eb3512e73aa4173d09ee8f836145ee4d4ffdb352dbe" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status413.php", + "size": 496, + "sha256": "ba21191df92298b3f5e696d5bd2304de126e7079a58aafc07abaf57f08e068b3" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status414.php", + "size": 487, + "sha256": "48e76a04e4b68d923bad98c60a5780f29e5742c9612969eaa85e99178fa3bfbf" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status415.php", + "size": 490, + "sha256": "5d37e1066f55b124202131f99cd1f1a6f0c652505ce8eccc3254e54afe45441b" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status416.php", + "size": 517, + "sha256": "5431ab8de0fd2a9bf5dbd002d0b800917e0dfa8ddf76621b3539de69db5da62c" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status417.php", + "size": 478, + "sha256": "4663bdded00c5d37fbd496ea9eb02fc7c9d269e025af5d51b6b787a522e0a0f6" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status418.php", + "size": 556, + "sha256": "d35d01decab8de5009630211257f4d3dfa910e28bbe137565546380819d3631f" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status428.php", + "size": 583, + "sha256": "6fa34afab1cbb4894e9dd4caaa27467ba7ff59470f656d27f23f03e2aa7abb1e" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status429.php", + "size": 627, + "sha256": "48ecac2d984be99bc448d43c4e9bcf495c2a3ca0fa4659a7fdacc21d2ca91879" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status431.php", + "size": 613, + "sha256": "c0856154f2f7cb350baaa171e45c16300164fadd5ab846c6cde097b621d06e81" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status500.php", + "size": 487, + "sha256": "22b93daa63b7a3c20c0aa5d4d6ebe5b5c717bc351c165adf02db0628418a49f0" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status501.php", + "size": 469, + "sha256": "92db81b9a7cdb04693e6088b4532bde4a193c17cb749a6c244b53232a06966b3" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status502.php", + "size": 457, + "sha256": "5cc5893df1b8a91a3911cc4307ce4cd584aed6c065502e1744c452a3200365b4" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status503.php", + "size": 481, + "sha256": "f4e92ac83b2c0ef4c596fd18affe8ece10836d578ba8ca9df8a3e84f68152a26" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status504.php", + "size": 469, + "sha256": "c0a3a1426f32a29a418c82c6f3e2694b8fe84ec83db93b2618a3f210db039753" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status505.php", + "size": 502, + "sha256": "181d699d679ae8619048865d1dc58222e0b725820bda79881f0ba00c361b2d84" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/Status511.php", + "size": 613, + "sha256": "6969ddf7b24ed27cf8906e55d7f0e60417f08d942b8999f8bff43d0d72f73f66" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Http/StatusUnknown.php", + "size": 970, + "sha256": "19864167b648019581dd7906046f6dcce64dd87c09f0fb5d60016348db954eb1" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/InvalidArgument.php", + "size": 1106, + "sha256": "e07fece0cd8cb70aaacb9f6aea251041cd5706a711122bf947c24242d780da3b" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Transport.php", + "size": 244, + "sha256": "050498aedbbbcd3c24eb0457371aee9a061f41bd9829844a9cea4c9d82229ed5" + }, + { + "path": "wordpress/wp-includes/Requests/src/Exception/Transport/Curl.php", + "size": 1397, + "sha256": "3e4e73c24d32a8abc038964a4c76aaf187b96698eaed47815be4d5d27bddcb3c" + }, + { + "path": "wordpress/wp-includes/Requests/src/HookManager.php", + "size": 709, + "sha256": "4b09da68b4f523dd9d36f047117e0c1fd442c6aebc8e4cc3632e5ac1a04ff317" + }, + { + "path": "wordpress/wp-includes/Requests/src/Hooks.php", + "size": 3032, + "sha256": "213c04c2e16e7d34ba62b286898c8c217b44ec258e21ccb60171338c366db8dd" + }, + { + "path": "wordpress/wp-includes/Requests/src/IdnaEncoder.php", + "size": 12447, + "sha256": "087fc47d99874d4c656bc40769bfc32006f0b87099cc4bce18b07a58eea97eed" + }, + { + "path": "wordpress/wp-includes/Requests/src/Ipv6.php", + "size": 5643, + "sha256": "cd22791b14895b1e9bb2c28953e65c400c6433109bbeeea925cbab650075c582" + }, + { + "path": "wordpress/wp-includes/Requests/src/Iri.php", + "size": 29726, + "sha256": "d35e4ad341edb5fd287385388606831ced33627cc34425d3d69adf80b554800f" + }, + { + "path": "wordpress/wp-includes/Requests/src/Port.php", + "size": 1505, + "sha256": "eab02dc614d84eb8e874ec743d1ede48cbac7df8f7841b664f6e97995e8d883c" + }, + { + "path": "wordpress/wp-includes/Requests/src/Proxy.php", + "size": 867, + "sha256": "251c12ecb0eccb1ae9b3f54a7b7a568401704c94ce05a8a1ac0443b5c71dfed0" + }, + { + "path": "wordpress/wp-includes/Requests/src/Proxy/Http.php", + "size": 4217, + "sha256": "4219fb6f79e9bf6cb6ccad40140ca7e58fe7929284a5f32f6a6e49967c8e6ec2" + }, + { + "path": "wordpress/wp-includes/Requests/src/Requests.php", + "size": 34001, + "sha256": "f5a143e79c81ba99a86791d574110e9f52d350017b70726ca464d05dc04862f6" + }, + { + "path": "wordpress/wp-includes/Requests/src/Response.php", + "size": 4281, + "sha256": "e1293dcac689d3a371c88328f3d6761555c56a15702d090ad08a8d2e3cf844eb" + }, + { + "path": "wordpress/wp-includes/Requests/src/Response/Headers.php", + "size": 3110, + "sha256": "c1be9741793086b9c2dbec4c96ab9898543f04db020c174e1052bfd669be645f" + }, + { + "path": "wordpress/wp-includes/Requests/src/Session.php", + "size": 9107, + "sha256": "df02979845a014eb85364a07254d9592baa49ded3814f15238d0b3c32f24b6be" + }, + { + "path": "wordpress/wp-includes/Requests/src/Ssl.php", + "size": 5425, + "sha256": "d9a3dc1bdde30ec56cdeeddfcd685f29b75ef496f1a9669a800ccfb1d2fd1202" + }, + { + "path": "wordpress/wp-includes/Requests/src/Transport.php", + "size": 1544, + "sha256": "ca39e25d43c50f81514d2f441f492e3fa412f932c587ba579eb5d95d91f3096b" + }, + { + "path": "wordpress/wp-includes/Requests/src/Transport/Curl.php", + "size": 19878, + "sha256": "2d10f26c919ba6d1b20b6a3fbfaa81db4ef2b44871fe46392be00b70793f7997" + }, + { + "path": "wordpress/wp-includes/Requests/src/Transport/Fsockopen.php", + "size": 16079, + "sha256": "670f86a367a1e28e28cf5014378931f700febad061a76edae11497680ebbbbac" + }, + { + "path": "wordpress/wp-includes/Requests/src/Utility/CaseInsensitiveDictionary.php", + "size": 2651, + "sha256": "535b92168bd6777c0c5df093ad0f5b260ec8d171dba1423e7440805ae6cd4d7e" + }, + { + "path": "wordpress/wp-includes/Requests/src/Utility/FilteredIterator.php", + "size": 2176, + "sha256": "c818f40e00f338557fbc458e90c6f32d80e3690a8ec844ef3761b2ea32a5c1f0" + }, + { + "path": "wordpress/wp-includes/Requests/src/Utility/InputValidator.php", + "size": 2512, + "sha256": "ddbe22b3f5ed77f3dd522cac31404298cee44a61b9acc30d449c1edd0577c4e1" + }, + { + "path": "wordpress/wp-includes/rest-api.php", + "size": 101211, + "sha256": "a76b900a50852aec5957634f333b52ce9ef93bcd427a26ad46390e44bdde902f" + }, + { + "path": "wordpress/wp-includes/rest-api/class-wp-rest-request.php", + "size": 26521, + "sha256": "6645183d80b306ba9c3cd13f47f1f2c3d2e23188e2e83efb8ad041e3b8dba094" + }, + { + "path": "wordpress/wp-includes/rest-api/class-wp-rest-response.php", + "size": 7377, + "sha256": "871f3554e71c96c5965ac331fbe0912bb3253f6ecaa88ce6fc9870d06706fdf1" + }, + { + "path": "wordpress/wp-includes/rest-api/class-wp-rest-server.php", + "size": 57983, + "sha256": "efc7d60ed76286fe73ffc7885d8c96fb596eb0f00f3b7d8591cba2e38b2b61ae" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-categories-controller.php", + "size": 8085, + "sha256": "acf811b7980cc327a7221aedad374f5703d55be2888a51d596b055e9af624a06" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-list-controller.php", + "size": 13384, + "sha256": "526ccd6155c1ce0fa95bc6144e952118a01f8a4e70fe3f115d5ddcc3257528c0" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-abilities-v1-run-controller.php", + "size": 7035, + "sha256": "570f7c7ef48a753ebe8242b3ee036c1bbb9687c060c51137c93d9b8f7b87c2a2" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-application-passwords-controller.php", + "size": 24318, + "sha256": "ce4b477bbc41e16440ce591ee7cb89bc3759d19a8efaaca876e39503484c7161" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-attachments-controller.php", + "size": 58159, + "sha256": "82663e580c37760f39e6ef443abaa2b2ff342d1547c3265c759eaf7bdc025268" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-autosaves-controller.php", + "size": 15502, + "sha256": "7f5173b68c2516bd9eccd35c2d4cfe793721260a72e8a24c541b012294962aa3" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-block-directory-controller.php", + "size": 9909, + "sha256": "ab9c6d8d64bcc838a07aa1885e1163921359065dd390355ad5ff76406d5e7e88" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-block-pattern-categories-controller.php", + "size": 4814, + "sha256": "d09dfca43febe918356a37823ce31f7e7134ff7b61de7f5e4286fcca00bf4864" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-block-patterns-controller.php", + "size": 9296, + "sha256": "906f2a893e657b5848f94ef403d0bc29833f2735930216243a0f54948d9b22f8" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-block-renderer-controller.php", + "size": 5864, + "sha256": "b257273b99f21956ce407a40e0b69642dbd0e120dec8c14bcd8755cd93f9e095" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-block-types-controller.php", + "size": 26877, + "sha256": "604a5e4c1e83b806a8ab355e7b63601401fbf2b72a9cdbb64ced57d29e7dd29a" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-blocks-controller.php", + "size": 3128, + "sha256": "b026b91c38464af7ed79709e65fadbfd3f45d7e0effe40711717855fb848f122" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-comments-controller.php", + "size": 63037, + "sha256": "2a838fd833833ec9c4f8bae84a9ff93fd965f15444baa4806a52a130737b1272" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-controller.php", + "size": 19039, + "sha256": "901657b866247660af62429af90bc17682f17dc2d362617adcde94aeb8d7d02d" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-edit-site-export-controller.php", + "size": 2110, + "sha256": "3f1e12a6b992cd6c2bb2b1d2ea25b3dd34bc4a584987a57ca32a5e79d356da74" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-font-collections-controller.php", + "size": 10642, + "sha256": "17ef298f20ff6e44287395814f82d212b6c5b214c15e6389deb1e3151bce31e9" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-font-faces-controller.php", + "size": 30110, + "sha256": "95e5f2b53d8a5c4b03502411d7ed10bc5b2c2d6a3a55c1d0dbc9698792dbc90c" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-font-families-controller.php", + "size": 17856, + "sha256": "c3f6895dee2cb1c74cbf617b0f8d94a78f0e1275727cf9f9d650548ed3e1bc58" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-controller.php", + "size": 23813, + "sha256": "821d76beb46c6ef671f6ee1c10872ebd2fedcfa694203181b468253287508499" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-global-styles-revisions-controller.php", + "size": 13295, + "sha256": "0b2c095042cdedde75de0281b97c19f3c5dc2c930cc4618acfe20b7c64ac1e9c" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-icons-controller.php", + "size": 7233, + "sha256": "06c3ecd06716bb148c45838226ef6b1a60adab26632e64c5b1f86c28c6f9e4dc" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-menu-items-controller.php", + "size": 33269, + "sha256": "f7c14a88631927caab3a43f6b0b6c6e0d18c404409e471c5da3607d9a7b5efe4" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-menu-locations-controller.php", + "size": 8883, + "sha256": "0f9dc1b51329b734520d164fc8205aac6101ce7c94d42704d70c75ca0b382515" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-menus-controller.php", + "size": 17077, + "sha256": "85489d1545f3b144fb60a90ee4686106d1f1441b67fc7fa566594c5cf148a4b5" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-navigation-fallback-controller.php", + "size": 5171, + "sha256": "81886539d3a9524fbccd3351239278b9d88c9e92e9bba08c1a902c84223a4cda" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-pattern-directory-controller.php", + "size": 12880, + "sha256": "c65eadd2d28c20a1a0337861d12e6cc153f4aa6833755ef264f23bc12d7a8d7a" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-plugins-controller.php", + "size": 28529, + "sha256": "126ac6e7b14f15901ccb2cc19b06f78cb0805ac2267c136dff30ab7cd9a11ef8" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-post-statuses-controller.php", + "size": 10309, + "sha256": "3aa4787c2dbca22cf9b6b28c77b0de147f5753a78ca656b9787609dc53ef7e96" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-post-types-controller.php", + "size": 14283, + "sha256": "0549aa2cb3ac2a52480064aba47124b7d2b9e40a970ea393b995980097df33b8" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-posts-controller.php", + "size": 104925, + "sha256": "c6773a855fbb0ef1d169cc4ab33adf19899f32d253c3e166c8b774dedbd4491b" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-revisions-controller.php", + "size": 27927, + "sha256": "8d1633934bcfaab328b36e7fba9eacee8684f1af1a192ef66d58a208c9e6c996" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-search-controller.php", + "size": 11481, + "sha256": "6ad632c1085136c5799714f370ee9cd8603d40a6e25cf224c7dcaef364b1069e" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-settings-controller.php", + "size": 10330, + "sha256": "4a1b63ab7502acf264e485ddf66ba8de4b85d9562676037c1c6584c18a8de632" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-sidebars-controller.php", + "size": 15901, + "sha256": "9b80f2023c270e5af108f14b628b4e8e52b0206026d285a840598ca55f36ab04" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-site-health-controller.php", + "size": 9836, + "sha256": "9e3a450efc532022f16516759ca614e62df26f01c63611b5e9d89c00db5be4da" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-taxonomies-controller.php", + "size": 14015, + "sha256": "cf5325542dbec1b12f4da73feef0e8ce5fab1e1c64b222656a0361e4b3257ffc" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-template-autosaves-controller.php", + "size": 7825, + "sha256": "f1f12eb98a5a3ca6a538a146361c324aaaf909e81880e26f2f09aaf1766dd9a2" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-template-revisions-controller.php", + "size": 8724, + "sha256": "cbb9808ec88211b19198ca45c23e9eabe7d78f421223812ddbce469036af99d2" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-templates-controller.php", + "size": 38236, + "sha256": "08410b8d414f9a68216ab0f0b3a5582cc4e1c38eee27c6ed8905cbbbc7667e65" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php", + "size": 35445, + "sha256": "fd0718f7a3a236dc12349c3ae3fb80c9d26f33e0eefc8e072293f048a4512bc1" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-themes-controller.php", + "size": 23462, + "sha256": "940732f10b7b562d71efdcdbbb08ed2f0d9f3f1e2d4d161ffac74d3e5aec3d4a" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-url-details-controller.php", + "size": 20553, + "sha256": "57990d4e6aacab6556de6bebcd3a081480225cf95d27e4309c0da934009e068c" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-users-controller.php", + "size": 49841, + "sha256": "e6ed533a7e397a838b276985c30a78d8c3d1db1bba00dcebd1f32f980e66b6a5" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-widget-types-controller.php", + "size": 19202, + "sha256": "a74bbb9fd067e78616a4d791c5b2bcd8472090794d60b7629901e6974d6fc904" + }, + { + "path": "wordpress/wp-includes/rest-api/endpoints/class-wp-rest-widgets-controller.php", + "size": 26862, + "sha256": "fcdd417e2aea0998a6cca9a37326582131a734352d9814b64f74ee617fcd34d6" + }, + { + "path": "wordpress/wp-includes/rest-api/fields/class-wp-rest-comment-meta-fields.php", + "size": 895, + "sha256": "86c69ea9f0eea921dc3dcd4654ca2632b377dc27aad1bcea7310a0bb4b5cedab" + }, + { + "path": "wordpress/wp-includes/rest-api/fields/class-wp-rest-meta-fields.php", + "size": 18487, + "sha256": "f8ff99b6f9fc8ca867f7cfa4b2bed40ed81b0f1b41d32f8b3497c77a5a6c52bc" + }, + { + "path": "wordpress/wp-includes/rest-api/fields/class-wp-rest-post-meta-fields.php", + "size": 1244, + "sha256": "7ace4a850551f8e7dda4c309d2a516a6b99c9c54c28c24bfa019923e98c98479" + }, + { + "path": "wordpress/wp-includes/rest-api/fields/class-wp-rest-term-meta-fields.php", + "size": 1241, + "sha256": "2e670258ff8ff793c97dcfc02af0165989e7dfc1ff1f90b601379da39056aa44" + }, + { + "path": "wordpress/wp-includes/rest-api/fields/class-wp-rest-user-meta-fields.php", + "size": 856, + "sha256": "f8c750f36cc78bbbb834dd8d546de282ed3943623511eadfc86f384f94ca82b2" + }, + { + "path": "wordpress/wp-includes/rest-api/search/class-wp-rest-post-format-search-handler.php", + "size": 3931, + "sha256": "9b712da6c35f6365c8119883a7448d06759dd0e0f242a1364c77fc54723d83c4" + }, + { + "path": "wordpress/wp-includes/rest-api/search/class-wp-rest-post-search-handler.php", + "size": 6057, + "sha256": "a4a03ecf9b03bb9c9767cb3d387750da103d98ca1ac262f0aa6c990771c53992" + }, + { + "path": "wordpress/wp-includes/rest-api/search/class-wp-rest-search-handler.php", + "size": 2295, + "sha256": "7a4464fb9e913383048ed22ea09d7b21450cbc8430d871e8676ef971649d6846" + }, + { + "path": "wordpress/wp-includes/rest-api/search/class-wp-rest-term-search-handler.php", + "size": 4634, + "sha256": "7b9a232c0d61a30d88fa0445615533a491712b217ca108e2a343ab4e0a63e182" + }, + { + "path": "wordpress/wp-includes/revision.php", + "size": 30712, + "sha256": "294c2bcb7e03609bfb94d480017585477f7be02bd092ca3646612c42e45fbd60" + }, + { + "path": "wordpress/wp-includes/rewrite.php", + "size": 19461, + "sha256": "03051a70b5ec18d476611281e89a8262de984bccdb0a3923e93331eb80575c4b" + }, + { + "path": "wordpress/wp-includes/robots-template.php", + "size": 5185, + "sha256": "aeab36b0193d513498138a2a357770d10d9d011b6d74cd9c8203d24d3dba3ea5" + }, + { + "path": "wordpress/wp-includes/rss-functions.php", + "size": 255, + "sha256": "831880b7e8d307ce67f19f7477f4de3c2ad19320d94246bba31c6b708ac3742f" + }, + { + "path": "wordpress/wp-includes/rss.php", + "size": 23203, + "sha256": "fcb3b96531a3688acf02a57c226c4245401a9943e6981c14ff76f976c951c0b2" + }, + { + "path": "wordpress/wp-includes/script-loader.php", + "size": 163126, + "sha256": "e5b725771b52db8da27ff34d0924d6a8f39c0b49cc3585e656f916b580889a31" + }, + { + "path": "wordpress/wp-includes/script-modules.php", + "size": 11943, + "sha256": "fc84b220e282f47a63c5482d5e35195e6af64810b872d47d7e286d370162758a" + }, + { + "path": "wordpress/wp-includes/session.php", + "size": 258, + "sha256": "0feca7640b876186887529a69d49c35f2e1402a580e4d7587c9c8564436122be" + }, + { + "path": "wordpress/wp-includes/shortcodes.php", + "size": 24034, + "sha256": "8b41aed4d1f2dd9a0b517bf85b941bacf4616ae148bff0a7cdf48286df6e7854" + }, + { + "path": "wordpress/wp-includes/SimplePie/autoloader.php", + "size": 2053, + "sha256": "cf4d0e5fe1425e42147e16b485dbd0705bd10a1424eac8e9d022127f56e64bf1" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie.php", + "size": 15353, + "sha256": "3d00b8f54d92ff53b506cb53389752f0af52f2d49597b9f04e52a2f12127c262" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Author.php", + "size": 543, + "sha256": "9bd410ee7741c4685ad159dba28d84bbf5413a067f6d0568046c5b0742923132" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Cache.php", + "size": 536, + "sha256": "8c7d1ab14a0a031ccc7bb497262012a0af371696c2052e7051893e94b54b4d84" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Cache/Base.php", + "size": 573, + "sha256": "0b0fdfaa5d958ab92027b4db9f0e5afa77f1793b27632841d1216ac5edd8fb4c" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Cache/DB.php", + "size": 560, + "sha256": "ac3dbfa6d626a710a1d91bbf7883e31cb5b259a4ffd54bcb01a8f072c7049fb0" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Cache/File.php", + "size": 565, + "sha256": "9c38c3e6ec906a7627ce927dbeaea8dbd4860a744f69dad32fe00430b2c1275f" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Cache/Memcache.php", + "size": 593, + "sha256": "f8c203932b1ea75f2fdeab3be638fdb01ded14dadc706e592a5bd8857f488770" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Cache/Memcached.php", + "size": 600, + "sha256": "741e6efba871589f6dcb3890374ca9670326fb8de21368eb840e83d21f347fb6" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Cache/MySQL.php", + "size": 572, + "sha256": "587c11b37dc05ec1556c4480b943fc36a7fbbf22bdcf0d2c2181ef3fe2af290c" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Cache/Redis.php", + "size": 572, + "sha256": "539fa478be742945d48e3f069a324cec958d25867e175a76342e10a797184d9d" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Caption.php", + "size": 550, + "sha256": "9db2576afc1d520c8aabc890ab89fabd64d7cb55e0d112837d474dd97872825a" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Category.php", + "size": 557, + "sha256": "41dd16eee7282a46dfb44f09689ccfc7757a587e9d5eafb7f735d23435a6a68c" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Content/Type/Sniffer.php", + "size": 628, + "sha256": "8c05e045baf97b8cfa2e3ae214326e245708e881add74022630c3be347f1b4d3" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Copyright.php", + "size": 564, + "sha256": "0306fe5e01aa78e97fe7aa1c56a9e1fe9cff7cec8eea072070efa380ec8be015" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Core.php", + "size": 308, + "sha256": "92b7b9142dd088c9c1606d80dc771038ec333d45f140db5510b7652f58025b83" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Credit.php", + "size": 543, + "sha256": "5f2e5791e40c78565d7d0f6911035a35bbfaaa340f3d9b2f6509ca533ec883c5" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Decode/HTML/Entities.php", + "size": 22661, + "sha256": "a5fce8d0e75126b0a562eca19f8db90df1b7b25bb08aa6c7f17ba2eabb7d0874" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Enclosure.php", + "size": 564, + "sha256": "cd1838dca239c2fbf0600989a1beb2aac2fad1a3cc36b660f5427f09d60806b1" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Exception.php", + "size": 595, + "sha256": "e86703e86e545f6783a50a571f39b7a54d72e16e16e9c0a585242721c2c9c368" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/File.php", + "size": 529, + "sha256": "d8f4e47e75173a9cbec524ab3faca086aa80a1f08e71b5de0b0430c1d74e3aff" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/gzdecode.php", + "size": 557, + "sha256": "0b77325e1fb7afbd5fa87d35d3ee9bca696ec456f1a41ef549639e17ab6e12b6" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/HTTP/Parser.php", + "size": 664, + "sha256": "961db60642f2ed9e1eac0e99dd88c3f84e24737e0571959159293e6d847f1b47" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/IRI.php", + "size": 522, + "sha256": "dade5507c0b54e82b6d6e65e9a96887a3459d9e8ed170043f8f9a068c594caab" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Item.php", + "size": 529, + "sha256": "a2d5f76f92dee827ddb6c427c3b0c94c98ca03212ae2b5f477a4fe47dd888ea9" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Locator.php", + "size": 550, + "sha256": "6f67021017614b9708516b6f700086d1483254fa778990fb9796b8e67c2f80b5" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Misc.php", + "size": 529, + "sha256": "efec3a5f100dab41de94bf08ceea1b560fdc03bbb066ebc6d756e79a6b52a890" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Net/IPv6.php", + "size": 553, + "sha256": "9f2e45839ae648052385d89f998cdf4643f27cd02a9762c4181f09af9a58b2ec" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Parse/Date.php", + "size": 565, + "sha256": "9f760e92a21f6fd858af22b83d74cceebc570a0c5ebca9d424c35c66dda1e7f3" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Parser.php", + "size": 543, + "sha256": "7c3c778f27c60fd1bc53073beab2205c37892a81a5ba6abd2774cef273d25065" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Rating.php", + "size": 543, + "sha256": "9845e162a829cb235ba7b07f7bbc0892eee4298b625bc1de744a128cdd9ee3f4" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Registry.php", + "size": 557, + "sha256": "d97ffe8d444a9deb6281cbb14b3f4e626c0ccd187ed71f875af4f030158f5862" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Restriction.php", + "size": 578, + "sha256": "d2c2a35ad6af9c14485b4c0ce1686c98bb4bf7d907184087e1c5a1b0a69762b0" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Sanitize.php", + "size": 557, + "sha256": "5cbd9375ff366bd84ff9bfc8e85a1e15c699cb2dbda6f74a52c58126d4319f06" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/Source.php", + "size": 543, + "sha256": "b9b15522874b8df50be914c1d42fb813b43fc892a14029f822d3b09e4f53c542" + }, + { + "path": "wordpress/wp-includes/SimplePie/library/SimplePie/XML/Declaration/Parser.php", + "size": 639, + "sha256": "654006ca65935251ca1d3d8ce083af2cbc213614bafa89c987efc8b253772fb6" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Author.php", + "size": 1906, + "sha256": "ec14505d2f3a7639488d6bf7be1fc75f6bae501c2a3cc89a239a1bc62d05a66a" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache.php", + "size": 3248, + "sha256": "b4095ce1911410521640332e33d803fdfa7a956df75b2c32d7fb0e5e4645a545" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/Base.php", + "size": 1878, + "sha256": "445705018f4a73ae592b98ca2fa3032e2dcc716033294e2501b07649a8d5a8ce" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/BaseDataCache.php", + "size": 3587, + "sha256": "1cb5ce864bc143a6e091ec2028ff5a2b58c4a1d3394b30dab8bc6407c07b55e4" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/CallableNameFilter.php", + "size": 1518, + "sha256": "450f3ccf32ee1229e384b013e72b38ca723580798ba1899a031a659ec653905c" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/DataCache.php", + "size": 2799, + "sha256": "42e8adc8734148d163c9bd1f97987eb28d16fbd971ff14ae15cd69870122333d" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/DB.php", + "size": 3705, + "sha256": "fcbe6794aa06c433013861c44afed9a0ba75d830684ad3dfd864e00dcb45a193" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/File.php", + "size": 2977, + "sha256": "d047ceae38ea4329176506e91f0451dd9364a1345fd57af73af216d26850c287" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/Memcache.php", + "size": 3712, + "sha256": "4e94c099353efcc18c98d48e4e5f586d6f8ec1003a90228a82485e7dd7a98ec0" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/Memcached.php", + "size": 3829, + "sha256": "a086b3c128fb29ddc1c2e41757561c3fa6a94c1fb11983c972dbe9d1a6755426" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/MySQL.php", + "size": 13703, + "sha256": "765f05a8b6c955bec472de45b3e6378f31758f2667ce8a387e6dcdeb6e2a2d75" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/NameFilter.php", + "size": 1175, + "sha256": "c2749481b524804cd587cc3101754d7d96234c24175b8d2f3b62b7142c66603e" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/Psr16.php", + "size": 3256, + "sha256": "d991394aff4754f941cde402ddb4883ee93b33bca805276a124f2611434da458" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Cache/Redis.php", + "size": 4305, + "sha256": "47131036b35c488c0b56e088b922dd69e56446ac3ba6f99a909d70f548ad0bdc" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Caption.php", + "size": 3099, + "sha256": "d32af0f40bbab53eb7417b6043c8e06c7af26d2950139ea071c0f407199d44b0" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Category.php", + "size": 2472, + "sha256": "5c7e267355083bbe9b78bb629a3ac69e5f6d6bceebaafd49e56a85aa781796b7" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Content/Type/Sniffer.php", + "size": 7646, + "sha256": "a9d01c701511448b5733f68ea990c3aada4ad543b21788d3b0bbdbe0eec342cb" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Copyright.php", + "size": 1714, + "sha256": "40f0c0616c6c494541cf4529f88330b1215b88cf979efa094c7213f63aaeaddb" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Credit.php", + "size": 2144, + "sha256": "59295bed2cc8f45b98a6c9f708741ac97eec349e5795773eb2470b6cdf5a9cac" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Enclosure.php", + "size": 32142, + "sha256": "39420edbfc83485cb562dc7b096fe0e13d65b3ae286a9ef0eb224d813f85a6b3" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Exception.php", + "size": 355, + "sha256": "2ee0aaa13f0319d8868a9883867015b2ca5aaf1e157ea576621d1dc5bf123cd9" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/File.php", + "size": 19394, + "sha256": "7c4fc2bc550b36d1d7869ae4dc27197cef50557fadb4ae23e4afdf87c7300e33" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Gzdecode.php", + "size": 8512, + "sha256": "c7908d464e9db96d56bb6fc36e5fe4765b52f63b430453e1564c725baf5938a2" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/HTTP/Client.php", + "size": 607, + "sha256": "39a5263e18c28a3e0b9f78ce6361c7bb3331ba7920961dc34780bafafe00d646" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/HTTP/ClientException.php", + "size": 333, + "sha256": "0eed18f70db77df7c10d07ff9b2c743b1e95031d7b23dfc377a75c8a2e40090a" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/HTTP/FileClient.php", + "size": 2267, + "sha256": "699c62ad6504885b6e3b855abd3b2b31644c850cb2f7d31a39609f7e3f19b7a4" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/HTTP/Parser.php", + "size": 15209, + "sha256": "3e5bcc975dc45b379b32529aa57ef5a7288811ab2bc5c8e2fce195f5e15ed770" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/HTTP/Psr18Client.php", + "size": 4447, + "sha256": "aa1f31bf719121789c73d92799a2404dada5eedc5ffbccaa8d96ea96982a3fb0" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/HTTP/Psr7Response.php", + "size": 2177, + "sha256": "4e4c50281872554a3554c3d5c79783f5b272efa80b553c504f6c81162a02226d" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/HTTP/RawTextResponse.php", + "size": 2071, + "sha256": "d3a390bc5d6dd77968672f366eb5afe630ad8135a7b708714c250d52d5e616c9" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/HTTP/Response.php", + "size": 6941, + "sha256": "cab84fce2fd999add45a2483018a9e7989dbcca703d424849ef52a9c601ed463" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/IRI.php", + "size": 34807, + "sha256": "9e8a6df8df4837f83255625b34a61dd2219841fdf93cd7cad2a51e89fee63541" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Item.php", + "size": 131621, + "sha256": "8269eea9c2eeb5b949109a1342af1217aa263eab489359b134c8ab80e32dddfc" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Locator.php", + "size": 16699, + "sha256": "27921d78ee3d37e7d3f77fdfa85a41c99d8dba57d3d9b7b97336bc8ed39ca9bc" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Misc.php", + "size": 69797, + "sha256": "d0d6e4e006d140b3e0dc97124c8d89f24e4433d3d0edac910d7b3c124018f3df" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Net/IPv6.php", + "size": 7380, + "sha256": "59563b59c0b40197063b5006b1abde0d858ab688ffb316852d628ebdb33312f1" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Parse/Date.php", + "size": 25734, + "sha256": "8a995ca13d2f1d476f60cba0aea6ab2d04b2e205d297e472740d80c9f366d128" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Parser.php", + "size": 34868, + "sha256": "7de0aa16ec8c0b341ff39584ac35625ae1b95dfc968944a43f68aabf1a7868ba" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Rating.php", + "size": 1794, + "sha256": "3e9fb0d346ac9a8784a8b1823d06138fbdbef6a35f5be17a3c97413c8f3623c2" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Registry.php", + "size": 7896, + "sha256": "affc911aefd677e1fc88ef1ee5a6289874da63ffbde3411febd85f4212321273" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/RegistryAware.php", + "size": 447, + "sha256": "6fbe26601a9d1d3bf8523417da0f6ce9c17bda7a76b64f17b09c9112e2b1c7d8" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Restriction.php", + "size": 2369, + "sha256": "2a6a499c1335e86e3c26eae11899934db8dbc8d4ec819cbb40e27a6fb182daa9" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Sanitize.php", + "size": 30353, + "sha256": "0f8d2f4d5b24674340cf6256c3ca2421c02701d87e4c61b6ccc2c607dcd78b8c" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/SimplePie.php", + "size": 128052, + "sha256": "14adaf4bd50fa87f3b7af39f022eb52b1fa82a25b4f6ede99af9083a60408e96" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/Source.php", + "size": 23738, + "sha256": "599970679da6cd5c0efe68e69999a9444114ffc2e0cc555303051780bf6d14fb" + }, + { + "path": "wordpress/wp-includes/SimplePie/src/XML/Declaration/Parser.php", + "size": 7744, + "sha256": "39d1babccd44dbd60e2e46dc43e1f011500b03785a8d8bcee1239c45c7a0c80a" + }, + { + "path": "wordpress/wp-includes/sitemaps.php", + "size": 3238, + "sha256": "43cebbd1901cc9f2d1588b8dc848322218b1815d77af715570014525837f50e3" + }, + { + "path": "wordpress/wp-includes/sitemaps/class-wp-sitemaps-index.php", + "size": 2010, + "sha256": "e83abe8c423d4291e350d11e96ede7aa4a9d03c3a318fafdefd2b80689ace274" + }, + { + "path": "wordpress/wp-includes/sitemaps/class-wp-sitemaps-provider.php", + "size": 4413, + "sha256": "f2bc6292016d758c60ef8fa1fa39e33f88c5c8d8429d0b4fefc0dec7152ff1c4" + }, + { + "path": "wordpress/wp-includes/sitemaps/class-wp-sitemaps-registry.php", + "size": 1934, + "sha256": "a00e991b55d6fc71f4c4aaad5cc4621001fc0a9295f543e4f6ee5f9c4c048355" + }, + { + "path": "wordpress/wp-includes/sitemaps/class-wp-sitemaps-renderer.php", + "size": 6687, + "sha256": "1286d5b29419d32799677ea2f59c27d3b4b0b28ecef1bd940184478b768cd75b" + }, + { + "path": "wordpress/wp-includes/sitemaps/class-wp-sitemaps-stylesheet.php", + "size": 8502, + "sha256": "bb09410298a9e1c876455fc24778315200bf01bca641256e9105cdfc676aaa16" + }, + { + "path": "wordpress/wp-includes/sitemaps/class-wp-sitemaps.php", + "size": 6402, + "sha256": "ebdab81d3c2115a39d2a2107bd5bb1fedfcdf7dfd7c343e5a957f90dcec426aa" + }, + { + "path": "wordpress/wp-includes/sitemaps/providers/class-wp-sitemaps-posts.php", + "size": 7509, + "sha256": "9e6f8d5ced3937d7f8636486e9e687c877afdfcc111f9053f1f89e753b8d49f5" + }, + { + "path": "wordpress/wp-includes/sitemaps/providers/class-wp-sitemaps-taxonomies.php", + "size": 5905, + "sha256": "687126c3c98739fa124f5afaf4e287ae80c852cd8b7ab4692a1a75db9c41d157" + }, + { + "path": "wordpress/wp-includes/sitemaps/providers/class-wp-sitemaps-users.php", + "size": 4185, + "sha256": "4a993c84258f4b57a4a28d86f59877e3487fa181f3e1a6f872820419d4f52da3" + }, + { + "path": "wordpress/wp-includes/sodium_compat/autoload-php7.php", + "size": 890, + "sha256": "1578b1c6f97fe3bd1417e6b493c4f55b1666659b1e6dafa3b22d7730f90f1144" + }, + { + "path": "wordpress/wp-includes/sodium_compat/autoload.php", + "size": 3117, + "sha256": "6d07afe8f71a5cd49d55261a1d18467d1b5fbb9aff1ddfe71dae92a1a04f6731" + }, + { + "path": "wordpress/wp-includes/sodium_compat/composer.json", + "size": 1608, + "sha256": "625889e17b2de0a4813d1d57ab177183bf069d6fb9600ac0f25ca7602e74e3f0" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/constants.php", + "size": 4161, + "sha256": "e211d55b064f9f8c6994e16efc6d3308ef943ebf1845c8d224e694f2660c10c3" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/namespaced.php", + "size": 1345, + "sha256": "476eb188ce7b66400c21748efb5accfc282ea02474a793042594452ee582392a" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/php72compat_const.php", + "size": 4597, + "sha256": "040aaf18aa07506264d40a64e83e8dc8e60a0c704bc5685f9301ec00fc527f07" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/php72compat.php", + "size": 47347, + "sha256": "45369eb57707c4b2b43333e57a018a3f2bc8372c35524189c70aa44e36592b74" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/php84compat_const.php", + "size": 401, + "sha256": "5164dd202126ef19dfdb7303347bb3c6977f9ef45c6b117c02fde01d3ab499d6" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/php84compat.php", + "size": 3656, + "sha256": "153ca2d911ca75988264abc5be241fd0331bbd5d2580fe502ec6e6f89361d388" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/ristretto255.php", + "size": 7977, + "sha256": "02ec4fdd5a82cc16b00f186a34e0595d41e93d154b09847f1bf4a021068d81df" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/sodium_compat.php", + "size": 27446, + "sha256": "f5674de460faa89c032b8c62e885a16b6e60e04a71e953b8a0d8b82f389aae50" + }, + { + "path": "wordpress/wp-includes/sodium_compat/lib/stream-xchacha20.php", + "size": 2075, + "sha256": "2e1269a2d5bb91448bea51525d76c6eb33a70acf69eb412d8425ba04b8aaa28b" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Compat.php", + "size": 86, + "sha256": "ae59dfb04b30ad6a6999d29826c86250393010df28c2f0e11b824280a6a579d4" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/BLAKE2b.php", + "size": 98, + "sha256": "ffd683c48ec5fdd38911303ae4f151d423c12bed750730514f698719f35bce18" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/ChaCha20.php", + "size": 100, + "sha256": "2482e70fd87c7bd235a340d2df60d88e806eb936b639044eb8732f2ccb3718f2" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/ChaCha20/Ctx.php", + "size": 108, + "sha256": "61609cbbd05134d59342ff9ae3e77cf627be9dac73bde356f36ec1059afb4d3a" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/ChaCha20/IetfCtx.php", + "size": 116, + "sha256": "0574987a882e6b4e6b39ff0082f4e7a93f2db6b8fe0d39f2374c31f8a4dee867" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Curve25519.php", + "size": 104, + "sha256": "81e544f88b402e6d040741acae42e7846c24c971bf2420479e0e5be16c907bf8" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Curve25519/Fe.php", + "size": 110, + "sha256": "0c1bdcd6743d95c7ee0d1a16fb3956d30f974e95ab366144ecad2713d0dc668a" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Curve25519/Ge/Cached.php", + "size": 124, + "sha256": "cc8aa08af8ef7e8589f8cd845cdaf34f476dffce15e9aedfa9442a07014a8e66" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Curve25519/Ge/P1p1.php", + "size": 120, + "sha256": "28080eba6fa5689e36b6e2729e516487c5a796d22697b5bddab7a1832dd9671f" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Curve25519/Ge/P2.php", + "size": 116, + "sha256": "bc5f9ffb87c43a7f260c90165ec7b1b4d4ef0a76aab1c033d635804577f37930" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Curve25519/Ge/P3.php", + "size": 116, + "sha256": "bd61dcbf8084251ec6ced2b96e81a36782882d960fd853fffd701c075b020d01" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Curve25519/Ge/Precomp.php", + "size": 126, + "sha256": "229c34972af661a6c2fffb9f46b048c4f2f871bd147433495d07afe15bc43a14" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Curve25519/H.php", + "size": 108, + "sha256": "9f945f6b32f34b23282cb15654dbe5fd6c9b088d1c11aae62a932b45d01e2404" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Ed25519.php", + "size": 98, + "sha256": "2b40b5cc980b2c61cf11b3805b2e504ef285bcece051f31b5383ea3056a7569c" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/HChaCha20.php", + "size": 102, + "sha256": "ff0f44d534ca1a085ca503a5bc02f0fd5c8cce9ad0cdbd5ab69530497fc92f1d" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/HSalsa20.php", + "size": 100, + "sha256": "202dbf136f07f5fd1fb490d09cd310e621d016a75118494ca9bd35abd63a8190" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Poly1305.php", + "size": 100, + "sha256": "db9e59e0d0ffff7e21f9d7a416de664bac98d0818653dc380af2422421198582" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Poly1305/State.php", + "size": 112, + "sha256": "cf3e7db368bd91f2b13823d393b2019c83eaddc8934cfcb83e85761740ab1699" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Salsa20.php", + "size": 98, + "sha256": "645f40cd76a9e39d9baa209314f15d9c05828b5c6170450c3e568a811f68c73d" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/SipHash.php", + "size": 98, + "sha256": "8ea1b29d750e901b8a774b716b354a8a6b8ae7b023c1ca5a009a0b67737cdaa3" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Util.php", + "size": 92, + "sha256": "d17abbe67dbb87e9114f15365f5830de52f5eeea8f44f1265b32cd8c9ca78611" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/X25519.php", + "size": 96, + "sha256": "7df850a3b047b4d5a691df65e7398db6f8e5c029c0fe7195014cf6103d733c9f" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/XChaCha20.php", + "size": 102, + "sha256": "e98d67678cbd7c21d3a0b3a25b08da1ecdc83a67e607d00a9efb21fa462f48d6" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Core/Xsalsa20.php", + "size": 100, + "sha256": "87423b4f144440ef7b73bdac40bb7c844e2266df1b1aabcc3fec165b2780c5f6" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/Crypto.php", + "size": 86, + "sha256": "fd56b4cab6806a9c819332813af5b50f7be14143424d5f2243fadfa65fb66f47" + }, + { + "path": "wordpress/wp-includes/sodium_compat/namespaced/File.php", + "size": 82, + "sha256": "7890c0a02a8667f6b1532da1a38623faf2af4c6cd5a00a1d54471b43934a3f81" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Compat.php", + "size": 164693, + "sha256": "139362e4612380a35df023885794cf1b9de903fa661f059ff7b6389bea198bc8" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/AEGIS/State128L.php", + "size": 8234, + "sha256": "ab39d7ce243e6c202efd5d03b57da7b2f50c53cc6e9d9c9332aa40b95e13166e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/AEGIS/State256.php", + "size": 6525, + "sha256": "d65eaf34883200b1346ffcd64f8086cdb995d3c0947922fb731938d5621cf481" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/AEGIS128L.php", + "size": 3668, + "sha256": "2b71db2fc9301d656887c97dd48504a12010b6af7824acfd0665da5e4496935e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/AEGIS256.php", + "size": 3598, + "sha256": "300d456b41ba37cc0cc784f163a45234cd103e01437747c0a076bb429e55b0cf" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/AES.php", + "size": 15885, + "sha256": "723c60d5fb6d3404f60791e579373e1a369f0ffb49fa8243825a2473cf5affc1" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/AES/Block.php", + "size": 10466, + "sha256": "4d8d69d658c6d4f306314501b7b07f268ee799b5e1c601a510559b2e8dae4913" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/AES/Expanded.php", + "size": 304, + "sha256": "d981af4c4ac849d885583e577cab12205a8a2b4ef0026a2cf51823f23112daf7" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/AES/KeySchedule.php", + "size": 1881, + "sha256": "c1942e7923f1bc9d3e8d4dfcf213d59a8f74d95283600a3f968e7d187ca3e10d" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Base64/Original.php", + "size": 8476, + "sha256": "226cf150cb230310d0b77ae4e462f706d0ddc950ea6547e032dd891fc57ddb0d" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Base64/UrlSafe.php", + "size": 8483, + "sha256": "34e40a8e88d16043f40bafd56d9de1501cfe72345845f5bff1982e56c24b37fa" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/BLAKE2b.php", + "size": 24192, + "sha256": "0b4411492b6e615e48cd280636d7bff795d5dcece452384b5529ac77c79e7f28" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/ChaCha20.php", + "size": 12889, + "sha256": "e16993d79b66dac7e8341fd20b0ef9173dcf8b73b22ee48920af4a5690ff6c7b" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/ChaCha20/Ctx.php", + "size": 4415, + "sha256": "f0e0638eeb041771d9e28cbe743b69e078af272066ed61ff5aa06fccbf895d13" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/ChaCha20/IetfCtx.php", + "size": 1324, + "sha256": "a141ca567f91dae9bc2d9aa0a3054fafd6bd1235492d6d5d38c9e9a8364c2023" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Curve25519.php", + "size": 143641, + "sha256": "02f855c266aa4376c19fc9f551f4b923260791a6fceed38d9c39732279eaff23" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Curve25519/Fe.php", + "size": 6352, + "sha256": "16d1e163378754a173dcaaa97ee42799bc7e9965650d2c40e3d83b52d5a3cdc6" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Curve25519/Ge/Cached.php", + "size": 2370, + "sha256": "bbfeb1c884d3e64e223a6da86503154cd7cb478c812921dd31b50e71e252eae4" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Curve25519/Ge/P1p1.php", + "size": 2257, + "sha256": "a8860db08a54f9b598c2041cadf00589da180e2229eb175d782ae8da04845704" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Curve25519/Ge/P2.php", + "size": 1789, + "sha256": "7c57be8e8ea812c754ec0116fec5a85b62719ff18a6950b5923a04538e989f94" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Curve25519/Ge/P3.php", + "size": 2250, + "sha256": "9694bd0948d20cb45ca3f787e85013884d7df7b543a344838f1111713e9e576a" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Curve25519/Ge/Precomp.php", + "size": 1906, + "sha256": "cef6a5f25e9bb7863ee00dc0ed6a7a3b3f153c9daad3936d666da55f7e07327e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Curve25519/H.php", + "size": 110457, + "sha256": "5bdca3663d50486dd4d1101cf90b10f5ef9fbb7ccbf6fca8c29487941ba348ae" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Ed25519.php", + "size": 18482, + "sha256": "08a28c75a29a4705096c0c62ef8c40b35244ac04d45d82a9ea79f68941196e15" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/HChaCha20.php", + "size": 4140, + "sha256": "e18efdf82e55dde96447a791eed1b6012af2951c7efe64b2a25b8d2dafa007ac" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/HSalsa20.php", + "size": 3673, + "sha256": "15f474f45cea3fece4eac6177580835539cf63849316b37e1e99e4edd14bbca9" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Poly1305.php", + "size": 1576, + "sha256": "6f78dc8121fed9cb4a02c1f7c2a915b2bd0f3b31e970175b5ed70013f899023e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Poly1305/State.php", + "size": 12912, + "sha256": "fc192e221f937aafc3cdc4b2ac1dd516f96553a75387762625f7fe56344bb144" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Ristretto255.php", + "size": 21884, + "sha256": "22ec93547310b8c3b3dc63eb5caad9c29bd6e4539b5bb18138e387909016912e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Salsa20.php", + "size": 8233, + "sha256": "ecff9a937af88fe087ff432b3dee096213890cf5377a2734edeec880547f360d" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/SecretStream/State.php", + "size": 3624, + "sha256": "7f5cdc92ae4330588efaff8a28d3251eac9b48aeab70e448604d180471f195b8" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/SipHash.php", + "size": 8233, + "sha256": "e1297c4ff528824e08d6082a91ccb356261153005f4d18e68c71b54a30c4e65a" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/Util.php", + "size": 28904, + "sha256": "1d508481ef6bbb5ca9e453cf044777128c78a9a37d0a7ac211e713340b2525af" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/X25519.php", + "size": 8413, + "sha256": "1b0cd07db0ebd48be4ddf91dded5056d9b5a33f1802fd5db461e7b9cd062f6e3" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/XChaCha20.php", + "size": 3320, + "sha256": "0aac8cebdfa935bbd6fbbe145268934d656a2481de89bb049c9ce20fe5d8004b" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core/XSalsa20.php", + "size": 1371, + "sha256": "80151b03429b8d17ea792b4da7de33d1ee60b97b0a82bdaaa2fb98699a368322" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/BLAKE2b.php", + "size": 22324, + "sha256": "520be088a2028f7633804179ad1843d4386a969acc754ee01b09c3e8b9b85b41" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/ChaCha20.php", + "size": 14511, + "sha256": "d7e29584cc431eb49919fd01cfc8264f90bebef7076e4bb2b4b133c630111c72" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/ChaCha20/Ctx.php", + "size": 4904, + "sha256": "9ebee6da219dcd57458002b18b403db77ee105ba1c1f05ab11e69009ed655741" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/ChaCha20/IetfCtx.php", + "size": 1503, + "sha256": "3d9cc2c6e0af0415b5d19c825eb7d62caa1299ee31ab68715982976b53c9d853" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Curve25519.php", + "size": 132974, + "sha256": "6f09b3a87b06c92e82dd276fb10ea1062edcd3c4c9eb5de8b156539b6510bcc3" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Curve25519/Fe.php", + "size": 5498, + "sha256": "f4e6c38e08d6ece5ee49dfe197bf03be0dbc2e357f4d26413d14c85fcb479a4e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Curve25519/Ge/Cached.php", + "size": 1805, + "sha256": "1f35e8d649cb0527d40fd3c0e05623f4535a62e60c0dbb7b23b35fd4c223f80d" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Curve25519/Ge/P1p1.php", + "size": 1764, + "sha256": "a73351127182d1548afa20c027288c8b23f4387b16b608efd6cf5b48b16ad800" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Curve25519/Ge/P2.php", + "size": 1377, + "sha256": "c20e998d89be90f328b1931916fd676d2191b7a2a414367863a32a4d25d95afc" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Curve25519/Ge/P3.php", + "size": 1698, + "sha256": "3435380807c5c1fdfd3e0a03f054119853fe9e82a18662c48fcfbae696b7125a" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Curve25519/Ge/Precomp.php", + "size": 1533, + "sha256": "c8c09e1e6af63f3548a6e6ba5e792c466c4f85ac6a1e5338ee4ceae4f2c94e6a" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Curve25519/H.php", + "size": 108797, + "sha256": "7809943ce7c07996f2da77548d9fcafaaf41cc1c32d7f448bdb0786577a4374e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Ed25519.php", + "size": 15735, + "sha256": "66089cf07de8b1e16a80288e3e673260f864dc0766216a483155661d36caf3ae" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/HChaCha20.php", + "size": 5297, + "sha256": "4f670346620429b2aedb16f3018598d0c6772b48719fb01231af654fdf7cd2a4" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/HSalsa20.php", + "size": 6941, + "sha256": "ee0884706a0b59fd1e346420b734c40f7f528e24cfd1c45dc491b1f64ab07582" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Int32.php", + "size": 24580, + "sha256": "dff42d5e03045a57bdbdae417bc277c9d8ac96117e39ab01b205587f1c19cc9e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Int64.php", + "size": 31172, + "sha256": "25363bea0769f796bcaacf77b014c014c8128c5c1a1232e7c6ad4bcf661a3f20" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Poly1305.php", + "size": 1586, + "sha256": "97babb05e989a164fadde055c4efd1f3fd306cd9f3e41fb8d8be910d4e74b86a" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Poly1305/State.php", + "size": 15965, + "sha256": "0ac2f7789512ead254d7b4093853ca23a8415f191d626a3635ebeda7dd2036a0" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Salsa20.php", + "size": 11506, + "sha256": "7500ed3a49401dd69341293627d33f5c300c3d89d5a67a40931d6daf9d734b97" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/SecretStream/State.php", + "size": 3656, + "sha256": "22f159f0c32748439170f03ff3735bb06e1e7a6bc5f30cf907c388ede721b97e" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/SipHash.php", + "size": 6613, + "sha256": "80228e14f6feeec9483e340e1e33a2330a91de70dce9049e2a25f4e19ea1f0b0" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/Util.php", + "size": 209, + "sha256": "78f690cb63b6292d3759a9ef588df55f497af822fa895d59cdbcbc23d00b0298" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/X25519.php", + "size": 11042, + "sha256": "640fd6b6bbae8659e2ece488eb57ec886b7d5adb9dafc88c6c1db66ef82cbba8" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/XChaCha20.php", + "size": 2454, + "sha256": "80ff1e308e00d96688b44ba5c89fdb3aae68618662ef4206162de7f8db03c689" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Core32/XSalsa20.php", + "size": 1379, + "sha256": "e23aec8e591ea6ebe9260e67f32b335692c5e847b383beb705065ee6c676b630" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Crypto.php", + "size": 54964, + "sha256": "d93de40d8523a69ee42e825341f9b4c35099b307c8a8c2713c80cc74d0e651a2" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/Crypto32.php", + "size": 55119, + "sha256": "ed89b3eccb14c5c47240a6bae06f2eed1cc8fd04a714d3581ed36e01b0601cd7" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/File.php", + "size": 54811, + "sha256": "72d4b56e77ad9914ee48bc706e5c2c2d3f7b8c254c23bde7504c6e84f8dcbd25" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/PHP52/SplFixedArray.php", + "size": 4910, + "sha256": "466be1d4bfe69cf86963b8bb69917c4398900122c4885a5a89d9c891ffdf353d" + }, + { + "path": "wordpress/wp-includes/sodium_compat/src/SodiumException.php", + "size": 158, + "sha256": "4dc4b39dfd4ec7932aa9680ee915ec5c407e3bf6699f3f694102b292543e8b04" + }, + { + "path": "wordpress/wp-includes/speculative-loading.php", + "size": 8600, + "sha256": "d811e40ada7b04fc2e1227041edcae09a4339d0297e977f3a175fb4b88f610f6" + }, + { + "path": "wordpress/wp-includes/spl-autoload-compat.php", + "size": 441, + "sha256": "f5b15139e19a2420753370028b6bf54ca2dab563b4b60074cdcc97e0220853b3" + }, + { + "path": "wordpress/wp-includes/style-engine.php", + "size": 7563, + "sha256": "54ae0fd3410aec5e03b74ff9a183e3369b174792adb2e20ea728015f3f8b494c" + }, + { + "path": "wordpress/wp-includes/style-engine/class-wp-style-engine-css-declarations.php", + "size": 5345, + "sha256": "ac6c88398f047479e3d50d0195c2902523f810d48b71741636e405d7feebc384" + }, + { + "path": "wordpress/wp-includes/style-engine/class-wp-style-engine-css-rule.php", + "size": 6062, + "sha256": "af4d598e812fe0dcd4265f0ed828efa6b4d5881916cd18a0a473e3ff3df4266b" + }, + { + "path": "wordpress/wp-includes/style-engine/class-wp-style-engine-css-rules-store.php", + "size": 3764, + "sha256": "fdc702bd09971c04bbc394f7d9301e7adaa86d7ada809c1d94b29bff1568cd87" + }, + { + "path": "wordpress/wp-includes/style-engine/class-wp-style-engine-processor.php", + "size": 5390, + "sha256": "533e3ea7966c97fa1978856e11b485e0cb2239dca58de797024ef97d50aa31e8" + }, + { + "path": "wordpress/wp-includes/style-engine/class-wp-style-engine.php", + "size": 28558, + "sha256": "a1df14b73b4224855fad56e55ec7f3bc4e2d0dca13f2041c3ff8e89625610da5" + }, + { + "path": "wordpress/wp-includes/taxonomy.php", + "size": 177144, + "sha256": "48af3bc6434c99beb2f274ba309590c4c063ab0c4984c6cc34d3697c5edbb1c3" + }, + { + "path": "wordpress/wp-includes/template-canvas.php", + "size": 544, + "sha256": "a1a8a7ed542c25d96c78d51438d84b3a7e053ded953f3452ae38f6b84f71038a" + }, + { + "path": "wordpress/wp-includes/template-loader.php", + "size": 4267, + "sha256": "29419e3e5fc3c20842395822b230d851c41e5ced70121431ffee7bf0dd917372" + }, + { + "path": "wordpress/wp-includes/template.php", + "size": 36824, + "sha256": "578e56453cdf93c67c8f968b7fa53bf9a6f138ad8792e4e36d3bf567ca427be5" + }, + { + "path": "wordpress/wp-includes/Text/Diff.php", + "size": 11958, + "sha256": "856f1717e7da9741f717fecffff8735acc1a995855e8c62b224d2c3a290f8bc7" + }, + { + "path": "wordpress/wp-includes/Text/Diff/Engine/native.php", + "size": 16049, + "sha256": "0d4cf24fbc9579d21dcb4a1100f9af4cc53d4e38c754e2526f1d56dd429ecc0b" + }, + { + "path": "wordpress/wp-includes/Text/Diff/Engine/shell.php", + "size": 5203, + "sha256": "a376895680e77451496664a3465712506e3d6a77d5f78271e34f903f769d9200" + }, + { + "path": "wordpress/wp-includes/Text/Diff/Engine/string.php", + "size": 8347, + "sha256": "f9dd02e54833fc6e0e69b1df598c989b8e2518834f8fa573650d95f217e0dc31" + }, + { + "path": "wordpress/wp-includes/Text/Diff/Engine/xdiff.php", + "size": 2203, + "sha256": "a54ba80d6fddaf5ca25a24826cae6c43174ee47f0e49ae1f2aebac2d49501769" + }, + { + "path": "wordpress/wp-includes/Text/Diff/Renderer.php", + "size": 6806, + "sha256": "88d54c6c1c0e53490d54ad80f2860f869aa86f6538739fe75b414d3ae61810f1" + }, + { + "path": "wordpress/wp-includes/Text/Diff/Renderer/inline.php", + "size": 5528, + "sha256": "21d31fa0bec82925b4d402b494b5486fa594fe45c3117d4adb60eaaa11959d4f" + }, + { + "path": "wordpress/wp-includes/Text/Exception.php", + "size": 241, + "sha256": "62655614733dd260d2763fea78fb1be3fca042dde7af6a0fe829d21386cf735a" + }, + { + "path": "wordpress/wp-includes/theme-compat/comments.php", + "size": 2148, + "sha256": "3050653209b028d6d6cbbb6ecc29720ba94b23607126f2d9a2de895d16f25384" + }, + { + "path": "wordpress/wp-includes/theme-compat/embed-404.php", + "size": 967, + "sha256": "a1289d817b0eceb03647d3d0b4d72dd2f9cc8d894bac6b8e1478a0b821fe6917" + }, + { + "path": "wordpress/wp-includes/theme-compat/embed-content.php", + "size": 3484, + "sha256": "382f213b31dd0e0c2da6243295f6dc024923edc4091505560924a98b1d6ef6c0" + }, + { + "path": "wordpress/wp-includes/theme-compat/embed.php", + "size": 481, + "sha256": "72ec50a9ac5aeb7f5073672b2fb7c08c1926051f85a330fc49fa49446c219d39" + }, + { + "path": "wordpress/wp-includes/theme-compat/footer-embed.php", + "size": 438, + "sha256": "a24de92c8b23fda7b49b45087f1c59538715ed7a062f3fac0101d854cb3464ad" + }, + { + "path": "wordpress/wp-includes/theme-compat/footer.php", + "size": 1055, + "sha256": "859591c424941c392e1e391e168aaae3aeecde3389bed3a2d260b29f97c4980f" + }, + { + "path": "wordpress/wp-includes/theme-compat/header-embed.php", + "size": 702, + "sha256": "f5ba9f25812866f8e61cb3dc3b33646a8d9e12e23effe1e29dbae978d524f76d" + }, + { + "path": "wordpress/wp-includes/theme-compat/header.php", + "size": 1860, + "sha256": "a80c47c04c222dedca260b268b5b1c7cfd6367b006d3e8056182eb0ebee1f839" + }, + { + "path": "wordpress/wp-includes/theme-compat/sidebar.php", + "size": 4489, + "sha256": "fb3a4321574896e27abe372f5ee0f3faf2a39c6a22b4afbe874ed83ca7934fe3" + }, + { + "path": "wordpress/wp-includes/theme-i18n.json", + "size": 1892, + "sha256": "00b0703c850691dd18d75e5a94270ebc575b663569670d01fc52497cc284562a" + }, + { + "path": "wordpress/wp-includes/theme-previews.php", + "size": 2887, + "sha256": "0e92a447b468ae7ca4d05a0b344c1b762f2512182f6fd2f9cfd91df5e658ab7f" + }, + { + "path": "wordpress/wp-includes/theme-templates.php", + "size": 4060, + "sha256": "4ba5da78bd0a837eeb4e818f44df52d56915658508ee44cd86b371ea5d12df7b" + }, + { + "path": "wordpress/wp-includes/theme.json", + "size": 9037, + "sha256": "f07d209c3f119d9ab1eed11052d19cdce7395f07e7f7c6f9490f802363826770" + }, + { + "path": "wordpress/wp-includes/theme.php", + "size": 134631, + "sha256": "74cd43c0b1695eb27baec7fc4b36079f20af77862eedc64776828e87da8a209a" + }, + { + "path": "wordpress/wp-includes/update.php", + "size": 38276, + "sha256": "a0f944cef4856535db9b53a1eb21f3bdf4f19ebca21d7d3e77d649db017245ec" + }, + { + "path": "wordpress/wp-includes/user.php", + "size": 178824, + "sha256": "3394b3ce03a8f5ca9ba28af276bb1a22b87d77a2fb031b467991d3ec4e241387" + }, + { + "path": "wordpress/wp-includes/utf8.php", + "size": 7260, + "sha256": "f06764a8f214a4e3d71cbac4ea5af4d9edeaf07701cfe09436c251a4042db0d7" + }, + { + "path": "wordpress/wp-includes/vars.php", + "size": 6607, + "sha256": "275bc54e7cb1523c6cdc11747c426540e2fd98a47a3c9b1e86a3fd3076f2721f" + }, + { + "path": "wordpress/wp-includes/version.php", + "size": 1103, + "sha256": "0eb0e81153a91014e9eb7f5cf2b5261e1b3b8ead89d834869d42eeb2024e860f" + }, + { + "path": "wordpress/wp-includes/view-transitions.php", + "size": 602, + "sha256": "2295af1d4d7c1c666c53d3eda143c7403db05960067c03c464a22f72b3b20740" + }, + { + "path": "wordpress/wp-includes/widgets.php", + "size": 70828, + "sha256": "5e51d83d86226609bfcfe3d4f3dbf6ead978f1018ea13648e8cf7cd27d4b10b6" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-nav-menu-widget.php", + "size": 6644, + "sha256": "b6035efdaccf0e8d6858579628d838cbb981399b11fe557b52f9fb9983a3782e" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-archives.php", + "size": 7245, + "sha256": "b437fb1af1e6addb56eb0d1f865a928ab27c3791be8ebf753b913efd2c69f6b2" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-block.php", + "size": 6557, + "sha256": "bb5595d739a34acd29714bae19977273093fcfc191ffa2c1cafeed3e91d10bfa" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-calendar.php", + "size": 2913, + "sha256": "a68df4a6ccdf3b1530d5f20be630755420bdbd88f7f59b6ed0f96a23b882233b" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-categories.php", + "size": 7606, + "sha256": "bfe76d575fedad800974a59ae21e7375a2ffd075fc2565c32ce5c08b1c1c23c2" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-custom-html.php", + "size": 12128, + "sha256": "8c668aa6826feb191443e3635f48b02a05c72e73a43482bdd5c84e603455967d" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-links.php", + "size": 7078, + "sha256": "e8ebabf0797e6989da5cb47e50a1ec04c603e9f59b437e0247bcfdfcece89fe3" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-media-audio.php", + "size": 6423, + "sha256": "f8fde79df6f5eda1f4f67460f1863f34ab86bb79de07a91ac50b48d7c7fb6626" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-media-gallery.php", + "size": 7548, + "sha256": "43f630882ac5aa6e173774c5f9d936ac654804ec9b49600a1e2a31b3ab890cbd" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-media-image.php", + "size": 12650, + "sha256": "74eabf549f0604ebacc33931c3ee3c5306d93172cb36cd62b6bc99974786cd57" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-media-video.php", + "size": 8826, + "sha256": "f33096f2c2fa6de59eeda46effc76648493502d87078a4dd909de37c2ef895f4" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-media.php", + "size": 15377, + "sha256": "ef1444202cfe0c31ba67b2f77252c57c41db58d340c3bd0c5e1f4f060ef67132" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-meta.php", + "size": 4091, + "sha256": "2be4958f1c8076d018aad2f0707b82aa9eaed2eabf16138da65bdce54dfd3249" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-pages.php", + "size": 5720, + "sha256": "7d84a46307b3e9a0878e04960afb1b8fbda6e979dfb61c2340c6b0eff2cf73ef" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-recent-comments.php", + "size": 6918, + "sha256": "80a93831407c18c3de00bbfabc4745499cd1ace7b7f19d55c07c239f5eec511f" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-recent-posts.php", + "size": 5905, + "sha256": "cdc47a7c0e108b38cbf1f4fa64a13d4239c7e5ff8af16de7fdc2ed52bab5156e" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-rss.php", + "size": 5244, + "sha256": "76e55ca41224122a1a01a858b0a9b7ecc126a24fc74120539a80c82ad9a19792" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-search.php", + "size": 2724, + "sha256": "3b1c1510e32e3912c6371fe2a7eacaedc22604bd4a1d0310289574d1eee1d2cc" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-tag-cloud.php", + "size": 6778, + "sha256": "8afaa1e6a945e2ee9f4713bc8bbe310094c139722f23d18d7a50c991ce67a1f1" + }, + { + "path": "wordpress/wp-includes/widgets/class-wp-widget-text.php", + "size": 21350, + "sha256": "03e7ad6f582be52ff17ad0c0ea623e38a2bbcfb3867a104b3f18c9f22fd786eb" + }, + { + "path": "wordpress/wp-includes/wp-db.php", + "size": 445, + "sha256": "9d82cf0ad6d04f044da36e20f724b314146edea68dc57f43334082a5ce4dd12e" + }, + { + "path": "wordpress/wp-includes/wp-diff.php", + "size": 799, + "sha256": "ef74102e7489ef221a7b9fb340f9fb5159da6d9177c3db69c5ace951f9c523a8" + }, + { + "path": "wordpress/wp-links-opml.php", + "size": 2493, + "sha256": "228a4d22e3b885647963a6ac5209efa903f2692e5e2c21faecb598602ced3ea4" + }, + { + "path": "wordpress/wp-load.php", + "size": 3937, + "sha256": "bfdc84087782be7aa13f64deb6fe6762f4c80058d9fa3d27705859716dc5dd11" + }, + { + "path": "wordpress/wp-login.php", + "size": 51850, + "sha256": "e0ab2fbcb9e5cf509dea66579f5bcb46738531b67dc74799349a27e26c537e47" + }, + { + "path": "wordpress/wp-mail.php", + "size": 8727, + "sha256": "351d58d29ccd6a982a6c4711b01788e19230d578f253d8dd8845748383b90456" + }, + { + "path": "wordpress/wp-settings.php", + "size": 32650, + "sha256": "e56fb879616515a04ead712bd6c72026c2177ff09bae14efa60d0046e6bb7861" + }, + { + "path": "wordpress/wp-signup.php", + "size": 34621, + "sha256": "e3b95c999070a16d1ad59adbb3f1aa3a5c3bf9edfa4c8942706fe7468d172899" + }, + { + "path": "wordpress/wp-trackback.php", + "size": 5214, + "sha256": "a32bf25a48af109caf79eaad1b262c2589cdfef1238c1ca65a71fd54514df51f" + }, + { + "path": "wordpress/xmlrpc.php", + "size": 3205, + "sha256": "b638886d692a78bb61055d90d6684d6f19d789fc779fb25489604c2be6e8b484" + } + ] +} diff --git a/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts b/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts new file mode 100644 index 00000000..ea0ca9c8 --- /dev/null +++ b/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts @@ -0,0 +1,98 @@ +import { decodeZip } from "@php-wasm/stream-compression" +import { isWordPressRuntimeFile } from "./wordpress-runtime-corpus.js" + +export const WORDPRESS_RUNTIME_ARTIFACT_SCHEMA = "wp-codebox/wordpress-runtime-artifact/v1" +export const WORDPRESS_RUNTIME_MAX_FILES = 2_000 +export const WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES = 32 * 1024 * 1024 +export const WORDPRESS_RUNTIME_MAX_ARCHIVE_BYTES = 32 * 1024 * 1024 +export const WORDPRESS_RUNTIME_MAX_FILE_BYTES = 8 * 1024 * 1024 + +export interface WordPressRuntimeArtifactManifest { + schema: typeof WORDPRESS_RUNTIME_ARTIFACT_SCHEMA + key: string + archive: { sha256: string; size: number } + source: { url: string; version?: string; identity?: string } + files: Array<{ path: string; size: number; sha256: string }> +} + +export interface RuntimeMemfs { + mkdir(path: string): void + writeFile(path: string, bytes: Uint8Array): void +} + +export function wordpressRuntimeArtifactKey(sha256: string): string { + if (!/^[a-f0-9]{64}$/.test(sha256)) throw new Error("WordPress runtime artifact hash must be a SHA-256 digest.") + return `runtime/wordpress/${sha256}.zip` +} + +export function validateWordPressRuntimeArtifactManifest(manifest: WordPressRuntimeArtifactManifest): void { + if (manifest.schema !== WORDPRESS_RUNTIME_ARTIFACT_SCHEMA) throw new Error("WordPress runtime artifact schema is invalid.") + if (manifest.key !== wordpressRuntimeArtifactKey(manifest.archive.sha256)) throw new Error("WordPress runtime artifact key is not content addressed.") + if (!Number.isSafeInteger(manifest.archive.size) || manifest.archive.size < 1 || manifest.archive.size > WORDPRESS_RUNTIME_MAX_ARCHIVE_BYTES) throw new Error("WordPress runtime artifact archive size is outside the allowed budget.") + if (!manifest.source.url.startsWith("https://")) throw new Error("WordPress runtime artifact source URL is invalid.") + if (!manifest.files.length || manifest.files.length > WORDPRESS_RUNTIME_MAX_FILES) throw new Error("WordPress runtime artifact file count is outside the allowed budget.") + + const paths = new Set(manifest.files.map((file) => file.path)) + let total = 0 + for (const file of manifest.files) { + if (!isSafeRuntimePath(file.path) || !isWordPressRuntimeFile(file.path, paths)) throw new Error(`WordPress runtime artifact contains an invalid file path: ${file.path}`) + if (!Number.isSafeInteger(file.size) || file.size < 0 || file.size > WORDPRESS_RUNTIME_MAX_FILE_BYTES || !/^[a-f0-9]{64}$/.test(file.sha256)) throw new Error(`WordPress runtime artifact has invalid metadata for ${file.path}.`) + total += file.size + } + if (total > WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES || paths.size !== manifest.files.length) throw new Error("WordPress runtime artifact file budget is invalid.") +} + +export async function materializeWordPressRuntimeArtifact(php: RuntimeMemfs, bucket: R2Bucket, manifest: WordPressRuntimeArtifactManifest): Promise<{ materializedFiles: number; materializedBytes: number }> { + validateWordPressRuntimeArtifactManifest(manifest) + const object = await bucket.get(manifest.key) + if (!object) throw new Error(`WordPress runtime artifact is unavailable: ${manifest.key}`) + if (object.size !== manifest.archive.size) throw new Error("WordPress runtime artifact size does not match its manifest.") + + const [hashBody, zipBody] = object.body.tee() + const archiveHash = sha256ReadableStream(hashBody) + const expected = new Map(manifest.files.map((file) => [file.path, file])) + let materializedFiles = 0 + let materializedBytes = 0 + for await (const entry of decodeZip(zipBody)) { + const file = expected.get(entry.name) + if (!file) throw new Error(`WordPress runtime artifact contains an unexpected file: ${entry.name}`) + const bytes = new Uint8Array(await entry.arrayBuffer()) + if (bytes.byteLength !== file.size || await sha256Hex(bytes) !== file.sha256) throw new Error(`WordPress runtime artifact file validation failed: ${entry.name}`) + materializedBytes += bytes.byteLength + materializedFiles++ + if (materializedFiles > WORDPRESS_RUNTIME_MAX_FILES || materializedBytes > WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES) throw new Error("WordPress runtime artifact exceeds its materialization budget.") + const destination = `/${entry.name}` + php.mkdir(destination.slice(0, destination.lastIndexOf("/"))) + php.writeFile(destination, bytes) + expected.delete(entry.name) + } + if (await archiveHash !== manifest.archive.sha256) throw new Error("WordPress runtime artifact archive hash does not match its manifest.") + if (expected.size) throw new Error("WordPress runtime artifact is missing manifest files.") + return { materializedFiles, materializedBytes } +} + +function isSafeRuntimePath(path: string): boolean { + return path.startsWith("wordpress/") && !path.includes("\\") && !path.split("/").some((segment) => !segment || segment === "." || segment === "..") +} + +async function sha256ReadableStream(stream: ReadableStream): Promise { + const chunks: Uint8Array[] = [] + let size = 0 + for await (const chunk of stream as AsyncIterable) { + size += chunk.byteLength + if (size > WORDPRESS_RUNTIME_MAX_ARCHIVE_BYTES) throw new Error("WordPress runtime artifact archive exceeds its size budget.") + chunks.push(chunk) + } + const bytes = new Uint8Array(size) + let offset = 0 + for (const chunk of chunks) { + bytes.set(chunk, offset) + offset += chunk.byteLength + } + return sha256Hex(bytes) +} + +async function sha256Hex(bytes: Uint8Array): Promise { + const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes).buffer) + return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("") +} diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 237ee123..50974013 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -10,6 +10,7 @@ import { routeWorkerRequest } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" +import { materializeWordPressRuntimeArtifact, type WordPressRuntimeArtifactManifest } from "./wordpress-runtime-artifact.js" import type { MarkdownPointer } from "./state-coordinator.js" export { WordPressStateCoordinator } from "./state-coordinator.js" import markdownDatabaseIntegrationRuntime from "../assets/markdown-database-integration-runtime.zip" @@ -17,9 +18,11 @@ import canonicalMarkdownSeed from "../assets/markdown-database-integration-canon import canonicalMarkdownSeedManifest from "../assets/markdown-database-integration-canonical-seed.json" with { type: "json" } import markdownPrimaryBootstrapIndex from "../assets/markdown-primary-bootstrap-index.sqlite" import wordpressInstallSeed from "../assets/wordpress-install-seed.sqlite" +import wordpressRuntimeArtifactManifest from "../assets/wordpress-runtime-artifact.json" with { type: "json" } const PHP_VERSION = "8.5.8" -const WORDPRESS_ARCHIVE_URL = "https://wordpress.org/latest.zip" +// Browser assets must come from the same immutable WordPress release as the server corpus. +const WORDPRESS_ARCHIVE_URL = (wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest).source.url const SQLITE_INTEGRATION_ARCHIVE_URL = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip" const MARKDOWN_DATABASE_INTEGRATION_REVISION = "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3" const SITE_URL = "https://wp-codebox-runtime.invalid" @@ -81,7 +84,7 @@ export default { const coordinator = env.WORDPRESS_STATE.getByName("default") if (route.kind === "probe") { if (route.phase === "canonical-session") return canonicalSessionProbe(env.WORDPRESS_STATE_BUCKET, await coordinatorCall<{ pointer: MarkdownPointer | null }>(coordinator, request.url, "state")) - return runBootProbe(route.phase) + return runBootProbe(route.phase, env.WORDPRESS_STATE_BUCKET) } if (route.kind === "r2-state") { if (request.method !== "GET") return new Response("WordPress state read requires GET.", { status: 405 }) @@ -262,13 +265,13 @@ async function discardRuntime(runtime: Runtime): Promise { } async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: string, authConstants: Record): Promise { - return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(bucket, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants), pointer } + return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(bucket, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants, bucket), pointer } } async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { if (!env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") const origin = new URL(requestUrl).origin - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env)) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env), env.WORDPRESS_STATE_BUCKET) try { const passwordFile = "/tmp/wordpress-admin-password" runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) @@ -460,7 +463,7 @@ async function packagedCanonicalMarkdownSeed(): Promise { return files.sort((left, right) => left.path.localeCompare(right.path)) } -async function runBootProbe(phase: string): Promise { +async function runBootProbe(phase: string, bucket: R2Bucket): Promise { if (phase === "wordpress-archive" || phase === "sqlite-archive") { const archive = phase === "wordpress-archive" ? await fetchArchive(WORDPRESS_ARCHIVE_URL, "wordpress.zip") @@ -501,7 +504,7 @@ async function runBootProbe(phase: string): Promise { if (phase === "streamed-files") { const php = new PHP(await createPhpRuntime()) try { - const evidence = await materializeWordPressServerFiles(php) + const evidence = await materializeWordPressServerFiles(php, bucket) const wordpressVersion = (await php.run({ code: " { if (phase === "mdi-files") { const php = new PHP(await createPhpRuntime()) try { - const wordpress = await materializeWordPressServerFiles(php) + const wordpress = await materializeWordPressServerFiles(php, bucket) await materializeMarkdownDatabaseIntegration(php) materializeRuntimeFiles(php, MARKDOWN_ROOT, initialMarkdownFiles()) const evidence = (await php.run({ @@ -525,7 +528,7 @@ async function runBootProbe(phase: string): Promise { } if (phase === "mdi-shortinit") { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex)) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: " $wp_version, 'markdownDropin' => defined('MARKDOWN_DB_DROPIN'), 'markdownMode' => defined('MARKDOWN_DB_MODE') ? MARKDOWN_DB_MODE : '']);", @@ -537,7 +540,7 @@ async function runBootProbe(phase: string): Promise { } if (phase === "mdi-wordpress" || phase === "mdi-option" || phase === "mdi-insert") { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex)) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const operation = phase === "mdi-option" ? "$updated = update_option('wp_codebox_mdi_probe', 1); $result = ['updated' => $updated];" @@ -554,7 +557,7 @@ async function runBootProbe(phase: string): Promise { } if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded"].includes(phase)) { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex)) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() return probeResponse(phase, JSON.parse(evidence) as Record) @@ -569,6 +572,11 @@ async function runBootProbe(phase: string): Promise { true, true, new Uint8Array(wordpressInstallSeed), + undefined, + undefined, + SITE_URL, + {}, + bucket, ) try { const wordpress = (await runtime.php.run({ @@ -594,6 +602,12 @@ async function runBootProbe(phase: string): Promise { phase === "full" || phase === "streamed-wordpress" ? "install-from-existing-files" : "do-not-attempt-installing", phase !== "wordpress-files", phase === "streamed-sqlite" || phase === "streamed-wordpress", + undefined, + undefined, + undefined, + SITE_URL, + {}, + bucket, ) try { const phpVersion = (await runtime.php.run({ code: "> = {}, + runtimeBucket?: R2Bucket, ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, @@ -680,7 +695,7 @@ async function bootWordPressRuntime( cookieStore: false, hooks: streamWordPressFiles || databaseSeed || markdownFiles ? { beforeWordPressFiles: streamWordPressFiles || markdownFiles ? async (php: PHP) => { - if (streamWordPressFiles) await materializeWordPressServerFiles(php) + if (streamWordPressFiles) await materializeWordPressServerFiles(php, runtimeBucket) if (markdownFiles) { await materializeMarkdownDatabaseIntegration(php) materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) @@ -762,27 +777,9 @@ async function materializeMarkdownDatabaseIntegration(php: PHP): Promise { } } -async function materializeWordPressServerFiles(php: PHP): Promise<{ materializedFiles: number; materializedBytes: number }> { - const decoder = new TextDecoder() - let materializedFiles = 0 - let materializedBytes = 0 - const archivePaths = await wordPressArchivePaths(decoder) - const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry: { path: Uint8Array }) => isWordPressRuntimeFile(decoder.decode(entry.path), archivePaths)) - const reader = stream.getReader() - while (true) { - const { done, value: entry } = await reader.read() - if (done) break - const path = entry instanceof File ? entry.name : decoder.decode(entry.path) - if (!isWordPressRuntimeFile(path, archivePaths)) continue - - const destination = `/${path}` - const bytes = entry instanceof File ? new Uint8Array(await entry.arrayBuffer()) : entry.bytes - php.mkdir(destination.slice(0, destination.lastIndexOf("/"))) - php.writeFile(destination, bytes) - materializedFiles++ - materializedBytes += bytes.byteLength - } - return { materializedFiles, materializedBytes } +async function materializeWordPressServerFiles(php: PHP, bucket: R2Bucket | undefined): Promise<{ materializedFiles: number; materializedBytes: number }> { + if (!bucket) throw new Error("WordPress runtime corpus artifact requires WORDPRESS_STATE_BUCKET.") + return materializeWordPressRuntimeArtifact(php, bucket, wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest) } async function serveWordPressStaticAsset(request: Request): Promise { @@ -825,18 +822,6 @@ async function serveWordPressStaticAsset(request: Request): Promise> { - const paths = new Set() - // decodeRemoteZip filters central-directory entries before requesting file ranges. - const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry: { path: Uint8Array }) => { - paths.add(decoder.decode(entry.path)) - return false - }) - for await (const _ of stream) { - // Draining the metadata-only stream completes central-directory parsing. - } - return paths -} function createPhpRuntime() { return loadPHPRuntime( diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index ab8abcec..3d8239c7 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -13,6 +13,8 @@ let child let output = "" try { + await run("npm", ["run", "generate:cloudflare-wordpress-runtime-corpus"]) + await run("npm", ["run", "provision:cloudflare-wordpress-runtime-corpus", "--", "--local", "--persist-to", stateDirectory]) await startWorker() await assertConcurrentMutations() const adminHtml = await login() @@ -43,6 +45,14 @@ try { await rm(stateDirectory, { recursive: true, force: true }) } +async function run(command, args) { + await new Promise((resolve, reject) => { + const childProcess = spawn(command, args, { cwd: process.cwd(), stdio: "inherit" }) + childProcess.on("error", reject) + childProcess.on("exit", (code) => code === 0 ? resolve(undefined) : reject(new Error(`${command} ${args.join(" ")} failed with status ${code}.`))) + }) +} + async function startWorker() { output = "" child = spawn("npm", ["exec", "--", "wrangler", "dev", "--config", "packages/runtime-cloudflare/wrangler.jsonc", "--port", String(port), "--persist-to", stateDirectory, "--var", `WORDPRESS_ADMIN_PASSWORD:${password}`, "--var", `WORDPRESS_AUTH_SECRET:${authSecret}`], { diff --git a/scripts/generate-cloudflare-wordpress-runtime-corpus.ts b/scripts/generate-cloudflare-wordpress-runtime-corpus.ts new file mode 100644 index 00000000..45a8f897 --- /dev/null +++ b/scripts/generate-cloudflare-wordpress-runtime-corpus.ts @@ -0,0 +1,48 @@ +import { createHash } from "node:crypto" +import { mkdir, writeFile } from "node:fs/promises" +import { dirname, resolve } from "node:path" +import { decodeRemoteZip, encodeZip } from "@php-wasm/stream-compression" +import { isWordPressRuntimeFile } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" +import { WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, wordpressRuntimeArtifactKey, type WordPressRuntimeArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" + +const sourceUrl = process.env.WORDPRESS_RUNTIME_ARCHIVE_URL ?? "https://downloads.wordpress.org/release/wordpress-7.0.2.zip" +const sourceVersion = process.env.WORDPRESS_RUNTIME_VERSION ?? "7.0.2" +const output = resolve(process.env.WORDPRESS_RUNTIME_ARTIFACT_OUTPUT ?? "artifacts/cloudflare-wordpress-runtime-corpus.zip") +const manifestOutput = resolve("packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json") +const decoder = new TextDecoder() +const archivePaths = new Set() +const selected: File[] = [] + +const response = await fetch(sourceUrl, { method: "HEAD" }) +if (!response.ok) throw new Error(`Unable to inspect WordPress archive: ${response.status}.`) +const identity = response.headers.get("etag") ?? response.headers.get("last-modified") ?? undefined +const stream = await decodeRemoteZip(sourceUrl, (entry) => { + const path = decoder.decode(entry.path) + archivePaths.add(path) + return isWordPressRuntimeFile(path, archivePaths) +}) +for await (const entry of stream) { + const path = entry instanceof File ? entry.name : decoder.decode(entry.path) + if (!isWordPressRuntimeFile(path, archivePaths)) continue + const bytes = entry instanceof File ? new Uint8Array(await entry.arrayBuffer()) : entry.bytes + selected.push(new File([bytes], path, { lastModified: 0 })) +} +selected.sort((left, right) => left.name.localeCompare(right.name)) +if (!selected.length) throw new Error("WordPress archive did not yield a runtime corpus.") +const archive = new Uint8Array(await new Response(encodeZip(selected)).arrayBuffer()) +const archiveSha256 = sha256Hex(archive) +const manifest: WordPressRuntimeArtifactManifest = { + schema: WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, + key: wordpressRuntimeArtifactKey(archiveSha256), + archive: { sha256: archiveSha256, size: archive.byteLength }, + source: { url: sourceUrl, version: sourceVersion, ...(identity ? { identity } : {}) }, + files: await Promise.all(selected.map(async (file) => ({ path: file.name, size: file.size, sha256: sha256Hex(new Uint8Array(await file.arrayBuffer())) }))), +} +await mkdir(dirname(output), { recursive: true }) +await writeFile(output, archive) +await writeFile(manifestOutput, `${JSON.stringify(manifest, null, 2)}\n`) +console.log(JSON.stringify({ key: manifest.key, archiveBytes: archive.byteLength, files: manifest.files.length, sha256: archiveSha256, source: manifest.source })) + +function sha256Hex(bytes: Uint8Array): string { + return createHash("sha256").update(bytes).digest("hex") +} diff --git a/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs b/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs new file mode 100644 index 00000000..55b266ab --- /dev/null +++ b/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs @@ -0,0 +1,24 @@ +import { createHash } from "node:crypto" +import { readFile } from "node:fs/promises" +import { spawn } from "node:child_process" + +const args = process.argv.slice(2) +const local = args.includes("--local") +const remote = args.includes("--remote") +const persistIndex = args.indexOf("--persist-to") +const persistTo = persistIndex === -1 ? undefined : args[persistIndex + 1] +if (local === remote || (local && !persistTo)) throw new Error("Use exactly one of --local --persist-to or --remote.") + +const manifest = JSON.parse(await readFile("packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json", "utf8")) +const archive = await readFile("artifacts/cloudflare-wordpress-runtime-corpus.zip") +const actual = createHash("sha256").update(archive).digest("hex") +if (actual !== manifest.archive.sha256 || archive.byteLength !== manifest.archive.size || manifest.key !== `runtime/wordpress/${actual}.zip`) throw new Error("Local WordPress runtime artifact does not match its content-addressed manifest.") + +const command = ["exec", "--", "wrangler", "r2", "object", "put", `wp-codebox-runtime-chubes/${manifest.key}`, "--file", "artifacts/cloudflare-wordpress-runtime-corpus.zip", local ? "--local" : "--remote"] +if (persistTo) command.push("--persist-to", persistTo) +await new Promise((resolve, reject) => { + const child = spawn("npm", command, { cwd: process.cwd(), stdio: "inherit" }) + child.on("error", reject) + child.on("exit", (code) => code === 0 ? resolve(undefined) : reject(new Error(`Wrangler R2 provisioning failed with status ${code}.`))) +}) +console.log(`Provisioned ${manifest.key} (${manifest.archive.size} bytes).`) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 69233904..b6548944 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -4,13 +4,14 @@ import { createHash } from "node:crypto" import { execFile } from "node:child_process" import { promisify } from "node:util" import test from "node:test" -import { decodeZip } from "@php-wasm/stream-compression" +import { decodeZip, encodeZip } from "@php-wasm/stream-compression" import { RUNTIME_COMMAND_RESULT_SCHEMA } from "../packages/runtime-core/src/runtime-contracts.js" import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "../packages/runtime-cloudflare/src/health-envelope.js" import { routeWorkerRequest } from "../packages/runtime-cloudflare/src/request-routing.js" import { toFetchResponse, toPHPRequest } from "../packages/runtime-cloudflare/src/request-translation.js" import { WordPressStateCoordinator } from "../packages/runtime-cloudflare/src/state-coordinator.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" +import { materializeWordPressRuntimeArtifact, WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, wordpressRuntimeArtifactKey, type WordPressRuntimeArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" const execFileAsync = promisify(execFile) @@ -157,7 +158,7 @@ test("Cloudflare runtime pins and bundles the public constrained MDI runtime", a test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object only for leases", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const coordinator = await readFile(new URL("../packages/runtime-cloudflare/src/state-coordinator.ts", import.meta.url), "utf8") - const materializer = worker.slice(worker.indexOf("async function materializeWordPressServerFiles"), worker.indexOf("function createPhpRuntime")) + const materializer = worker.slice(worker.indexOf("async function materializeWordPressServerFiles"), worker.indexOf("async function serveWordPressStaticAsset")) const corpus = await readFile(new URL("../packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts", import.meta.url), "utf8") assert.match(worker, /return runCoordinatedWordPressRequest\(request, env, coordinator, route\.kind\)/) @@ -199,15 +200,64 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /"x-wp-codebox-static": "wordpress-archive"/) assert.match(worker, /cache\.match\(request\)/) assert.match(worker, /cache\.put\(request, response\.clone\(\)\)/) - assert.match(materializer, /const archivePaths = await wordPressArchivePaths\(decoder\)/) - assert.match(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL,\s*\(entry: \{ path: Uint8Array \}\) => isWordPressRuntimeFile\(decoder\.decode\(entry\.path\), archivePaths\)\)/) - assert.match(materializer, /return false/) + assert.match(materializer, /materializeWordPressRuntimeArtifact\(php, bucket, wordpressRuntimeArtifactManifest/) + assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL/) assert.match(corpus, /path\.endsWith\("\.map"\)/) assert.match(corpus, /SERVER_READ_EXTENSION/) assert.match(corpus, /path\.endsWith\("\/style\.css"\)/) assert.match(corpus, /STATIC_ARCHIVE_ROOTS/) }) +test("WordPress runtime artifacts are content-addressed and reject unavailable or corrupt R2 objects", async () => { + const source = new TextEncoder().encode(" createHash("sha256").update(bytes).digest("hex") + const manifest: WordPressRuntimeArtifactManifest = { + schema: WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, + key: wordpressRuntimeArtifactKey(sha256(archive)), + archive: { sha256: sha256(archive), size: archive.byteLength }, + source: { url: "https://downloads.wordpress.org/release/wordpress-6.8.1.zip", version: "6.8.1" }, + files: [{ path: "wordpress/wp-includes/version.php", size: source.byteLength, sha256: sha256(source) }], + } + const writes = new Map() + const php = { mkdir: () => {}, writeFile: (path: string, bytes: Uint8Array) => writes.set(path, bytes) } + const bucket = (bytes: Uint8Array | null) => ({ get: async () => bytes === null ? null : { size: bytes.byteLength, body: new Blob([bytes]).stream() } }) + + await assert.rejects(materializeWordPressRuntimeArtifact(php, bucket(null) as never, manifest), /unavailable/) + await assert.rejects(materializeWordPressRuntimeArtifact(php, bucket(new Uint8Array([...archive, 0])) as never, manifest), /size does not match/) + const corrupted = archive.slice() + corrupted[100] ^= 1 + await assert.rejects(materializeWordPressRuntimeArtifact(php, bucket(corrupted) as never, manifest), /validation failed|archive hash/) + const evidence = await materializeWordPressRuntimeArtifact(php, bucket(archive) as never, manifest) + assert.deepEqual(evidence, { materializedFiles: 1, materializedBytes: source.byteLength }) + assert.deepEqual(writes.get("/wordpress/wp-includes/version.php"), source) +}) + +test("WordPress runtime artifact validation rejects path traversal and invalid budgets", async () => { + const manifest: WordPressRuntimeArtifactManifest = { + schema: WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, + key: wordpressRuntimeArtifactKey("a".repeat(64)), + archive: { sha256: "a".repeat(64), size: 1 }, + source: { url: "https://downloads.wordpress.org/release/wordpress-6.8.1.zip" }, + files: [{ path: "wordpress/../wp-includes/version.php", size: 9 * 1024 * 1024, sha256: "b".repeat(64) }], + } + const php = { mkdir: () => {}, writeFile: () => {} } + await assert.rejects(materializeWordPressRuntimeArtifact(php, { get: async () => null } as never, manifest), /invalid file path/) +}) + +test("WordPress runtime corpus generator keeps the ZIP outside the Worker bundle", async () => { + const generator = await readFile(new URL("../scripts/generate-cloudflare-wordpress-runtime-corpus.ts", import.meta.url), "utf8") + const artifact = await readFile(new URL("../packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts", import.meta.url), "utf8") + const manifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json", import.meta.url), "utf8")) as WordPressRuntimeArtifactManifest + assert.match(generator, /decodeRemoteZip\(sourceUrl/) + assert.match(generator, /encodeZip\(selected\)/) + assert.match(generator, /lastModified: 0/) + assert.match(generator, /artifacts\/cloudflare-wordpress-runtime-corpus\.zip/) + assert.match(artifact, /decodeZip\(zipBody\)/) + assert.equal(manifest.key, wordpressRuntimeArtifactKey(manifest.archive.sha256)) + assert.ok(manifest.files.length > 0) +}) + test("Cloudflare coordinator serializes leases, promotes with CAS, and recovers stale leases", async () => { const values = new Map() const objects = new Map() From 962acac084377255b2393777dfb60c6461f3cee0 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 02:04:35 -0400 Subject: [PATCH 07/87] Buffer the bounded R2 runtime artifact --- .../src/wordpress-runtime-artifact.ts | 30 +++++-------------- tests/cloudflare-runtime.test.ts | 22 ++++++++++---- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts b/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts index ea0ca9c8..0a2279a8 100644 --- a/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts +++ b/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts @@ -3,8 +3,8 @@ import { isWordPressRuntimeFile } from "./wordpress-runtime-corpus.js" export const WORDPRESS_RUNTIME_ARTIFACT_SCHEMA = "wp-codebox/wordpress-runtime-artifact/v1" export const WORDPRESS_RUNTIME_MAX_FILES = 2_000 -export const WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES = 32 * 1024 * 1024 -export const WORDPRESS_RUNTIME_MAX_ARCHIVE_BYTES = 32 * 1024 * 1024 +export const WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES = 24 * 1024 * 1024 +export const WORDPRESS_RUNTIME_MAX_ARCHIVE_BYTES = 8 * 1024 * 1024 export const WORDPRESS_RUNTIME_MAX_FILE_BYTES = 8 * 1024 * 1024 export interface WordPressRuntimeArtifactManifest { @@ -46,14 +46,16 @@ export async function materializeWordPressRuntimeArtifact(php: RuntimeMemfs, buc validateWordPressRuntimeArtifactManifest(manifest) const object = await bucket.get(manifest.key) if (!object) throw new Error(`WordPress runtime artifact is unavailable: ${manifest.key}`) + if (object.size > WORDPRESS_RUNTIME_MAX_ARCHIVE_BYTES) throw new Error("WordPress runtime artifact archive exceeds its size budget.") if (object.size !== manifest.archive.size) throw new Error("WordPress runtime artifact size does not match its manifest.") - const [hashBody, zipBody] = object.body.tee() - const archiveHash = sha256ReadableStream(hashBody) + // The archive and expanded corpus caps leave most of the 128 MiB isolate for PHP-WASM and runtime overhead. + const archiveBytes = new Uint8Array(await object.arrayBuffer()) + if (await sha256Hex(archiveBytes) !== manifest.archive.sha256) throw new Error("WordPress runtime artifact archive hash does not match its manifest.") const expected = new Map(manifest.files.map((file) => [file.path, file])) let materializedFiles = 0 let materializedBytes = 0 - for await (const entry of decodeZip(zipBody)) { + for await (const entry of decodeZip(new Blob([archiveBytes]).stream())) { const file = expected.get(entry.name) if (!file) throw new Error(`WordPress runtime artifact contains an unexpected file: ${entry.name}`) const bytes = new Uint8Array(await entry.arrayBuffer()) @@ -66,7 +68,6 @@ export async function materializeWordPressRuntimeArtifact(php: RuntimeMemfs, buc php.writeFile(destination, bytes) expected.delete(entry.name) } - if (await archiveHash !== manifest.archive.sha256) throw new Error("WordPress runtime artifact archive hash does not match its manifest.") if (expected.size) throw new Error("WordPress runtime artifact is missing manifest files.") return { materializedFiles, materializedBytes } } @@ -75,23 +76,6 @@ function isSafeRuntimePath(path: string): boolean { return path.startsWith("wordpress/") && !path.includes("\\") && !path.split("/").some((segment) => !segment || segment === "." || segment === "..") } -async function sha256ReadableStream(stream: ReadableStream): Promise { - const chunks: Uint8Array[] = [] - let size = 0 - for await (const chunk of stream as AsyncIterable) { - size += chunk.byteLength - if (size > WORDPRESS_RUNTIME_MAX_ARCHIVE_BYTES) throw new Error("WordPress runtime artifact archive exceeds its size budget.") - chunks.push(chunk) - } - const bytes = new Uint8Array(size) - let offset = 0 - for (const chunk of chunks) { - bytes.set(chunk, offset) - offset += chunk.byteLength - } - return sha256Hex(bytes) -} - async function sha256Hex(bytes: Uint8Array): Promise { const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes).buffer) return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("") diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index b6548944..2644c3b5 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -221,14 +221,26 @@ test("WordPress runtime artifacts are content-addressed and reject unavailable o } const writes = new Map() const php = { mkdir: () => {}, writeFile: (path: string, bytes: Uint8Array) => writes.set(path, bytes) } - const bucket = (bytes: Uint8Array | null) => ({ get: async () => bytes === null ? null : { size: bytes.byteLength, body: new Blob([bytes]).stream() } }) + let arrayBufferReads = 0 + const bucket = (bytes: Uint8Array | null) => ({ get: async () => bytes === null ? null : { + size: bytes.byteLength, + // R2 supplies both helpers. Reading body would fail this production-shaped mock. + body: new ReadableStream({ pull: (controller) => controller.error(new Error("R2 body stream should not be read directly.")) }), + arrayBuffer: async () => { + arrayBufferReads++ + return new Blob([bytes]).arrayBuffer() + }, + } }) await assert.rejects(materializeWordPressRuntimeArtifact(php, bucket(null) as never, manifest), /unavailable/) await assert.rejects(materializeWordPressRuntimeArtifact(php, bucket(new Uint8Array([...archive, 0])) as never, manifest), /size does not match/) - const corrupted = archive.slice() - corrupted[100] ^= 1 - await assert.rejects(materializeWordPressRuntimeArtifact(php, bucket(corrupted) as never, manifest), /validation failed|archive hash/) + const oversized = { get: async () => ({ size: 33 * 1024 * 1024, body: new ReadableStream(), arrayBuffer: async () => { throw new Error("oversized archive must not be read") } }) } + await assert.rejects(materializeWordPressRuntimeArtifact(php, oversized as never, manifest), /exceeds its size budget/) + const hashCorruptManifest = { ...manifest, key: wordpressRuntimeArtifactKey("a".repeat(64)), archive: { ...manifest.archive, sha256: "a".repeat(64) } } + await assert.rejects(materializeWordPressRuntimeArtifact(php, bucket(archive) as never, hashCorruptManifest), /archive hash does not match/) + assert.equal(writes.size, 0, "hash-corrupt archives fail before extraction") const evidence = await materializeWordPressRuntimeArtifact(php, bucket(archive) as never, manifest) + assert.equal(arrayBufferReads, 2, "valid and hash-corrupt archives use R2 arrayBuffer()") assert.deepEqual(evidence, { materializedFiles: 1, materializedBytes: source.byteLength }) assert.deepEqual(writes.get("/wordpress/wp-includes/version.php"), source) }) @@ -253,7 +265,7 @@ test("WordPress runtime corpus generator keeps the ZIP outside the Worker bundle assert.match(generator, /encodeZip\(selected\)/) assert.match(generator, /lastModified: 0/) assert.match(generator, /artifacts\/cloudflare-wordpress-runtime-corpus\.zip/) - assert.match(artifact, /decodeZip\(zipBody\)/) + assert.match(artifact, /decodeZip\(new Blob\(\[archiveBytes\]\)\.stream\(\)\)/) assert.equal(manifest.key, wordpressRuntimeArtifactKey(manifest.archive.sha256)) assert.ok(manifest.files.length > 0) }) From fac127a313f116742416cc30c09da8a1792e8337 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 02:18:38 -0400 Subject: [PATCH 08/87] Extract the runtime corpus inside PHP --- .../src/wordpress-runtime-artifact.ts | 92 +++++++++++++++---- tests/cloudflare-runtime.test.ts | 35 ++++++- 2 files changed, 103 insertions(+), 24 deletions(-) diff --git a/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts b/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts index 0a2279a8..b3887c59 100644 --- a/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts +++ b/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts @@ -1,4 +1,3 @@ -import { decodeZip } from "@php-wasm/stream-compression" import { isWordPressRuntimeFile } from "./wordpress-runtime-corpus.js" export const WORDPRESS_RUNTIME_ARTIFACT_SCHEMA = "wp-codebox/wordpress-runtime-artifact/v1" @@ -16,10 +15,18 @@ export interface WordPressRuntimeArtifactManifest { } export interface RuntimeMemfs { - mkdir(path: string): void writeFile(path: string, bytes: Uint8Array): void + run(request: { code: string }): Promise<{ text: string }> } +const WORDPRESS_RUNTIME_ARCHIVE_TEMP_PATH = "/tmp/wp-codebox-wordpress-runtime.zip" +const REQUIRED_WORDPRESS_RUNTIME_FILES = [ + "wordpress/index.php", + "wordpress/wp-load.php", + "wordpress/wp-includes/version.php", + "wordpress/wp-settings.php", +] + export function wordpressRuntimeArtifactKey(sha256: string): string { if (!/^[a-f0-9]{64}$/.test(sha256)) throw new Error("WordPress runtime artifact hash must be a SHA-256 digest.") return `runtime/wordpress/${sha256}.zip` @@ -52,24 +59,71 @@ export async function materializeWordPressRuntimeArtifact(php: RuntimeMemfs, buc // The archive and expanded corpus caps leave most of the 128 MiB isolate for PHP-WASM and runtime overhead. const archiveBytes = new Uint8Array(await object.arrayBuffer()) if (await sha256Hex(archiveBytes) !== manifest.archive.sha256) throw new Error("WordPress runtime artifact archive hash does not match its manifest.") - const expected = new Map(manifest.files.map((file) => [file.path, file])) - let materializedFiles = 0 - let materializedBytes = 0 - for await (const entry of decodeZip(new Blob([archiveBytes]).stream())) { - const file = expected.get(entry.name) - if (!file) throw new Error(`WordPress runtime artifact contains an unexpected file: ${entry.name}`) - const bytes = new Uint8Array(await entry.arrayBuffer()) - if (bytes.byteLength !== file.size || await sha256Hex(bytes) !== file.sha256) throw new Error(`WordPress runtime artifact file validation failed: ${entry.name}`) - materializedBytes += bytes.byteLength - materializedFiles++ - if (materializedFiles > WORDPRESS_RUNTIME_MAX_FILES || materializedBytes > WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES) throw new Error("WordPress runtime artifact exceeds its materialization budget.") - const destination = `/${entry.name}` - php.mkdir(destination.slice(0, destination.lastIndexOf("/"))) - php.writeFile(destination, bytes) - expected.delete(entry.name) + + // The archive digest binds these bytes to the already path- and budget-checked manifest. + // Crossing into PHP once avoids per-file JS/WASM calls during cold boot. + php.writeFile(WORDPRESS_RUNTIME_ARCHIVE_TEMP_PATH, archiveBytes) + const output = (await php.run({ code: zipMaterializationCode(manifest) })).text.trim() + let evidence: unknown + try { + evidence = JSON.parse(output) + } catch { + throw new Error(`WordPress runtime artifact extraction did not return valid evidence: ${output}`) } - if (expected.size) throw new Error("WordPress runtime artifact is missing manifest files.") - return { materializedFiles, materializedBytes } + if (!isMaterializationEvidence(evidence, manifest)) throw new Error("WordPress runtime artifact extraction returned invalid evidence.") + return evidence +} + +function zipMaterializationCode(manifest: WordPressRuntimeArtifactManifest): string { + const expected = Object.fromEntries(manifest.files.map((file) => [file.path, file.size])) + const expectedJson = JSON.stringify(expected).replace(/open($archive_path); + if (true !== $opened) { + throw new RuntimeException('WordPress runtime artifact ZIP could not be opened (ZipArchive status ' . $opened . ').'); + } + try { + if ($zip->numFiles !== count($expected)) { + throw new RuntimeException('WordPress runtime artifact ZIP file count does not match its manifest.'); + } + for ($index = 0; $index < $zip->numFiles; $index++) { + $name = $zip->getNameIndex($index); + $stat = $zip->statIndex($index); + if (!is_string($name) || !array_key_exists($name, $expected) || !is_array($stat) || !isset($stat['size']) || (int) $stat['size'] !== $expected[$name]) { + throw new RuntimeException('WordPress runtime artifact ZIP entries do not match its manifest.'); + } + } + if (!$zip->extractTo('/', array_keys($expected))) { + throw new RuntimeException('WordPress runtime artifact ZIP extraction failed.'); + } + foreach ($required as $path) { + if (!is_file('/' . $path)) { + throw new RuntimeException('WordPress runtime artifact is missing required file after extraction: ' . $path); + } + } + echo json_encode(array('materializedFiles' => count($expected), 'materializedBytes' => array_sum($expected)), JSON_THROW_ON_ERROR); + } finally { + $zip->close(); + } +} finally { + @unlink($archive_path); +}` +} + +function isMaterializationEvidence(value: unknown, manifest: WordPressRuntimeArtifactManifest): value is { materializedFiles: number; materializedBytes: number } { + if (!value || typeof value !== "object") return false + const evidence = value as Record + return evidence.materializedFiles === manifest.files.length + && evidence.materializedBytes === manifest.files.reduce((total, file) => total + file.size, 0) } function isSafeRuntimePath(path: string): boolean { diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 2644c3b5..b66c6fc3 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -220,7 +220,14 @@ test("WordPress runtime artifacts are content-addressed and reject unavailable o files: [{ path: "wordpress/wp-includes/version.php", size: source.byteLength, sha256: sha256(source) }], } const writes = new Map() - const php = { mkdir: () => {}, writeFile: (path: string, bytes: Uint8Array) => writes.set(path, bytes) } + const phpRuns: string[] = [] + const php = { + writeFile: (path: string, bytes: Uint8Array) => writes.set(path, bytes), + run: async ({ code }: { code: string }) => { + phpRuns.push(code) + return { text: JSON.stringify({ materializedFiles: 1, materializedBytes: source.byteLength }) } + }, + } let arrayBufferReads = 0 const bucket = (bytes: Uint8Array | null) => ({ get: async () => bytes === null ? null : { size: bytes.byteLength, @@ -239,10 +246,25 @@ test("WordPress runtime artifacts are content-addressed and reject unavailable o const hashCorruptManifest = { ...manifest, key: wordpressRuntimeArtifactKey("a".repeat(64)), archive: { ...manifest.archive, sha256: "a".repeat(64) } } await assert.rejects(materializeWordPressRuntimeArtifact(php, bucket(archive) as never, hashCorruptManifest), /archive hash does not match/) assert.equal(writes.size, 0, "hash-corrupt archives fail before extraction") + const malformed = new Uint8Array([0, 1, 2, 3]) + const malformedManifest = { ...manifest, key: wordpressRuntimeArtifactKey(sha256(malformed)), archive: { sha256: sha256(malformed), size: malformed.byteLength } } + const malformedWrites: Uint8Array[] = [] + await assert.rejects(materializeWordPressRuntimeArtifact({ + writeFile: (_path, bytes) => malformedWrites.push(bytes), + run: async () => { throw new Error("WordPress runtime artifact ZIP could not be opened (ZipArchive status 19).") }, + }, bucket(malformed) as never, malformedManifest), /ZIP could not be opened/) + assert.equal(malformedWrites.length, 1, "a hash-valid malformed archive is rejected by PHP after its single archive write") const evidence = await materializeWordPressRuntimeArtifact(php, bucket(archive) as never, manifest) - assert.equal(arrayBufferReads, 2, "valid and hash-corrupt archives use R2 arrayBuffer()") + assert.equal(arrayBufferReads, 3, "valid, hash-corrupt, and malformed archives use R2 arrayBuffer()") assert.deepEqual(evidence, { materializedFiles: 1, materializedBytes: source.byteLength }) - assert.deepEqual(writes.get("/wordpress/wp-includes/version.php"), source) + assert.equal(writes.size, 1, "the verified archive crosses the JS/WASM boundary once") + assert.deepEqual(writes.get("/tmp/wp-codebox-wordpress-runtime.zip"), archive) + assert.equal(phpRuns.length, 1) + assert.match(phpRuns[0], /extension_loaded\('zip'\)/) + assert.match(phpRuns[0], /\$zip->numFiles !== count\(\$expected\)/) + assert.match(phpRuns[0], /\$zip->extractTo\('\/', array_keys\(\$expected\)\)/) + assert.match(phpRuns[0], /missing required file after extraction/) + assert.match(phpRuns[0], /finally \{\n @unlink\(\$archive_path\)/) }) test("WordPress runtime artifact validation rejects path traversal and invalid budgets", async () => { @@ -253,8 +275,10 @@ test("WordPress runtime artifact validation rejects path traversal and invalid b source: { url: "https://downloads.wordpress.org/release/wordpress-6.8.1.zip" }, files: [{ path: "wordpress/../wp-includes/version.php", size: 9 * 1024 * 1024, sha256: "b".repeat(64) }], } - const php = { mkdir: () => {}, writeFile: () => {} } + let writes = 0 + const php = { writeFile: () => { writes++ }, run: async () => ({ text: "" }) } await assert.rejects(materializeWordPressRuntimeArtifact(php, { get: async () => null } as never, manifest), /invalid file path/) + assert.equal(writes, 0, "unsafe manifests are rejected before any archive write") }) test("WordPress runtime corpus generator keeps the ZIP outside the Worker bundle", async () => { @@ -265,7 +289,8 @@ test("WordPress runtime corpus generator keeps the ZIP outside the Worker bundle assert.match(generator, /encodeZip\(selected\)/) assert.match(generator, /lastModified: 0/) assert.match(generator, /artifacts\/cloudflare-wordpress-runtime-corpus\.zip/) - assert.match(artifact, /decodeZip\(new Blob\(\[archiveBytes\]\)\.stream\(\)\)/) + assert.doesNotMatch(artifact, /decodeZip/) + assert.match(artifact, /php\.writeFile\(WORDPRESS_RUNTIME_ARCHIVE_TEMP_PATH, archiveBytes\)/) assert.equal(manifest.key, wordpressRuntimeArtifactKey(manifest.archive.sha256)) assert.ok(manifest.files.length > 0) }) From bea4645f66ecde2090c869d055a00d096f3f6392 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 02:37:28 -0400 Subject: [PATCH 09/87] Add bounded MDI init diagnostics --- packages/runtime-cloudflare/README.md | 4 + packages/runtime-cloudflare/src/worker.ts | 112 +++++++++++++++++++++- tests/cloudflare-runtime.test.ts | 29 ++++++ 3 files changed, 144 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 90696312..0cb9144c 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -18,6 +18,10 @@ The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache. +## MDI Init Diagnostics + +The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. These phases never accept callback names or removal rules from request input. + ## Verification 1. Run `npm run generate:cloudflare-canonical-mdi-seed` and `npm run generate:cloudflare-wordpress-runtime-corpus` to regenerate deterministic runtime artifacts and manifests. diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 50974013..dd243f58 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -556,7 +556,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded"].includes(phase)) { + if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() @@ -628,6 +628,116 @@ function wordpressProbeCode(phase: string): string { return " get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);" } + const initExclusions: Record = { + "mdi-init-exclude-scheduling": [ + "wp_schedule_update_checks", + "wp_schedule_delete_old_privacy_export_files", + "wp_cron", + "WP_Site_Health::schedule_cron", + "wp_schedule_site_health_cron", + "WP_Site_Health::maybe_create_scheduled_event", + ], + "mdi-init-exclude-block-registration": [ + "register_block_core_*", + "register_core_block_*", + "register_core_block_types_from_metadata", + "register_core_block_style_handles", + "wp_register_core_block_style_handles", + "WP_Block_Supports::init", + ], + "mdi-init-exclude-theme-patterns-styles": [ + "_register_theme_block_patterns", + "_register_theme_block_pattern_categories", + "_register_core_block_patterns_and_categories", + "wp_register_global_styles", + "wp_register_global_styles_custom_css", + "wp_register_typography_support", + ], + "mdi-init-exclude-widgets": ["wp_widgets_init"], + "mdi-init-exclude-rest-connectors-sitemaps": [ + "rest_api_init", + "wp_sitemaps_get_server", + "WP_Sitemaps::init", + "WP_Sitemaps_Registry::init", + ], + "mdi-init-exclude-initial-content-types": [ + "create_initial_taxonomies", + "create_initial_post_types", + "create_initial_post_statuses", + "wp_create_initial_post_meta", + ], + } + if (phase === "mdi-init-callbacks" || initExclusions[phase]) { + const excluded = initExclusions[phase] ?? [] + return `callbacks) || !is_array($hook->callbacks)) return $inventory; + foreach ($hook->callbacks as $priority => $callbacks) { + $identifiers = array(); + foreach ($callbacks as $registered) $identifiers[] = wp_codebox_init_probe_callback_identifier($registered['function']); + sort($identifiers, SORT_STRING); + $inventory[(string) $priority] = $identifiers; + } + ksort($inventory, SORT_NUMERIC); + return $inventory; +} +function wp_codebox_init_probe_matches($identifier, $patterns) { + foreach ($patterns as $pattern) { + if (str_ends_with($pattern, '*') && str_starts_with($identifier, substr($pattern, 0, -1))) return true; + if ($identifier === $pattern) return true; + } + return false; +} +$inventory = wp_codebox_init_probe_inventory(); +if ($phase === 'mdi-init-callbacks') { + echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'callbacks' => $inventory, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); + return; +} +$removed = array(); +$hook = isset($GLOBALS['wp_filter']['init']) ? $GLOBALS['wp_filter']['init'] : null; +if ($hook && isset($hook->callbacks) && is_array($hook->callbacks)) { + foreach ($hook->callbacks as $priority => $callbacks) { + foreach ($callbacks as $index => $registered) { + $identifier = wp_codebox_init_probe_callback_identifier($registered['function']); + if (wp_codebox_init_probe_matches($identifier, $excluded)) { + unset($hook->callbacks[$priority][$index]); + $removed[] = $identifier; + } + } + if (empty($hook->callbacks[$priority])) unset($hook->callbacks[$priority]); + } +} +sort($removed, SORT_STRING); +$memory_before = memory_get_usage(true); +do_action('init'); +echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'removedCallbacks' => $removed, 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); +return; +PHP; +file_put_contents($settings_path, str_replace($needle, $probe, $settings)); +require '/wordpress/wp-load.php';` + } + const stops: Record = { "seeded-includes": { needle: "add_action( 'after_setup_theme', array( wp_script_modules(), 'add_hooks' ) );" }, "seeded-embed": { needle: "/**\n * WordPress Textdomain Registry object." }, diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index b66c6fc3..ec954efc 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -155,6 +155,35 @@ test("Cloudflare runtime pins and bundles the public constrained MDI runtime", a ]) }) +test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusion groups", async () => { + const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + + for (const phase of [ + "mdi-init-callbacks", + "mdi-init-exclude-scheduling", + "mdi-init-exclude-block-registration", + "mdi-init-exclude-theme-patterns-styles", + "mdi-init-exclude-widgets", + "mdi-init-exclude-rest-connectors-sitemaps", + "mdi-init-exclude-initial-content-types", + ]) assert.match(worker, new RegExp(`"${phase}"`)) + + assert.match(worker, /\$needle = "do_action\( 'init' \);"/) + assert.match(worker, /substr_count\(\$settings, \$needle\) !== 1/) + assert.match(worker, /sort\(\$identifiers, SORT_STRING\)/) + assert.match(worker, /ksort\(\$inventory, SORT_NUMERIC\)/) + assert.match(worker, /"wp_schedule_update_checks"/) + assert.match(worker, /"WP_Site_Health::maybe_create_scheduled_event"/) + assert.match(worker, /"register_block_core_\*"/) + assert.match(worker, /"WP_Block_Supports::init"/) + assert.match(worker, /"wp_widgets_init"/) + assert.match(worker, /"rest_api_init"/) + assert.match(worker, /"create_initial_taxonomies"/) + assert.match(worker, /"wp_create_initial_post_meta"/) + assert.match(worker, /do_action\('init'\);/) + assert.doesNotMatch(worker, /searchParams\.get\([^)]*callback|searchParams\.get\([^)]*exclude/) +}) + test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object only for leases", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const coordinator = await readFile(new URL("../packages/runtime-cloudflare/src/state-coordinator.ts", import.meta.url), "utf8") From b9233edb0887de9f7f0d37df973be0984d880e0b Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 02:45:59 -0400 Subject: [PATCH 10/87] Split widget initialization diagnostics --- packages/runtime-cloudflare/README.md | 4 +- packages/runtime-cloudflare/src/worker.ts | 100 +++++++++++++++++++++- tests/cloudflare-runtime.test.ts | 12 ++- 3 files changed, 113 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 0cb9144c..00c1b0ff 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -20,7 +20,9 @@ The WordPress server corpus is a separate deployment artifact, never a Worker-mo ## MDI Init Diagnostics -The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. These phases never accept callback names or removal rules from request input. +The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. + +The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names or removal rules from request input. ## Verification diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index dd243f58..1c3f6914 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -556,7 +556,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types"].includes(phase)) { + if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() @@ -628,6 +628,104 @@ function wordpressProbeCode(phase: string): string { return " get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);" } + const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks"] + if (widgetPhases.includes(phase)) { + return `callbacks) || !is_array($hook->callbacks)) return $inventory; + foreach ($hook->callbacks as $priority => $callbacks) { + $identifiers = array(); + foreach ($callbacks as $registered) $identifiers[] = wp_codebox_widgets_probe_callback_identifier($registered['function']); + sort($identifiers, SORT_STRING); + $inventory[(string) $priority] = $identifiers; + } + ksort($inventory, SORT_NUMERIC); + return $inventory; +} +function wp_codebox_widgets_probe_remove_callbacks($hook_name, $identifiers, $retain) { + $removed = array(); + $hook = isset($GLOBALS['wp_filter'][$hook_name]) ? $GLOBALS['wp_filter'][$hook_name] : null; + if (!$hook || !isset($hook->callbacks) || !is_array($hook->callbacks)) return $removed; + foreach ($hook->callbacks as $priority => $callbacks) { + foreach ($callbacks as $index => $registered) { + $identifier = wp_codebox_widgets_probe_callback_identifier($registered['function']); + if (in_array($identifier, $identifiers, true) !== $retain) { + unset($hook->callbacks[$priority][$index]); + $removed[] = $identifier; + } + } + if (empty($hook->callbacks[$priority])) unset($hook->callbacks[$priority]); + } + sort($removed, SORT_STRING); + return $removed; +} +function wp_codebox_widgets_probe_register_defaults() { + if (!is_blog_installed()) return; + register_widget('WP_Widget_Pages'); + register_widget('WP_Widget_Calendar'); + register_widget('WP_Widget_Archives'); + if (get_option('link_manager_enabled')) register_widget('WP_Widget_Links'); + register_widget('WP_Widget_Media_Audio'); + register_widget('WP_Widget_Media_Image'); + register_widget('WP_Widget_Media_Gallery'); + register_widget('WP_Widget_Media_Video'); + register_widget('WP_Widget_Meta'); + register_widget('WP_Widget_Search'); + register_widget('WP_Widget_Text'); + register_widget('WP_Widget_Categories'); + register_widget('WP_Widget_Recent_Posts'); + register_widget('WP_Widget_Recent_Comments'); + register_widget('WP_Widget_RSS'); + register_widget('WP_Widget_Tag_Cloud'); + register_widget('WP_Nav_Menu_Widget'); + register_widget('WP_Widget_Custom_HTML'); + register_widget('WP_Widget_Block'); +} +if ($phase === 'mdi-widgets-callbacks') { + echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'callbacks' => wp_codebox_widgets_probe_inventory('widgets_init'), 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); + return; +} +$removed_init = wp_codebox_widgets_probe_remove_callbacks('init', array('wp_widgets_init'), false); +$memory_before = memory_get_usage(true); +wp_codebox_widgets_probe_register_defaults(); +$removed_widgets = array(); +if ($phase === 'mdi-widgets-hooks') { + do_action('widgets_init'); +} elseif ($phase === 'mdi-widgets-factory') { + $removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array('WP_Widget_Factory::_register_widgets'), true); + do_action('widgets_init'); +} elseif ($phase === 'mdi-widgets-remaining-hooks') { + $removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array('WP_Widget_Factory::_register_widgets'), false); + do_action('widgets_init'); +} +$widget_class_count = isset($GLOBALS['wp_widget_factory']->widgets) && is_array($GLOBALS['wp_widget_factory']->widgets) ? count($GLOBALS['wp_widget_factory']->widgets) : 0; +echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'removedInitCallbacks' => $removed_init, 'removedWidgetsCallbacks' => $removed_widgets, 'widgetClassCount' => $widget_class_count, 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); +return; +PHP; +file_put_contents($settings_path, str_replace($needle, $probe, $settings)); +require '/wordpress/wp-load.php';` + } + const initExclusions: Record = { "mdi-init-exclude-scheduling": [ "wp_schedule_update_checks", diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index ec954efc..040d12fa 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -166,6 +166,11 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", + "mdi-widgets-callbacks", + "mdi-widgets-constructors", + "mdi-widgets-hooks", + "mdi-widgets-factory", + "mdi-widgets-remaining-hooks", ]) assert.match(worker, new RegExp(`"${phase}"`)) assert.match(worker, /\$needle = "do_action\( 'init' \);"/) @@ -177,11 +182,16 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /"register_block_core_\*"/) assert.match(worker, /"WP_Block_Supports::init"/) assert.match(worker, /"wp_widgets_init"/) + assert.match(worker, /'WP_Widget_Factory::_register_widgets'/) + assert.match(worker, /function wp_codebox_widgets_probe_register_defaults\(\)/) + assert.match(worker, /if \(get_option\('link_manager_enabled'\)\) register_widget\('WP_Widget_Links'\);/) + assert.match(worker, /'widgetClassCount' => \$widget_class_count/) + assert.match(worker, /wp_codebox_widgets_probe_inventory\('widgets_init'\)/) assert.match(worker, /"rest_api_init"/) assert.match(worker, /"create_initial_taxonomies"/) assert.match(worker, /"wp_create_initial_post_meta"/) assert.match(worker, /do_action\('init'\);/) - assert.doesNotMatch(worker, /searchParams\.get\([^)]*callback|searchParams\.get\([^)]*exclude/) + assert.doesNotMatch(worker, /searchParams\.get\([^)]*callback|searchParams\.get\([^)]*exclude|searchParams\.get\([^)]*widget/) }) test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object only for leases", async () => { From 3847b72c87335bb30f16e3e8ee381a7334f7267d Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 02:50:47 -0400 Subject: [PATCH 11/87] Bisect MDI widget settings registration --- packages/runtime-cloudflare/README.md | 2 +- packages/runtime-cloudflare/src/worker.ts | 34 ++++++++++++++++++++--- tests/cloudflare-runtime.test.ts | 18 ++++++++++++ 3 files changed, 49 insertions(+), 5 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 00c1b0ff..75f70c73 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -22,7 +22,7 @@ The WordPress server corpus is a separate deployment artifact, never a Worker-mo The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. -The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names or removal rules from request input. +The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` calls `get_option()` once for every fixed core widget option id base without registering a sidebar or returning option values. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. ## Verification diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 1c3f6914..73dfa2b2 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -556,7 +556,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks"].includes(phase)) { + if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() @@ -628,7 +628,7 @@ function wordpressProbeCode(phase: string): string { return " get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);" } - const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks"] + const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads"] if (widgetPhases.includes(phase)) { return `widgets) && is_array($GLOBALS['wp_widget_factory']->widgets) ? $GLOBALS['wp_widget_factory']->widgets : array(); + foreach ($class_names as $class_name) { + if (!isset($registered[$class_name])) throw new Exception('Widget probe class was not registered: ' . $class_name); + $registered[$class_name]->_register(); + } +} +$direct_groups = array( + 'mdi-widgets-direct-basic-classic-first' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'), + 'mdi-widgets-direct-basic-classic-second' => array('WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget'), + 'mdi-widgets-direct-media' => array('WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video'), + 'mdi-widgets-direct-custom-html-block' => array('WP_Widget_Custom_HTML', 'WP_Widget_Block'), + 'mdi-widgets-direct-block' => array('WP_Widget_Block'), + 'mdi-widgets-direct-custom-html' => array('WP_Widget_Custom_HTML'), +); +$widget_option_id_bases = array('archives', 'block', 'calendar', 'categories', 'custom_html', 'links', 'media_audio', 'media_gallery', 'media_image', 'media_video', 'meta', 'nav_menu', 'pages', 'recent-comments', 'recent-posts', 'rss', 'search', 'tag_cloud', 'text'); if ($phase === 'mdi-widgets-callbacks') { echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'callbacks' => wp_codebox_widgets_probe_inventory('widgets_init'), 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); return; } $removed_init = wp_codebox_widgets_probe_remove_callbacks('init', array('wp_widgets_init'), false); $memory_before = memory_get_usage(true); -wp_codebox_widgets_probe_register_defaults(); +if ($phase === 'mdi-widgets-option-reads') { + foreach ($widget_option_id_bases as $id_base) get_option('widget_' . $id_base); + echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'optionCount' => count($widget_option_id_bases), 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); + return; +} $removed_widgets = array(); -if ($phase === 'mdi-widgets-hooks') { +wp_codebox_widgets_probe_register_defaults(); +if (isset($direct_groups[$phase])) { + $removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array(), true); + wp_codebox_widgets_probe_register_selected($direct_groups[$phase]); + echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'removedInitCallbacks' => $removed_init, 'removedWidgetsCallbacks' => $removed_widgets, 'classNamesAttempted' => $direct_groups[$phase], 'classCount' => count($direct_groups[$phase]), 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); + return; +} elseif ($phase === 'mdi-widgets-hooks') { do_action('widgets_init'); } elseif ($phase === 'mdi-widgets-factory') { $removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array('WP_Widget_Factory::_register_widgets'), true); diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 040d12fa..64511e03 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -171,6 +171,13 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", + "mdi-widgets-direct-basic-classic-first", + "mdi-widgets-direct-basic-classic-second", + "mdi-widgets-direct-media", + "mdi-widgets-direct-custom-html-block", + "mdi-widgets-direct-block", + "mdi-widgets-direct-custom-html", + "mdi-widgets-option-reads", ]) assert.match(worker, new RegExp(`"${phase}"`)) assert.match(worker, /\$needle = "do_action\( 'init' \);"/) @@ -187,6 +194,17 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /if \(get_option\('link_manager_enabled'\)\) register_widget\('WP_Widget_Links'\);/) assert.match(worker, /'widgetClassCount' => \$widget_class_count/) assert.match(worker, /wp_codebox_widgets_probe_inventory\('widgets_init'\)/) + assert.match(worker, /function wp_codebox_widgets_probe_register_selected\(\$class_names\)/) + assert.match(worker, /\$registered\[\$class_name\]\->_register\(\)/) + assert.match(worker, /'mdi-widgets-direct-basic-classic-first' => array\('WP_Widget_Pages'/) + assert.match(worker, /'mdi-widgets-direct-media' => array\('WP_Widget_Media_Audio'/) + assert.match(worker, /'mdi-widgets-direct-custom-html-block' => array\('WP_Widget_Custom_HTML', 'WP_Widget_Block'\)/) + assert.match(worker, /'mdi-widgets-direct-block' => array\('WP_Widget_Block'\)/) + assert.match(worker, /'mdi-widgets-direct-custom-html' => array\('WP_Widget_Custom_HTML'\)/) + assert.match(worker, /foreach \(\$widget_option_id_bases as \$id_base\) get_option\('widget_' \. \$id_base\)/) + assert.match(worker, /'classNamesAttempted' => \$direct_groups\[\$phase\]/) + assert.match(worker, /'optionCount' => count\(\$widget_option_id_bases\)/) + assert.doesNotMatch(worker, /get_option\('widget_' \. \$id_base\).*json_encode/) assert.match(worker, /"rest_api_init"/) assert.match(worker, /"create_initial_taxonomies"/) assert.match(worker, /"wp_create_initial_post_meta"/) From a23366bbfb03c69c3c4a43ac482f5923005a3ed1 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:04:00 -0400 Subject: [PATCH 12/87] Compare MDI widget option visibility --- packages/runtime-cloudflare/README.md | 2 +- packages/runtime-cloudflare/src/worker.ts | 60 +++++++++++++++++++++-- tests/cloudflare-runtime.test.ts | 19 +++++-- 3 files changed, 73 insertions(+), 8 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 75f70c73..7952c5f5 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -22,7 +22,7 @@ The WordPress server corpus is a separate deployment artifact, never a Worker-mo The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. -The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` calls `get_option()` once for every fixed core widget option id base without registering a sidebar or returning option values. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. +The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` reports only fixed widget option names, grouped by `get_option()` presence and direct `$wpdb` SQLite-row presence; it returns no option values. `mdi-widgets-get-settings` calls only `get_settings()` on each default factory object, with `mdi-widgets-get-settings-first` and `mdi-widgets-get-settings-second` as fixed halves; each reports completed classes and array-shape dimensions only. `mdi-widgets-register-one` preloads the fixed `widget_text` option, calls that object's `get_settings()`, then calls only `_register_one()` for its fixed instance. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. ## Verification diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 73dfa2b2..200bd364 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -556,7 +556,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads"].includes(phase)) { + if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() @@ -628,7 +628,7 @@ function wordpressProbeCode(phase: string): string { return " get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);" } - const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads"] + const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"] if (widgetPhases.includes(phase)) { return `_register(); } } +function wp_codebox_widgets_probe_array_dimensions($value) { + if (!is_array($value)) return 0; + $dimensions = 1; + foreach ($value as $item) $dimensions = max($dimensions, 1 + wp_codebox_widgets_probe_array_dimensions($item)); + return $dimensions; +} +function wp_codebox_widgets_probe_get_settings($class_names) { + $registered = isset($GLOBALS['wp_widget_factory']->widgets) && is_array($GLOBALS['wp_widget_factory']->widgets) ? $GLOBALS['wp_widget_factory']->widgets : array(); + $completed = array(); + $settings_shapes = array(); + foreach ($class_names as $class_name) { + if (!isset($registered[$class_name])) throw new Exception('Widget probe class was not registered: ' . $class_name); + $settings = $registered[$class_name]->get_settings(); + $completed[] = $class_name; + $settings_shapes[$class_name] = array('arrayLike' => is_array($settings), 'dimensions' => wp_codebox_widgets_probe_array_dimensions($settings)); + } + return array('completedClassNames' => $completed, 'settingsShapes' => $settings_shapes); +} $direct_groups = array( 'mdi-widgets-direct-basic-classic-first' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'), 'mdi-widgets-direct-basic-classic-second' => array('WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget'), @@ -717,6 +735,12 @@ $direct_groups = array( 'mdi-widgets-direct-custom-html' => array('WP_Widget_Custom_HTML'), ); $widget_option_id_bases = array('archives', 'block', 'calendar', 'categories', 'custom_html', 'links', 'media_audio', 'media_gallery', 'media_image', 'media_video', 'meta', 'nav_menu', 'pages', 'recent-comments', 'recent-posts', 'rss', 'search', 'tag_cloud', 'text'); +$widget_classes = array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block'); +$get_settings_groups = array( + 'mdi-widgets-get-settings' => $widget_classes, + 'mdi-widgets-get-settings-first' => array_slice($widget_classes, 0, 10), + 'mdi-widgets-get-settings-second' => array_slice($widget_classes, 10), +); if ($phase === 'mdi-widgets-callbacks') { echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'callbacks' => wp_codebox_widgets_probe_inventory('widgets_init'), 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); return; @@ -724,12 +748,40 @@ if ($phase === 'mdi-widgets-callbacks') { $removed_init = wp_codebox_widgets_probe_remove_callbacks('init', array('wp_widgets_init'), false); $memory_before = memory_get_usage(true); if ($phase === 'mdi-widgets-option-reads') { - foreach ($widget_option_id_bases as $id_base) get_option('widget_' . $id_base); - echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'optionCount' => count($widget_option_id_bases), 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); + $get_option_found = array(); + $get_option_missing = array(); + $sqlite_present = array(); + $sqlite_missing = array(); + foreach ($widget_option_id_bases as $id_base) { + $option_name = 'widget_' . $id_base; + if (get_option($option_name, false) === false) $get_option_missing[] = $option_name; + else $get_option_found[] = $option_name; + $row_present = $wpdb->get_var($wpdb->prepare("SELECT 1 FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", $option_name)); + if ($row_present === '1') $sqlite_present[] = $option_name; + else $sqlite_missing[] = $option_name; + } + echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'getOption' => array('found' => $get_option_found, 'missing' => $get_option_missing), 'sqliteRows' => array('present' => $sqlite_present, 'missing' => $sqlite_missing), 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); return; } $removed_widgets = array(); wp_codebox_widgets_probe_register_defaults(); +if (isset($get_settings_groups[$phase])) { + $settings_probe = wp_codebox_widgets_probe_get_settings($get_settings_groups[$phase]); + echo json_encode(array_merge(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true), $settings_probe)); + return; +} +if ($phase === 'mdi-widgets-register-one') { + $class_name = 'WP_Widget_Text'; + $option_name = 'widget_text'; + $instance_number = 2; + if (!isset($GLOBALS['wp_widget_factory']->widgets[$class_name])) throw new Exception('Widget probe class was not registered: ' . $class_name); + update_option($option_name, array($instance_number => array('title' => 'Widget probe'))); + $widget = $GLOBALS['wp_widget_factory']->widgets[$class_name]; + $settings = $widget->get_settings(); + $widget->_register_one($instance_number); + echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'widgetClassName' => $class_name, 'preloadedOptionName' => $option_name, 'getSettingsShape' => array('arrayLike' => is_array($settings), 'dimensions' => wp_codebox_widgets_probe_array_dimensions($settings)), 'registerOneCompleted' => true)); + return; +} if (isset($direct_groups[$phase])) { $removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array(), true); wp_codebox_widgets_probe_register_selected($direct_groups[$phase]); diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 64511e03..bf1cc13e 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -178,6 +178,10 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", + "mdi-widgets-get-settings", + "mdi-widgets-get-settings-first", + "mdi-widgets-get-settings-second", + "mdi-widgets-register-one", ]) assert.match(worker, new RegExp(`"${phase}"`)) assert.match(worker, /\$needle = "do_action\( 'init' \);"/) @@ -201,10 +205,19 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /'mdi-widgets-direct-custom-html-block' => array\('WP_Widget_Custom_HTML', 'WP_Widget_Block'\)/) assert.match(worker, /'mdi-widgets-direct-block' => array\('WP_Widget_Block'\)/) assert.match(worker, /'mdi-widgets-direct-custom-html' => array\('WP_Widget_Custom_HTML'\)/) - assert.match(worker, /foreach \(\$widget_option_id_bases as \$id_base\) get_option\('widget_' \. \$id_base\)/) + assert.match(worker, /if \(get_option\(\$option_name, false\) === false\) \$get_option_missing\[\] = \$option_name/) + assert.match(worker, /\$wpdb->get_var\(\$wpdb->prepare\("SELECT 1 FROM \{\$wpdb->options\} WHERE option_name = %s LIMIT 1", \$option_name\)\)/) + assert.match(worker, /'getOption' => array\('found' => \$get_option_found, 'missing' => \$get_option_missing\)/) + assert.match(worker, /'sqliteRows' => array\('present' => \$sqlite_present, 'missing' => \$sqlite_missing\)/) + assert.match(worker, /function wp_codebox_widgets_probe_get_settings\(\$class_names\)/) + assert.match(worker, /\$settings = \$registered\[\$class_name\]->get_settings\(\)/) + assert.match(worker, /'mdi-widgets-get-settings-first' => array_slice\(\$widget_classes, 0, 10\)/) + assert.match(worker, /'mdi-widgets-get-settings-second' => array_slice\(\$widget_classes, 10\)/) + assert.match(worker, /\$widget->_register_one\(\$instance_number\)/) + assert.match(worker, /update_option\(\$option_name, array\(\$instance_number => array\('title' => 'Widget probe'\)\)\)/) assert.match(worker, /'classNamesAttempted' => \$direct_groups\[\$phase\]/) - assert.match(worker, /'optionCount' => count\(\$widget_option_id_bases\)/) - assert.doesNotMatch(worker, /get_option\('widget_' \. \$id_base\).*json_encode/) + assert.doesNotMatch(worker, /json_encode\(\$settings\)/) + assert.doesNotMatch(worker, /json_encode\(get_option/) assert.match(worker, /"rest_api_init"/) assert.match(worker, /"create_initial_taxonomies"/) assert.match(worker, /"wp_create_initial_post_meta"/) From 1969510d103c7f8f591d848bb5524d3a9a97173f Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:10:08 -0400 Subject: [PATCH 13/87] Probe complete canonical WordPress state --- packages/runtime-cloudflare/README.md | 6 ++- packages/runtime-cloudflare/src/worker.ts | 66 +++++++++++++++++------ scripts/cloudflare-local-gate.mjs | 15 ++++++ tests/cloudflare-runtime.test.ts | 15 ++++++ 4 files changed, 85 insertions(+), 17 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 7952c5f5..9bfbd164 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -20,6 +20,10 @@ The WordPress server corpus is a separate deployment artifact, never a Worker-mo ## MDI Init Diagnostics +Every `mdi-*` lifecycle diagnostic materializes the validated packaged canonical MDI seed and the separately managed R2 WordPress runtime artifact in a disposable PHP-WASM runtime. They never acquire a coordinator lease, read or write a canonical R2 revision, or promote state. The old three-option fixture was intentionally removed because it does not represent complete MDI WordPress state. + +`?phase=canonical-wordpress` performs a full normal WordPress boot with that exact seed and artifact outside the coordinator, then returns WordPress version, memory, and non-sensitive table counts. Its widget-state count includes the `widget_*` options and `sidebars_widgets`. `?phase=canonical-bootstrap-setup` additionally runs the normal bootstrap setup with a probe-only internal password and fixed probe origin, flushes only the disposable runtime, and returns changed-path counts. Neither probe returns password, option, user, or content values. + The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` reports only fixed widget option names, grouped by `get_option()` presence and direct `$wpdb` SQLite-row presence; it returns no option values. `mdi-widgets-get-settings` calls only `get_settings()` on each default factory object, with `mdi-widgets-get-settings-first` and `mdi-widgets-get-settings-second` as fixed halves; each reports completed classes and array-shape dimensions only. `mdi-widgets-register-one` preloads the fixed `widget_text` option, calls that object's `get_settings()`, then calls only `_register_one()` for its fixed instance. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. @@ -30,6 +34,6 @@ The fixed widget probes split `wp_widgets_init` without changing its normal path 2. Run `npm run provision:cloudflare-wordpress-runtime-corpus -- --local --persist-to ` to verify and upload the exact content-addressed artifact into isolated local R2 storage. Use `--remote` only for an explicit deployment operation. 3. Run `npm run test:cloudflare-runtime` for routing, canonical-state, artifact validation, source contract, and TypeScript coverage. 4. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. -5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions the artifact before workerd starts, then injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. +5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions the artifact before workerd starts, then verifies the complete-seed canonical boot and ephemeral bootstrap probes, injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 200bd364..af07d900 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -517,7 +517,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise try { const wordpress = await materializeWordPressServerFiles(php, bucket) await materializeMarkdownDatabaseIntegration(php) - materializeRuntimeFiles(php, MARKDOWN_ROOT, initialMarkdownFiles()) + materializeRuntimeFiles(php, MARKDOWN_ROOT, await packagedCanonicalMarkdownSeed()) const evidence = (await php.run({ code: " file_exists('/wordpress/wp-content/db.php'), 'storage' => file_exists('/wordpress/wp-content/plugins/markdown-database-integration/inc/class-wp-markdown-storage.php'), 'siteurl' => file_exists('/wordpress/wp-content/markdown/_options/siteurl.json')]);", })).text.trim() @@ -528,7 +528,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } if (phase === "mdi-shortinit") { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: " $wp_version, 'markdownDropin' => defined('MARKDOWN_DB_DROPIN'), 'markdownMode' => defined('MARKDOWN_DB_MODE') ? MARKDOWN_DB_MODE : '']);", @@ -540,7 +540,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } if (phase === "mdi-wordpress" || phase === "mdi-option" || phase === "mdi-insert") { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const operation = phase === "mdi-option" ? "$updated = update_option('wp_codebox_mdi_probe', 1); $result = ['updated' => $updated];" @@ -557,7 +557,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() return probeResponse(phase, JSON.parse(evidence) as Record) @@ -566,6 +566,37 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } + if (phase === "canonical-wordpress" || phase === "canonical-bootstrap-setup") { + const canonicalSeed = await packagedCanonicalMarkdownSeed() + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, canonicalSeed, new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket) + try { + if (phase === "canonical-bootstrap-setup") { + const passwordFile = "/tmp/wp-codebox-canonical-bootstrap-probe-password" + runtime.php.writeFile(passwordFile, new TextEncoder().encode("probe-only-password")) + const output = (await runtime.php.run({ code: canonicalBootstrapSetupCode(passwordFile, "https://canonical-probe.invalid") })).text.trim() + if (output !== "flushed") throw new Error("Canonical bootstrap setup probe did not confirm its ephemeral flush.") + const changes = canonicalChangedPathCounts(canonicalSeed, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT)) + return probeResponse(phase, { wordpressVersion: runtime.wordpressVersion, canonicalSeedFiles: canonicalSeed.length, ...changes }) + } + const evidence = (await runtime.php.run({ code: ` get_bloginfo('version'), + 'postCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->posts}"), + 'pageCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->posts} WHERE post_type = 'page'"), + 'userCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->users}"), + 'optionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options}"), + 'widgetOptionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options} WHERE option_name LIKE 'widget\\_%'"), + 'widgetStateOptionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options} WHERE option_name LIKE 'widget\\_%' OR option_name = 'sidebars_widgets'"), + 'memoryBytes' => memory_get_usage(true), + 'peakMemoryBytes' => memory_get_peak_usage(true), +]);` })).text.trim() + return probeResponse(phase, { canonicalSeedFiles: canonicalSeed.length, ...JSON.parse(evidence) as Record }) + } finally { + runtime.php.exit() + } + } + if (phase?.startsWith("seeded-")) { const runtime = await bootWordPressRuntime( "do-not-attempt-installing", @@ -1006,18 +1037,6 @@ async function bootWordPressRuntime( return { php, requestHandler, wordpressVersion } } -function initialMarkdownFiles(): RuntimeFile[] { - const options = [ - { option_id: 1, option_name: "siteurl", option_value: SITE_URL, autoload: "on" }, - { option_id: 2, option_name: "home", option_value: SITE_URL, autoload: "on" }, - { option_id: 3, option_name: "blogname", option_value: "WP Codebox Cloudflare Runtime", autoload: "on" }, - ] - return options.map((option) => ({ - path: `_options/${option.option_name}.json`, - bytes: new TextEncoder().encode(JSON.stringify(option, null, 2)), - })) -} - function materializeRuntimeFiles(php: PHP, root: string, files: RuntimeFile[]): void { for (const file of files) { const destination = `${root}/${file.path}` @@ -1043,6 +1062,21 @@ function collectRuntimeFiles(php: PHP, root: string): RuntimeFile[] { return files.sort((left, right) => left.path.localeCompare(right.path)) } +function canonicalChangedPathCounts(before: RuntimeFile[], after: RuntimeFile[]): { createdPathCount: number; changedPathCount: number; deletedPathCount: number } { + const beforeByPath = new Map(before.map((file) => [file.path, file.bytes])) + const afterByPath = new Map(after.map((file) => [file.path, file.bytes])) + let createdPathCount = 0 + let changedPathCount = 0 + let deletedPathCount = 0 + for (const [path, bytes] of afterByPath) { + const previous = beforeByPath.get(path) + if (!previous) createdPathCount++ + else if (previous.byteLength !== bytes.byteLength || previous.some((byte, index) => byte !== bytes[index])) changedPathCount++ + } + for (const path of beforeByPath.keys()) if (!afterByPath.has(path)) deletedPathCount++ + return { createdPathCount, changedPathCount, deletedPathCount } +} + async function sha256Hex(bytes: Uint8Array): Promise { const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes).buffer) return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("") diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 3d8239c7..54bc2ce9 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -16,6 +16,7 @@ try { await run("npm", ["run", "generate:cloudflare-wordpress-runtime-corpus"]) await run("npm", ["run", "provision:cloudflare-wordpress-runtime-corpus", "--", "--local", "--persist-to", stateDirectory]) await startWorker() + await assertCanonicalProbes() await assertConcurrentMutations() const adminHtml = await login() const editorHtml = await assertPostNewEditor() @@ -166,6 +167,20 @@ async function assertHealthResponse() { if (response.status !== 200 || body.schema !== "wp-codebox/cloudflare-runtime-health/v1" || body.marker !== "wp-codebox-cloudflare-runtime-health" || body.phpVersion !== "8.5.8" || typeof body.wordpressVersion !== "string" || body.execution?.status !== "ok") throw new Error(`Unexpected Cloudflare runtime health envelope: ${JSON.stringify(body)}`) } +async function assertCanonicalProbes() { + const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json() + const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json() + const counts = ["canonicalSeedFiles", "postCount", "pageCount", "userCount", "optionCount", "widgetOptionCount", "widgetStateOptionCount", "memoryBytes", "peakMemoryBytes"] + if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19) { + throw new Error(`Canonical WordPress probe did not boot the complete seed: ${JSON.stringify(wordpress)}`) + } + const changedCounts = ["createdPathCount", "changedPathCount", "deletedPathCount"] + if (setup.completed !== true || typeof setup.evidence?.wordpressVersion !== "string" || changedCounts.some((key) => !Number.isInteger(setup.evidence?.[key])) || setup.evidence.changedPathCount < 1) { + throw new Error(`Canonical bootstrap setup probe did not produce ephemeral changes: ${JSON.stringify(setup)}`) + } + console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) +} + async function assertWordPressPage(target, label) { const response = await request(target) const body = await response.text() diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index bf1cc13e..10881e54 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -225,6 +225,21 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.doesNotMatch(worker, /searchParams\.get\([^)]*callback|searchParams\.get\([^)]*exclude|searchParams\.get\([^)]*widget/) }) +test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed without canonical persistence", async () => { + const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) + + assert.equal((probes.match(/initialMarkdownFiles/g) ?? []).length, 0) + assert.ok((probes.match(/await packagedCanonicalMarkdownSeed\(\)/g) ?? []).length >= 4) + assert.match(probes, /phase === "canonical-wordpress" \|\| phase === "canonical-bootstrap-setup"/) + assert.match(probes, /materializeWordPressServerFiles/) + assert.match(probes, /canonicalBootstrapSetupCode\(passwordFile, "https:\/\/canonical-probe\.invalid"\)/) + assert.match(probes, /canonicalChangedPathCounts\(canonicalSeed, collectRuntimeFiles/) + assert.doesNotMatch(probes, /persistMarkdownRevision|commitLease|coordinatorCall/) + assert.match(worker, /'widgetOptionCount' => \(int\) \$wpdb->get_var/) + assert.match(worker, /function canonicalChangedPathCounts/) +}) + test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object only for leases", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const coordinator = await readFile(new URL("../packages/runtime-cloudflare/src/state-coordinator.ts", import.meta.url), "utf8") From ddb1f4c5cfc64438d3e9b1d69061176d8935ce90 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:21:16 -0400 Subject: [PATCH 14/87] Probe remaining widget registrations --- packages/runtime-cloudflare/src/worker.ts | 15 +++++++++++---- tests/cloudflare-runtime.test.ts | 9 ++++++++- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index af07d900..f35afb08 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -556,7 +556,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { + if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-first-pages", "mdi-widgets-direct-basic-classic-first-calendar", "mdi-widgets-direct-basic-classic-first-archives", "mdi-widgets-direct-basic-classic-first-meta", "mdi-widgets-direct-basic-classic-first-search", "mdi-widgets-direct-basic-classic-first-text", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-direct-factory-all", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() @@ -659,7 +659,7 @@ function wordpressProbeCode(phase: string): string { return " get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);" } - const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"] + const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-first-pages", "mdi-widgets-direct-basic-classic-first-calendar", "mdi-widgets-direct-basic-classic-first-archives", "mdi-widgets-direct-basic-classic-first-meta", "mdi-widgets-direct-basic-classic-first-search", "mdi-widgets-direct-basic-classic-first-text", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-direct-factory-all", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"] if (widgetPhases.includes(phase)) { return ` $completed, 'settingsShapes' => $settings_shapes); } $direct_groups = array( - 'mdi-widgets-direct-basic-classic-first' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'), + 'mdi-widgets-direct-basic-classic-first' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'), + 'mdi-widgets-direct-basic-classic-first-pages' => array('WP_Widget_Pages'), + 'mdi-widgets-direct-basic-classic-first-calendar' => array('WP_Widget_Calendar'), + 'mdi-widgets-direct-basic-classic-first-archives' => array('WP_Widget_Archives'), + 'mdi-widgets-direct-basic-classic-first-meta' => array('WP_Widget_Meta'), + 'mdi-widgets-direct-basic-classic-first-search' => array('WP_Widget_Search'), + 'mdi-widgets-direct-basic-classic-first-text' => array('WP_Widget_Text'), 'mdi-widgets-direct-basic-classic-second' => array('WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget'), 'mdi-widgets-direct-media' => array('WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video'), 'mdi-widgets-direct-custom-html-block' => array('WP_Widget_Custom_HTML', 'WP_Widget_Block'), 'mdi-widgets-direct-block' => array('WP_Widget_Block'), 'mdi-widgets-direct-custom-html' => array('WP_Widget_Custom_HTML'), + 'mdi-widgets-direct-factory-all' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block'), ); $widget_option_id_bases = array('archives', 'block', 'calendar', 'categories', 'custom_html', 'links', 'media_audio', 'media_gallery', 'media_image', 'media_video', 'meta', 'nav_menu', 'pages', 'recent-comments', 'recent-posts', 'rss', 'search', 'tag_cloud', 'text'); -$widget_classes = array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block'); +$widget_classes = array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block'); $get_settings_groups = array( 'mdi-widgets-get-settings' => $widget_classes, 'mdi-widgets-get-settings-first' => array_slice($widget_classes, 0, 10), diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 10881e54..b15e9837 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -200,11 +200,18 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /wp_codebox_widgets_probe_inventory\('widgets_init'\)/) assert.match(worker, /function wp_codebox_widgets_probe_register_selected\(\$class_names\)/) assert.match(worker, /\$registered\[\$class_name\]\->_register\(\)/) - assert.match(worker, /'mdi-widgets-direct-basic-classic-first' => array\('WP_Widget_Pages'/) + assert.match(worker, /'mdi-widgets-direct-basic-classic-first' => array\('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'\)/) + assert.doesNotMatch(worker, /'mdi-widgets-direct-basic-classic-first' => array\([^)]*WP_Widget_Links/) + for (const className of ["Pages", "Calendar", "Archives", "Meta", "Search", "Text"]) { + assert.match(worker, new RegExp(`'mdi-widgets-direct-basic-classic-first-${className.toLowerCase()}' => array\\('WP_Widget_${className}'\\)`)) + } assert.match(worker, /'mdi-widgets-direct-media' => array\('WP_Widget_Media_Audio'/) assert.match(worker, /'mdi-widgets-direct-custom-html-block' => array\('WP_Widget_Custom_HTML', 'WP_Widget_Block'\)/) assert.match(worker, /'mdi-widgets-direct-block' => array\('WP_Widget_Block'\)/) assert.match(worker, /'mdi-widgets-direct-custom-html' => array\('WP_Widget_Custom_HTML'\)/) + assert.match(worker, /'mdi-widgets-direct-factory-all' => array\('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Media_Audio'/) + assert.doesNotMatch(worker, /'mdi-widgets-direct-factory-all' => array\([^)]*WP_Widget_Links/) + assert.doesNotMatch(worker, /'mdi-widgets-direct-factory-all' => array\([^)]*_wp_block_theme_register_classic_sidebars/) assert.match(worker, /if \(get_option\(\$option_name, false\) === false\) \$get_option_missing\[\] = \$option_name/) assert.match(worker, /\$wpdb->get_var\(\$wpdb->prepare\("SELECT 1 FROM \{\$wpdb->options\} WHERE option_name = %s LIMIT 1", \$option_name\)\)/) assert.match(worker, /'getOption' => array\('found' => \$get_option_found, 'missing' => \$get_option_missing\)/) From 2ec7efe04e50a3cddb8eac30a317fe7547f2801c Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:29:02 -0400 Subject: [PATCH 15/87] Remove diagnostic hooks through WordPress APIs --- packages/runtime-cloudflare/src/worker.ts | 20 +++---- .../runtime-core/src/runtime-php-snippets.ts | 11 ++-- .../src/bench-command-handlers.ts | 11 ++-- .../src/phpunit-command-handlers.ts | 20 ++++--- scripts/cloudflare-local-gate.mjs | 20 +++++++ tests/cloudflare-runtime.test.ts | 53 +++++++++++++++++++ tests/runtime-php-snippets.test.ts | 10 ++++ 7 files changed, 112 insertions(+), 33 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index f35afb08..1b1f670c 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -697,15 +697,15 @@ function wp_codebox_widgets_probe_remove_callbacks($hook_name, $identifiers, $re $removed = array(); $hook = isset($GLOBALS['wp_filter'][$hook_name]) ? $GLOBALS['wp_filter'][$hook_name] : null; if (!$hook || !isset($hook->callbacks) || !is_array($hook->callbacks)) return $removed; - foreach ($hook->callbacks as $priority => $callbacks) { - foreach ($callbacks as $index => $registered) { + $callbacks_by_priority = $hook->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($callbacks as $registered) { + if (!isset($registered['function'])) continue; $identifier = wp_codebox_widgets_probe_callback_identifier($registered['function']); if (in_array($identifier, $identifiers, true) !== $retain) { - unset($hook->callbacks[$priority][$index]); - $removed[] = $identifier; + if (remove_action($hook_name, $registered['function'], (int) $priority)) $removed[] = $identifier; } } - if (empty($hook->callbacks[$priority])) unset($hook->callbacks[$priority]); } sort($removed, SORT_STRING); return $removed; @@ -931,15 +931,15 @@ if ($phase === 'mdi-init-callbacks') { $removed = array(); $hook = isset($GLOBALS['wp_filter']['init']) ? $GLOBALS['wp_filter']['init'] : null; if ($hook && isset($hook->callbacks) && is_array($hook->callbacks)) { - foreach ($hook->callbacks as $priority => $callbacks) { - foreach ($callbacks as $index => $registered) { + $callbacks_by_priority = $hook->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($callbacks as $registered) { + if (!isset($registered['function'])) continue; $identifier = wp_codebox_init_probe_callback_identifier($registered['function']); if (wp_codebox_init_probe_matches($identifier, $excluded)) { - unset($hook->callbacks[$priority][$index]); - $removed[] = $identifier; + if (remove_action('init', $registered['function'], (int) $priority)) $removed[] = $identifier; } } - if (empty($hook->callbacks[$priority])) unset($hook->callbacks[$priority]); } } sort($removed, SORT_STRING); diff --git a/packages/runtime-core/src/runtime-php-snippets.ts b/packages/runtime-core/src/runtime-php-snippets.ts index 979e677a..dc26ea3d 100644 --- a/packages/runtime-core/src/runtime-php-snippets.ts +++ b/packages/runtime-core/src/runtime-php-snippets.ts @@ -28,16 +28,15 @@ function ${defer}(string $hook_name, array $before): array { if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; } - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); - } - if (empty($wp_filter[$hook_name]->callbacks[$priority])) { - unset($wp_filter[$hook_name]->callbacks[$priority]); + if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + } } } usort($deferred, static function (array $left, array $right): int { return ($left['priority'] ?? 10) <=> ($right['priority'] ?? 10); }); diff --git a/packages/runtime-playground/src/bench-command-handlers.ts b/packages/runtime-playground/src/bench-command-handlers.ts index db1efa5f..b2085c81 100644 --- a/packages/runtime-playground/src/bench-command-handlers.ts +++ b/packages/runtime-playground/src/bench-command-handlers.ts @@ -1195,16 +1195,15 @@ function wp_codebox_bench_defer_new_wordpress_hook_callbacks(string $hook_name, if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; } - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); - } - if (empty($wp_filter[$hook_name]->callbacks[$priority])) { - unset($wp_filter[$hook_name]->callbacks[$priority]); + if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + } } } usort($deferred, static function (array $left, array $right): int { diff --git a/packages/runtime-playground/src/phpunit-command-handlers.ts b/packages/runtime-playground/src/phpunit-command-handlers.ts index f53aafcf..7b28581a 100644 --- a/packages/runtime-playground/src/phpunit-command-handlers.ts +++ b/packages/runtime-playground/src/phpunit-command-handlers.ts @@ -510,15 +510,14 @@ function pg_remove_new_wordpress_hook_callbacks(string $hook_name, array $before if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { if (!isset($before[$priority . ':' . $callback_id])) { - unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); + $callback = $callbacks[$callback_id]; + if (isset($callback['function'])) remove_action($hook_name, $callback['function'], (int) $priority); } } - if (empty($wp_filter[$hook_name]->callbacks[$priority])) { - unset($wp_filter[$hook_name]->callbacks[$priority]); - } } } @@ -528,16 +527,15 @@ function pg_defer_new_wordpress_hook_callbacks(string $hook_name, array $before) if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; } - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); - } - if (empty($wp_filter[$hook_name]->callbacks[$priority])) { - unset($wp_filter[$hook_name]->callbacks[$priority]); + if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + } } } usort($deferred, static function (array $left, array $right): int { return $left['priority'] <=> $right['priority']; }); diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 54bc2ce9..90531d88 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -17,6 +17,7 @@ try { await run("npm", ["run", "provision:cloudflare-wordpress-runtime-corpus", "--", "--local", "--persist-to", stateDirectory]) await startWorker() await assertCanonicalProbes() + await assertMdiDiagnostics() await assertConcurrentMutations() const adminHtml = await login() const editorHtml = await assertPostNewEditor() @@ -181,6 +182,25 @@ async function assertCanonicalProbes() { console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) } +async function assertMdiDiagnostics() { + const phases = ["mdi-init-exclude-widgets", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first"] + const diagnostics = await Promise.all(phases.map(async (phase) => { + const response = await request(`${origin}/?phase=${phase}`) + const body = await response.text() + assertNoPhpDiagnostics(body, phase) + if (!response.ok) throw new Error(`${phase} failed with ${response.status}: ${body}`) + const probe = JSON.parse(body) + if (probe.completed !== true || probe.evidence?.bootstrapPhase !== phase) throw new Error(`${phase} did not complete: ${body}`) + return probe.evidence + })) + const [initExclusion, factory, remainingHooks, directRegistration] = diagnostics + if (!Array.isArray(initExclusion.removedCallbacks) || !initExclusion.removedCallbacks.includes("wp_widgets_init")) throw new Error(`mdi-init-exclude-widgets did not remove wp_widgets_init: ${JSON.stringify(initExclusion)}`) + if (!Array.isArray(factory.removedInitCallbacks) || !Array.isArray(factory.removedWidgetsCallbacks) || !Number.isInteger(factory.widgetClassCount)) throw new Error(`mdi-widgets-factory did not return fixed removal evidence: ${JSON.stringify(factory)}`) + if (!Array.isArray(remainingHooks.removedWidgetsCallbacks) || !Number.isInteger(remainingHooks.widgetClassCount)) throw new Error(`mdi-widgets-remaining-hooks did not return fixed removal evidence: ${JSON.stringify(remainingHooks)}`) + if (!Array.isArray(directRegistration.classNamesAttempted) || directRegistration.classCount !== directRegistration.classNamesAttempted.length) throw new Error(`mdi-widgets-direct-basic-classic-first did not return its fixed class group: ${JSON.stringify(directRegistration)}`) + console.log(`MDI diagnostics: init exclusion removed ${initExclusion.removedCallbacks.length} callback(s); widget factory ${factory.widgetClassCount} classes; remaining hooks ${remainingHooks.widgetClassCount} classes; direct registration ${directRegistration.classCount} classes.`) +} + async function assertWordPressPage(target, label) { const response = await request(target) const body = await response.text() diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index b15e9837..0bbc9320 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -232,6 +232,59 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.doesNotMatch(worker, /searchParams\.get\([^)]*callback|searchParams\.get\([^)]*exclude|searchParams\.get\([^)]*widget/) }) +test("Cloudflare widget diagnostics preserve later hook priorities after callback removal", async () => { + const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const helper = worker.match(/function wp_codebox_widgets_probe_callback_identifier\(\$callback\) \{[\s\S]*?(?=function wp_codebox_widgets_probe_register_defaults)/)?.[0] + assert.ok(helper, "The widget probe callback helper is present in the worker PHP probe.") + assert.match(helper, /\$callbacks_by_priority = \$hook->callbacks/) + assert.match(helper, /remove_action\(\$hook_name, \$registered\['function'\], \(int\) \$priority\)/) + assert.doesNotMatch(helper, /unset\(\$hook->callbacks/) + + const php = `callbacks[$priority][] = array('function' => $callback, 'accepted_args' => $accepted_args); + $this->priorities = array_keys($this->callbacks); + sort($this->priorities, SORT_NUMERIC); + } + public function remove_filter($callback, $priority) { + if (!isset($this->callbacks[$priority])) return false; + foreach ($this->callbacks[$priority] as $index => $registered) { + if ($registered['function'] === $callback) { + unset($this->callbacks[$priority][$index]); + if (empty($this->callbacks[$priority])) unset($this->callbacks[$priority]); + $this->priorities = array_keys($this->callbacks); + sort($this->priorities, SORT_NUMERIC); + return true; + } + } + return false; + } + public function do_action() { + foreach ($this->priorities as $priority) { + foreach ($this->callbacks[$priority] as $registered) call_user_func($registered['function']); + } + } +} +$GLOBALS['wp_filter'] = array('widgets_init' => new WP_Hook()); +function add_action($hook_name, $callback, $priority = 10, $accepted_args = 1) { $GLOBALS['wp_filter'][$hook_name]->add_filter($callback, $priority, $accepted_args); } +function remove_action($hook_name, $callback, $priority = 10) { return $GLOBALS['wp_filter'][$hook_name]->remove_filter($callback, $priority); } +function do_action($hook_name) { $GLOBALS['wp_filter'][$hook_name]->do_action(); } +${helper} +function wp_codebox_probe_removed_callback() { $GLOBALS['wp_codebox_probe_calls'][] = 'removed'; } +function wp_codebox_probe_later_priority_sentinel() { $GLOBALS['wp_codebox_probe_calls'][] = 'later-priority-sentinel'; } +$GLOBALS['wp_codebox_probe_calls'] = array(); +add_action('widgets_init', 'wp_codebox_probe_removed_callback', 10); +add_action('widgets_init', 'wp_codebox_probe_later_priority_sentinel', 20); +$removed_callbacks = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array('wp_codebox_probe_removed_callback'), false); +do_action('widgets_init'); +echo json_encode(array('removed' => $removed_callbacks, 'calls' => $GLOBALS['wp_codebox_probe_calls']));` + const { stdout } = await execFileAsync("php", ["-r", php.slice(" { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) diff --git a/tests/runtime-php-snippets.test.ts b/tests/runtime-php-snippets.test.ts index 9481c060..a73633ed 100644 --- a/tests/runtime-php-snippets.test.ts +++ b/tests/runtime-php-snippets.test.ts @@ -30,6 +30,16 @@ $GLOBALS['calls'] = array(); function did_action($hook_name) { return (int) ($GLOBALS['wp_actions'][$hook_name] ?? 0); } function wp_get_abilities() { return array('before' => true, 'after' => true); } +function remove_action($hook_name, $callback, $priority = 10) { + foreach ($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority] ?? array() as $callback_id => $registered) { + if (($registered['function'] ?? null) === $callback) { + unset($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority][$callback_id]); + if (empty($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority])) unset($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority]); + return true; + } + } + return false; +} $GLOBALS['wp_filter']['init']->callbacks[10]['existing'] = array('function' => static function () { $GLOBALS['calls'][] = 'existing'; }, 'accepted_args' => 0); $state = contained_runtime_test_component_lifecycle_replay_prepare(); From 6d95c3b44efa58066a368f5a4b76f3d5f5ad3a40 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:31:46 -0400 Subject: [PATCH 16/87] Isolate canonical cron scheduling --- packages/runtime-cloudflare/src/worker.ts | 8 +++++++- tests/cloudflare-runtime.test.ts | 13 +++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 1b1f670c..8d76b479 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -556,7 +556,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-first-pages", "mdi-widgets-direct-basic-classic-first-calendar", "mdi-widgets-direct-basic-classic-first-archives", "mdi-widgets-direct-basic-classic-first-meta", "mdi-widgets-direct-basic-classic-first-search", "mdi-widgets-direct-basic-classic-first-text", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-direct-factory-all", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { + if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-wp-cron", "mdi-init-exclude-privacy-schedule", "mdi-init-exclude-update-schedule", "mdi-init-exclude-wp-cron-privacy-schedule", "mdi-init-exclude-wp-cron-update-schedule", "mdi-init-exclude-privacy-update-schedule", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-first-pages", "mdi-widgets-direct-basic-classic-first-calendar", "mdi-widgets-direct-basic-classic-first-archives", "mdi-widgets-direct-basic-classic-first-meta", "mdi-widgets-direct-basic-classic-first-search", "mdi-widgets-direct-basic-classic-first-text", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-direct-factory-all", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() @@ -851,6 +851,12 @@ require '/wordpress/wp-load.php';` "wp_schedule_site_health_cron", "WP_Site_Health::maybe_create_scheduled_event", ], + "mdi-init-exclude-wp-cron": ["wp_cron"], + "mdi-init-exclude-privacy-schedule": ["wp_schedule_delete_old_privacy_export_files"], + "mdi-init-exclude-update-schedule": ["wp_schedule_update_checks"], + "mdi-init-exclude-wp-cron-privacy-schedule": ["wp_cron", "wp_schedule_delete_old_privacy_export_files"], + "mdi-init-exclude-wp-cron-update-schedule": ["wp_cron", "wp_schedule_update_checks"], + "mdi-init-exclude-privacy-update-schedule": ["wp_schedule_delete_old_privacy_export_files", "wp_schedule_update_checks"], "mdi-init-exclude-block-registration": [ "register_block_core_*", "register_core_block_*", diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 0bbc9320..ee3b021f 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -161,6 +161,12 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi for (const phase of [ "mdi-init-callbacks", "mdi-init-exclude-scheduling", + "mdi-init-exclude-wp-cron", + "mdi-init-exclude-privacy-schedule", + "mdi-init-exclude-update-schedule", + "mdi-init-exclude-wp-cron-privacy-schedule", + "mdi-init-exclude-wp-cron-update-schedule", + "mdi-init-exclude-privacy-update-schedule", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", @@ -189,6 +195,12 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /sort\(\$identifiers, SORT_STRING\)/) assert.match(worker, /ksort\(\$inventory, SORT_NUMERIC\)/) assert.match(worker, /"wp_schedule_update_checks"/) + assert.match(worker, /"mdi-init-exclude-wp-cron": \["wp_cron"\]/) + assert.match(worker, /"mdi-init-exclude-privacy-schedule": \["wp_schedule_delete_old_privacy_export_files"\]/) + assert.match(worker, /"mdi-init-exclude-update-schedule": \["wp_schedule_update_checks"\]/) + assert.match(worker, /"mdi-init-exclude-wp-cron-privacy-schedule": \["wp_cron", "wp_schedule_delete_old_privacy_export_files"\]/) + assert.match(worker, /"mdi-init-exclude-wp-cron-update-schedule": \["wp_cron", "wp_schedule_update_checks"\]/) + assert.match(worker, /"mdi-init-exclude-privacy-update-schedule": \["wp_schedule_delete_old_privacy_export_files", "wp_schedule_update_checks"\]/) assert.match(worker, /"WP_Site_Health::maybe_create_scheduled_event"/) assert.match(worker, /"register_block_core_\*"/) assert.match(worker, /"WP_Block_Supports::init"/) @@ -229,6 +241,7 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /"create_initial_taxonomies"/) assert.match(worker, /"wp_create_initial_post_meta"/) assert.match(worker, /do_action\('init'\);/) + assert.match(worker, /'completed' => true, 'removedCallbacks' => \$removed, 'memoryBeforeBytes' => \$memory_before, 'memoryBytes' => memory_get_usage\(true\), 'peakMemoryBytes' => memory_get_peak_usage\(true\)/) assert.doesNotMatch(worker, /searchParams\.get\([^)]*callback|searchParams\.get\([^)]*exclude|searchParams\.get\([^)]*widget/) }) From 8fd3a40601e73a946c60da6b420edf6f20691dcd Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:36:35 -0400 Subject: [PATCH 17/87] Skip disabled cron registration in Cloudflare --- packages/runtime-cloudflare/README.md | 4 +++- packages/runtime-cloudflare/src/worker.ts | 25 ++++++++++++++++++++--- scripts/cloudflare-local-gate.mjs | 4 ++-- tests/cloudflare-runtime.test.ts | 18 ++++++++++++++++ 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 9bfbd164..187da058 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -18,13 +18,15 @@ The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache. +Canonical Cloudflare boots materialize a named mu-plugin that removes only core's `wp_cron` `init` callback when the runtime policy defines `DISABLE_WP_CRON` as true. Cron spawning is explicitly disabled by this runtime policy because the PHP-WASM/MDI shutdown callback path is not supported. Update and privacy scheduling callbacks remain registered; an explicit durable cron execution contract is separate future scope. + ## MDI Init Diagnostics Every `mdi-*` lifecycle diagnostic materializes the validated packaged canonical MDI seed and the separately managed R2 WordPress runtime artifact in a disposable PHP-WASM runtime. They never acquire a coordinator lease, read or write a canonical R2 revision, or promote state. The old three-option fixture was intentionally removed because it does not represent complete MDI WordPress state. `?phase=canonical-wordpress` performs a full normal WordPress boot with that exact seed and artifact outside the coordinator, then returns WordPress version, memory, and non-sensitive table counts. Its widget-state count includes the `widget_*` options and `sidebars_widgets`. `?phase=canonical-bootstrap-setup` additionally runs the normal bootstrap setup with a probe-only internal password and fixed probe origin, flushes only the disposable runtime, and returns changed-path counts. Neither probe returns password, option, user, or content values. -The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. +The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-wp-cron`, `mdi-init-exclude-privacy-schedule`, `mdi-init-exclude-update-schedule`, `mdi-init-exclude-wp-cron-privacy-schedule`, `mdi-init-exclude-wp-cron-update-schedule`, `mdi-init-exclude-privacy-update-schedule`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group through WordPress's public `remove_action()` API, runs `init` once, and returns the exact removed names, completion, and memory measurements before subsequent WordPress lifecycle work. The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` reports only fixed widget option names, grouped by `get_option()` presence and direct `$wpdb` SQLite-row presence; it returns no option values. `mdi-widgets-get-settings` calls only `get_settings()` on each default factory object, with `mdi-widgets-get-settings-first` and `mdi-widgets-get-settings-second` as fixed halves; each reports completed classes and array-shape dimensions only. `mdi-widgets-register-one` preloads the fixed `widget_text` option, calls that object's `get_settings()`, then calls only `_register_one()` for its fixed instance. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 8d76b479..071e0966 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -265,13 +265,13 @@ async function discardRuntime(runtime: Runtime): Promise { } async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: string, authConstants: Record): Promise { - return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(bucket, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants, bucket), pointer } + return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(bucket, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants, bucket, true), pointer } } async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { if (!env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") const origin = new URL(requestUrl).origin - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env), env.WORDPRESS_STATE_BUCKET) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env), env.WORDPRESS_STATE_BUCKET, true) try { const passwordFile = "/tmp/wordpress-admin-password" runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) @@ -568,7 +568,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise if (phase === "canonical-wordpress" || phase === "canonical-bootstrap-setup") { const canonicalSeed = await packagedCanonicalMarkdownSeed() - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, canonicalSeed, new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, canonicalSeed, new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) try { if (phase === "canonical-bootstrap-setup") { const passwordFile = "/tmp/wp-codebox-canonical-bootstrap-probe-password" @@ -588,6 +588,9 @@ echo json_encode([ 'optionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options}"), 'widgetOptionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options} WHERE option_name LIKE 'widget\\_%'"), 'widgetStateOptionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options} WHERE option_name LIKE 'widget\\_%' OR option_name = 'sidebars_widgets'"), + 'wpCronInitAttached' => false !== has_action('init', 'wp_cron'), + 'updateScheduleInitAttached' => false !== has_action('init', 'wp_schedule_update_checks'), + 'privacyScheduleInitAttached' => false !== has_action('init', 'wp_schedule_delete_old_privacy_export_files'), 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true), ]);` })).text.trim() @@ -1000,6 +1003,7 @@ async function bootWordPressRuntime( siteUrl = SITE_URL, authConstants: Partial> = {}, runtimeBucket?: R2Bucket, + canonicalCronAdapter = false, ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, @@ -1031,6 +1035,7 @@ async function bootWordPressRuntime( materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed) } + if (canonicalCronAdapter) materializeCanonicalCronAdapter(php) } : undefined, beforeDatabaseSetup: databaseSeed ? (php: PHP) => { php.mkdir("/wordpress/wp-content/database") @@ -1058,6 +1063,20 @@ function materializeRuntimeFiles(php: PHP, root: string, files: RuntimeFile[]): } } +function materializeCanonicalCronAdapter(php: PHP): void { + const path = "/wordpress/wp-content/mu-plugins/wp-codebox-canonical-cron-policy.php" + php.mkdir(path.slice(0, path.lastIndexOf("/"))) + php.writeFile(path, new TextEncoder().encode(` { diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 90531d88..939d8d18 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -172,14 +172,14 @@ async function assertCanonicalProbes() { const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json() const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json() const counts = ["canonicalSeedFiles", "postCount", "pageCount", "userCount", "optionCount", "widgetOptionCount", "widgetStateOptionCount", "memoryBytes", "peakMemoryBytes"] - if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19) { + if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== true || wordpress.evidence.privacyScheduleInitAttached !== true) { throw new Error(`Canonical WordPress probe did not boot the complete seed: ${JSON.stringify(wordpress)}`) } const changedCounts = ["createdPathCount", "changedPathCount", "deletedPathCount"] if (setup.completed !== true || typeof setup.evidence?.wordpressVersion !== "string" || changedCounts.some((key) => !Number.isInteger(setup.evidence?.[key])) || setup.evidence.changedPathCount < 1) { throw new Error(`Canonical bootstrap setup probe did not produce ephemeral changes: ${JSON.stringify(setup)}`) } - console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) + console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, cron init disabled while update/privacy scheduling remain registered, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) } async function assertMdiDiagnostics() { diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index ee3b021f..9761480a 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -313,6 +313,24 @@ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed withou assert.match(worker, /function canonicalChangedPathCounts/) }) +test("Cloudflare canonical runtime materializes a conditional cron policy without removing other scheduling callbacks", async () => { + const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const adapter = worker.match(/function materializeCanonicalCronAdapter\(php: PHP\): void \{[\s\S]*?\n\}/)?.[0] + + assert.ok(adapter, "The canonical cron policy materializer is present.") + assert.match(adapter, /wp-content\/mu-plugins\/wp-codebox-canonical-cron-policy\.php/) + assert.match(adapter, /defined\( 'DISABLE_WP_CRON' \) && DISABLE_WP_CRON/) + assert.match(adapter, /remove_action\( 'init', 'wp_cron' \)/) + assert.doesNotMatch(adapter, /wp_schedule_update_checks|wp_schedule_delete_old_privacy_export_files|\$GLOBALS\['wp_filter'\]/) + assert.match(worker, /runtimeBucket\?: R2Bucket,\n canonicalCronAdapter = false,/) + assert.match(worker, /authConstants, bucket, true\), pointer/) + assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) + assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) + assert.match(worker, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) + assert.match(worker, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) + assert.match(worker, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/) +}) + test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object only for leases", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const coordinator = await readFile(new URL("../packages/runtime-cloudflare/src/state-coordinator.ts", import.meta.url), "utf8") From 4803823d4b692a624e9f3cf8b24d4d2df4b61852 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:41:30 -0400 Subject: [PATCH 18/87] Remove disabled cron at init time --- packages/runtime-cloudflare/src/worker.ts | 8 +++++++- tests/cloudflare-runtime.test.ts | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 071e0966..e30782d7 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -1072,7 +1072,13 @@ function materializeCanonicalCronAdapter(php: PHP): void { */ if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) { - remove_action( 'init', 'wp_cron' ); + add_action( + 'init', + static function (): void { + remove_action( 'init', 'wp_cron' ); + }, + PHP_INT_MIN + ); } `)) } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 9761480a..7a491e68 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -320,7 +320,7 @@ test("Cloudflare canonical runtime materializes a conditional cron policy withou assert.ok(adapter, "The canonical cron policy materializer is present.") assert.match(adapter, /wp-content\/mu-plugins\/wp-codebox-canonical-cron-policy\.php/) assert.match(adapter, /defined\( 'DISABLE_WP_CRON' \) && DISABLE_WP_CRON/) - assert.match(adapter, /remove_action\( 'init', 'wp_cron' \)/) + assert.match(adapter, /add_action\([\s\S]*'init'[\s\S]*remove_action\( 'init', 'wp_cron' \)[\s\S]*PHP_INT_MIN/) assert.doesNotMatch(adapter, /wp_schedule_update_checks|wp_schedule_delete_old_privacy_export_files|\$GLOBALS\['wp_filter'\]/) assert.match(worker, /runtimeBucket\?: R2Bucket,\n canonicalCronAdapter = false,/) assert.match(worker, /authConstants, bucket, true\), pointer/) From 7d439cb92f27dca8abb4e9cbc0744246ba7d8424 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:54:50 -0400 Subject: [PATCH 19/87] Patch disabled cron before canonical init --- packages/runtime-cloudflare/README.md | 2 +- packages/runtime-cloudflare/src/worker.ts | 35 ++++++++++------------- tests/cloudflare-runtime.test.ts | 24 ++++++++++------ 3 files changed, 31 insertions(+), 30 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 187da058..5c9613db 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -18,7 +18,7 @@ The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache. -Canonical Cloudflare boots materialize a named mu-plugin that removes only core's `wp_cron` `init` callback when the runtime policy defines `DISABLE_WP_CRON` as true. Cron spawning is explicitly disabled by this runtime policy because the PHP-WASM/MDI shutdown callback path is not supported. Update and privacy scheduling callbacks remain registered; an explicit durable cron execution contract is separate future scope. +Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpress/wp-settings.php` after the R2 WordPress corpus is materialized and before WordPress executes. The patch requires exactly one canonical `do_action( 'init' );` needle, fails closed otherwise, removes only core's `wp_cron` callback through public `remove_action()` when `DISABLE_WP_CRON` is true, then executes the original `init` call once. Cron spawning is explicitly disabled by this runtime policy because the PHP-WASM/MDI shutdown callback path is not supported. Update and privacy scheduling callbacks remain registered; an explicit durable cron execution contract is separate future scope. ## MDI Init Diagnostics diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index e30782d7..52154548 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -1003,7 +1003,7 @@ async function bootWordPressRuntime( siteUrl = SITE_URL, authConstants: Partial> = {}, runtimeBucket?: R2Bucket, - canonicalCronAdapter = false, + shouldPatchCanonicalCronAtInit = false, ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, @@ -1035,7 +1035,7 @@ async function bootWordPressRuntime( materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed) } - if (canonicalCronAdapter) materializeCanonicalCronAdapter(php) + if (shouldPatchCanonicalCronAtInit) patchCanonicalCronAtInit(php) } : undefined, beforeDatabaseSetup: databaseSeed ? (php: PHP) => { php.mkdir("/wordpress/wp-content/database") @@ -1063,24 +1063,19 @@ function materializeRuntimeFiles(php: PHP, root: string, files: RuntimeFile[]): } } -function materializeCanonicalCronAdapter(php: PHP): void { - const path = "/wordpress/wp-content/mu-plugins/wp-codebox-canonical-cron-policy.php" - php.mkdir(path.slice(0, path.lastIndexOf("/"))) - php.writeFile(path, new TextEncoder().encode(` { +test("Cloudflare canonical runtime patches the unique init call in MEMFS without removing other scheduling callbacks", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const adapter = worker.match(/function materializeCanonicalCronAdapter\(php: PHP\): void \{[\s\S]*?\n\}/)?.[0] - - assert.ok(adapter, "The canonical cron policy materializer is present.") - assert.match(adapter, /wp-content\/mu-plugins\/wp-codebox-canonical-cron-policy\.php/) - assert.match(adapter, /defined\( 'DISABLE_WP_CRON' \) && DISABLE_WP_CRON/) - assert.match(adapter, /add_action\([\s\S]*'init'[\s\S]*remove_action\( 'init', 'wp_cron' \)[\s\S]*PHP_INT_MIN/) - assert.doesNotMatch(adapter, /wp_schedule_update_checks|wp_schedule_delete_old_privacy_export_files|\$GLOBALS\['wp_filter'\]/) - assert.match(worker, /runtimeBucket\?: R2Bucket,\n canonicalCronAdapter = false,/) + const patcher = worker.slice(worker.indexOf("function patchCanonicalCronAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) + + assert.ok(patcher.startsWith("function patchCanonicalCronAtInit"), "The canonical cron init patcher is present.") + assert.match(patcher, /const settingsPath = "\/wordpress\/wp-settings\.php"/) + assert.match(patcher, /php\.readFileAsBuffer\(settingsPath\)/) + assert.match(patcher, /firstNeedle === -1 \|\| firstNeedle !== settings\.lastIndexOf\(needle\)/) + assert.match(patcher, /throw new Error\("WordPress canonical cron patch needle was not uniquely found\."\)/) + assert.match(patcher, /defined\( 'DISABLE_WP_CRON' \) && DISABLE_WP_CRON/) + assert.match(patcher, /remove_action\( 'init', 'wp_cron' \)/) + assert.equal((patcher.match(/do_action\( 'init' \);/g) ?? []).length, 1) + assert.match(patcher, /php\.writeFile\(settingsPath/) + assert.doesNotMatch(patcher, /mu-plugins|add_action\(|wp_schedule_update_checks|wp_schedule_delete_old_privacy_export_files|\$GLOBALS\['wp_filter'\]/) + assert.doesNotMatch(worker, /materializeCanonicalCronAdapter|wp-codebox-canonical-cron-policy/) + assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalCronAtInit = false,/) assert.match(worker, /authConstants, bucket, true\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) From 2be2d25f81f4df7ec87b65eb398d964522417f3a Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 03:59:14 -0400 Subject: [PATCH 20/87] Disable browser-request cron scheduling --- packages/runtime-cloudflare/README.md | 2 +- packages/runtime-cloudflare/src/worker.ts | 10 ++++++---- scripts/cloudflare-local-gate.mjs | 4 ++-- tests/cloudflare-runtime.test.ts | 14 ++++++++------ 4 files changed, 17 insertions(+), 13 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 5c9613db..1b509863 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -18,7 +18,7 @@ The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache. -Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpress/wp-settings.php` after the R2 WordPress corpus is materialized and before WordPress executes. The patch requires exactly one canonical `do_action( 'init' );` needle, fails closed otherwise, removes only core's `wp_cron` callback through public `remove_action()` when `DISABLE_WP_CRON` is true, then executes the original `init` call once. Cron spawning is explicitly disabled by this runtime policy because the PHP-WASM/MDI shutdown callback path is not supported. Update and privacy scheduling callbacks remain registered; an explicit durable cron execution contract is separate future scope. +Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpress/wp-settings.php` after the R2 WordPress corpus is materialized and before WordPress executes. The disabled-cron scheduling policy requires exactly one canonical `do_action( 'init' );` needle and fails closed otherwise. When `DISABLE_WP_CRON` is true, it removes only core's `wp_cron`, `wp_schedule_delete_old_privacy_export_files`, and `wp_schedule_update_checks` callbacks through public `remove_action()` before executing the original `init` call once. This prevents browser requests from recreating cron work that cannot execute in the PHP-WASM/MDI shutdown path. Durable cron execution belongs to a separate explicit queue or scheduled-Worker contract. ## MDI Init Diagnostics diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 52154548..fc25d91c 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -1003,7 +1003,7 @@ async function bootWordPressRuntime( siteUrl = SITE_URL, authConstants: Partial> = {}, runtimeBucket?: R2Bucket, - shouldPatchCanonicalCronAtInit = false, + shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit = false, ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, @@ -1035,7 +1035,7 @@ async function bootWordPressRuntime( materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed) } - if (shouldPatchCanonicalCronAtInit) patchCanonicalCronAtInit(php) + if (shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit) patchCanonicalDisabledCronSchedulingPolicyAtInit(php) } : undefined, beforeDatabaseSetup: databaseSeed ? (php: PHP) => { php.mkdir("/wordpress/wp-content/database") @@ -1063,16 +1063,18 @@ function materializeRuntimeFiles(php: PHP, root: string, files: RuntimeFile[]): } } -function patchCanonicalCronAtInit(php: PHP): void { +function patchCanonicalDisabledCronSchedulingPolicyAtInit(php: PHP): void { const settingsPath = "/wordpress/wp-settings.php" const needle = "do_action( 'init' );" const settings = new TextDecoder().decode(php.readFileAsBuffer(settingsPath)) const firstNeedle = settings.indexOf(needle) if (firstNeedle === -1 || firstNeedle !== settings.lastIndexOf(needle)) { - throw new Error("WordPress canonical cron patch needle was not uniquely found.") + throw new Error("WordPress canonical disabled-cron scheduling policy patch needle was not uniquely found.") } const replacement = `if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) { remove_action( 'init', 'wp_cron' ); + remove_action( 'init', 'wp_schedule_delete_old_privacy_export_files' ); + remove_action( 'init', 'wp_schedule_update_checks' ); } ${needle}` php.writeFile(settingsPath, new TextEncoder().encode(`${settings.slice(0, firstNeedle)}${replacement}${settings.slice(firstNeedle + needle.length)}`)) diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 939d8d18..75fd8207 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -172,14 +172,14 @@ async function assertCanonicalProbes() { const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json() const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json() const counts = ["canonicalSeedFiles", "postCount", "pageCount", "userCount", "optionCount", "widgetOptionCount", "widgetStateOptionCount", "memoryBytes", "peakMemoryBytes"] - if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== true || wordpress.evidence.privacyScheduleInitAttached !== true) { + if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== false || wordpress.evidence.privacyScheduleInitAttached !== false) { throw new Error(`Canonical WordPress probe did not boot the complete seed: ${JSON.stringify(wordpress)}`) } const changedCounts = ["createdPathCount", "changedPathCount", "deletedPathCount"] if (setup.completed !== true || typeof setup.evidence?.wordpressVersion !== "string" || changedCounts.some((key) => !Number.isInteger(setup.evidence?.[key])) || setup.evidence.changedPathCount < 1) { throw new Error(`Canonical bootstrap setup probe did not produce ephemeral changes: ${JSON.stringify(setup)}`) } - console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, cron init disabled while update/privacy scheduling remain registered, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) + console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) } async function assertMdiDiagnostics() { diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 796de7a8..8961b014 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -313,22 +313,24 @@ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed withou assert.match(worker, /function canonicalChangedPathCounts/) }) -test("Cloudflare canonical runtime patches the unique init call in MEMFS without removing other scheduling callbacks", async () => { +test("Cloudflare canonical runtime patches the unique init call with the disabled-cron scheduling policy", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const patcher = worker.slice(worker.indexOf("function patchCanonicalCronAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) + const patcher = worker.slice(worker.indexOf("function patchCanonicalDisabledCronSchedulingPolicyAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) - assert.ok(patcher.startsWith("function patchCanonicalCronAtInit"), "The canonical cron init patcher is present.") + assert.ok(patcher.startsWith("function patchCanonicalDisabledCronSchedulingPolicyAtInit"), "The canonical disabled-cron scheduling policy patcher is present.") assert.match(patcher, /const settingsPath = "\/wordpress\/wp-settings\.php"/) assert.match(patcher, /php\.readFileAsBuffer\(settingsPath\)/) assert.match(patcher, /firstNeedle === -1 \|\| firstNeedle !== settings\.lastIndexOf\(needle\)/) - assert.match(patcher, /throw new Error\("WordPress canonical cron patch needle was not uniquely found\."\)/) + assert.match(patcher, /throw new Error\("WordPress canonical disabled-cron scheduling policy patch needle was not uniquely found\."\)/) assert.match(patcher, /defined\( 'DISABLE_WP_CRON' \) && DISABLE_WP_CRON/) assert.match(patcher, /remove_action\( 'init', 'wp_cron' \)/) + assert.match(patcher, /remove_action\( 'init', 'wp_schedule_delete_old_privacy_export_files' \)/) + assert.match(patcher, /remove_action\( 'init', 'wp_schedule_update_checks' \)/) assert.equal((patcher.match(/do_action\( 'init' \);/g) ?? []).length, 1) assert.match(patcher, /php\.writeFile\(settingsPath/) - assert.doesNotMatch(patcher, /mu-plugins|add_action\(|wp_schedule_update_checks|wp_schedule_delete_old_privacy_export_files|\$GLOBALS\['wp_filter'\]/) + assert.doesNotMatch(patcher, /mu-plugins|add_action\(|WP_Site_Health|wp_schedule_site_health_cron|\$GLOBALS\['wp_filter'\]/) assert.doesNotMatch(worker, /materializeCanonicalCronAdapter|wp-codebox-canonical-cron-policy/) - assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalCronAtInit = false,/) + assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit = false,/) assert.match(worker, /authConstants, bucket, true\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) From ba72370793f690967196f0e31233e132503e4bf6 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 04:04:57 -0400 Subject: [PATCH 21/87] Probe canonical lifecycle boundaries --- packages/runtime-cloudflare/src/worker.ts | 47 ++++++++++++++++++++++- scripts/cloudflare-local-gate.mjs | 17 +++++++- tests/cloudflare-runtime.test.ts | 23 +++++++++++ 3 files changed, 85 insertions(+), 2 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index fc25d91c..f52fcd95 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -566,6 +566,16 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } + if (["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"].includes(phase)) { + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) + try { + const evidence = (await runtime.php.run({ code: canonicalLifecycleProbeCode(phase) })).text.trim() + return probeResponse(phase, JSON.parse(evidence) as Record) + } finally { + runtime.php.exit() + } + } + if (phase === "canonical-wordpress" || phase === "canonical-bootstrap-setup") { const canonicalSeed = await packagedCanonicalMarkdownSeed() const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, canonicalSeed, new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) @@ -993,6 +1003,41 @@ file_put_contents($settings_path, str_replace($needle, $replacement, $settings)) require '/wordpress/wp-load.php';` } +function canonicalLifecycleProbeCode(phase: string): string { + const stops: Record = { + "canonical-current-user": { needle: "// Set up current user." }, + "canonical-init": { needle: "do_action( 'init' );", after: true }, + "canonical-site-status": { needle: "// Check site status." }, + "canonical-wp-loaded": { needle: "do_action( 'wp_loaded' );", after: true }, + } + const stop = stops[phase] + if (!stop) throw new Error(`Unknown canonical lifecycle probe phase: ${phase}`) + + return ` $wp_version, + 'bootstrapPhase' => '${phase}', + 'completed' => true, + 'memoryBytes' => memory_get_usage(true), + 'peakMemoryBytes' => memory_get_peak_usage(true), + 'wpCronInitAttached' => false !== has_action('init', 'wp_cron'), + 'updateScheduleInitAttached' => false !== has_action('init', 'wp_schedule_update_checks'), + 'privacyScheduleInitAttached' => false !== has_action('init', 'wp_schedule_delete_old_privacy_export_files'), +)); +return; +PHP; +$replacement = ${stop.after ? "$needle . \"\\n\" . \$stop" : "$stop . \"\\n\" . $needle"}; +file_put_contents($settings_path, str_replace($needle, $replacement, $settings)); +require '/wordpress/wp-load.php';` +} + async function bootWordPressRuntime( wordpressInstallMode: WordPressInstallMode = "install-from-existing-files", includeSqlite = true, @@ -1195,6 +1240,6 @@ function instantiatePrecompiledWasm(module: WebAssembly.Module) { return (imports: WebAssembly.Imports, receiveInstance: (instance: WebAssembly.Instance, wasmModule: WebAssembly.Module) => void) => receiveInstance(new WebAssembly.Instance(module, imports), module) } -function probeResponse(phase: string, evidence: Record): Response { +function probeResponse(phase: string, evidence: Record): Response { return Response.json({ schema: "wp-codebox/cloudflare-boot-probe/v1", phase, completed: true, evidence }) } diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 75fd8207..eda01fb9 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -171,6 +171,14 @@ async function assertHealthResponse() { async function assertCanonicalProbes() { const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json() const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json() + const lifecyclePhases = ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"] + const lifecycle = await Promise.all(lifecyclePhases.map(async (phase) => { + const response = await request(`${origin}/?phase=${phase}`) + const body = await response.text() + assertNoPhpDiagnostics(body, phase) + if (!response.ok) throw new Error(`${phase} failed with ${response.status}: ${body}`) + return JSON.parse(body) + })) const counts = ["canonicalSeedFiles", "postCount", "pageCount", "userCount", "optionCount", "widgetOptionCount", "widgetStateOptionCount", "memoryBytes", "peakMemoryBytes"] if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== false || wordpress.evidence.privacyScheduleInitAttached !== false) { throw new Error(`Canonical WordPress probe did not boot the complete seed: ${JSON.stringify(wordpress)}`) @@ -179,7 +187,14 @@ async function assertCanonicalProbes() { if (setup.completed !== true || typeof setup.evidence?.wordpressVersion !== "string" || changedCounts.some((key) => !Number.isInteger(setup.evidence?.[key])) || setup.evidence.changedPathCount < 1) { throw new Error(`Canonical bootstrap setup probe did not produce ephemeral changes: ${JSON.stringify(setup)}`) } - console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) + for (const probe of lifecycle) { + const evidence = probe.evidence + const schedulingAttached = probe.phase === "canonical-current-user" + if (probe.completed !== true || !lifecyclePhases.includes(probe.phase) || evidence?.bootstrapPhase !== probe.phase || typeof evidence?.wordpressVersion !== "string" || !Number.isInteger(evidence?.memoryBytes) || !Number.isInteger(evidence?.peakMemoryBytes) || evidence?.wpCronInitAttached !== schedulingAttached || evidence?.updateScheduleInitAttached !== schedulingAttached || evidence?.privacyScheduleInitAttached !== schedulingAttached) { + throw new Error(`Canonical lifecycle probe did not stop at its bounded phase: ${JSON.stringify(probe)}`) + } + } + console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${lifecyclePhases.join(", ")} completed, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) } async function assertMdiDiagnostics() { diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 8961b014..2c7b275f 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -313,6 +313,29 @@ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed withou assert.match(worker, /function canonicalChangedPathCounts/) }) +test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch with unique bounded stops", async () => { + const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) + const lifecycle = worker.slice(worker.indexOf("function canonicalLifecycleProbeCode"), worker.indexOf("\nasync function bootWordPressRuntime")) + + for (const phase of ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"]) { + assert.match(probes, new RegExp(`"${phase}"`)) + assert.match(lifecycle, new RegExp(`"${phase}"`)) + } + assert.match(probes, /packagedCanonicalMarkdownSeed\(\), new Uint8Array\(markdownPrimaryBootstrapIndex\), "https:\/\/canonical-probe\.invalid", \{\}, bucket, true\)/) + assert.match(lifecycle, /substr_count\(\$settings, \$needle\) !== 1/) + assert.match(lifecycle, /WordPress canonical lifecycle probe needle was not uniquely found\./) + assert.match(lifecycle, /'completed' => true/) + assert.match(lifecycle, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) + assert.match(lifecycle, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) + assert.match(lifecycle, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/) + assert.doesNotMatch(lifecycle, /get_option|\$wpdb|\$_COOKIE|AUTH_KEY|password|token/i) + assert.equal((lifecycle.match(/do_action\( 'init' \);/g) ?? []).length, 1) + assert.equal((lifecycle.match(/do_action\( 'wp_loaded' \);/g) ?? []).length, 1) + assert.match(lifecycle, /stop\.after \? "\$needle/) + assert.match(lifecycle, /: "\$stop/) +}) + test("Cloudflare canonical runtime patches the unique init call with the disabled-cron scheduling policy", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const patcher = worker.slice(worker.indexOf("function patchCanonicalDisabledCronSchedulingPolicyAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) From e909a0126b8e5e6fb4e48d95563a297558b32944 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 04:15:23 -0400 Subject: [PATCH 22/87] Bisect canonical wp-loaded callbacks --- packages/runtime-cloudflare/src/worker.ts | 71 ++++++++++++++++++++++- scripts/cloudflare-local-gate.mjs | 17 +++++- tests/cloudflare-runtime.test.ts | 14 ++++- 3 files changed, 97 insertions(+), 5 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index f52fcd95..6f489fca 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -566,7 +566,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"].includes(phase)) { + if (["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) try { const evidence = (await runtime.php.run({ code: canonicalLifecycleProbeCode(phase) })).text.trim() @@ -1004,15 +1004,81 @@ require '/wordpress/wp-load.php';` } function canonicalLifecycleProbeCode(phase: string): string { + const wpLoadedExclusions: Record = { + "canonical-wp-loaded-exclude-rewrite-flush": { identifiers: ["WP_Rewrite::flush_rules", "playground_maybe_flush_rewrite_rules"], retain: false }, + "canonical-wp-loaded-exclude-core-template-header": { identifiers: ["_add_template_loader_filters", "_custom_header_background_just_in_time", "_custom_logo_header_styles"], retain: false }, + "canonical-wp-loaded-exclude-playground": { identifiers: ["playground_maybe_flush_rewrite_rules", "playground_save_wp_env_info"], retain: false }, + "canonical-wp-loaded-exclude-wp-cron": { identifiers: ["_wp_cron"], retain: false }, + "canonical-wp-loaded-exclude-all": { identifiers: [], retain: true }, + } + const wpLoadedNeedle = "do_action( 'wp_loaded' );" const stops: Record = { "canonical-current-user": { needle: "// Set up current user." }, "canonical-init": { needle: "do_action( 'init' );", after: true }, "canonical-site-status": { needle: "// Check site status." }, - "canonical-wp-loaded": { needle: "do_action( 'wp_loaded' );", after: true }, + "canonical-wp-loaded": { needle: wpLoadedNeedle, after: true }, + "canonical-wp-loaded-callbacks": { needle: wpLoadedNeedle }, + ...Object.fromEntries(Object.keys(wpLoadedExclusions).map((name) => [name, { needle: wpLoadedNeedle }])), } const stop = stops[phase] if (!stop) throw new Error(`Unknown canonical lifecycle probe phase: ${phase}`) + const wpLoadedProbe = phase === "canonical-wp-loaded-callbacks" || wpLoadedExclusions[phase] + const exclusion = wpLoadedExclusions[phase] + const wpLoadedStop = !wpLoadedProbe ? "" : `function wp_codebox_canonical_wp_loaded_callback_identifier($callback) { + if (is_string($callback)) return $callback; + if ($callback instanceof Closure) return 'Closure'; + if (is_array($callback) && count($callback) === 2 && is_string($callback[1])) { + $class = is_object($callback[0]) ? get_class($callback[0]) : $callback[0]; + return is_string($class) ? $class . '::' . $callback[1] : 'Closure'; + } + if (is_object($callback) && method_exists($callback, '__invoke')) return get_class($callback) . '::__invoke'; + return 'Closure'; +} +function wp_codebox_canonical_wp_loaded_inventory() { + $inventory = array(); + $hook = isset($GLOBALS['wp_filter']['wp_loaded']) ? $GLOBALS['wp_filter']['wp_loaded'] : null; + if (!$hook || !isset($hook->callbacks) || !is_array($hook->callbacks)) return $inventory; + $remaining = 100; + foreach ($hook->callbacks as $priority => $callbacks) { + if (0 === $remaining) break; + $identifiers = array(); + foreach ($callbacks as $registered) if (isset($registered['function'])) $identifiers[] = wp_codebox_canonical_wp_loaded_callback_identifier($registered['function']); + sort($identifiers, SORT_STRING); + $identifiers = array_slice($identifiers, 0, $remaining); + if ($identifiers) { + $inventory[(string) $priority] = $identifiers; + $remaining -= count($identifiers); + } + } + ksort($inventory, SORT_NUMERIC); + return $inventory; +} +function wp_codebox_canonical_wp_loaded_remove_snapshot($identifiers, $retain) { + $removed = array(); + $hook = isset($GLOBALS['wp_filter']['wp_loaded']) ? $GLOBALS['wp_filter']['wp_loaded'] : null; + if (!$hook || !isset($hook->callbacks) || !is_array($hook->callbacks)) return $removed; + $callbacks_by_priority = $hook->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($callbacks as $registered) { + if (!isset($registered['function'])) continue; + $identifier = wp_codebox_canonical_wp_loaded_callback_identifier($registered['function']); + $matches = in_array($identifier, $identifiers, true); + if (($retain && !$matches) || (!$retain && $matches)) { + if (remove_action('wp_loaded', $registered['function'], (int) $priority)) $removed[] = $identifier; + } + } + } + sort($removed, SORT_STRING); + return $removed; +} +$inventory = wp_codebox_canonical_wp_loaded_inventory(); +${phase === "canonical-wp-loaded-callbacks" ? `echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => '${phase}', 'callbacks' => $inventory, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); +return;` : `$removed = wp_codebox_canonical_wp_loaded_remove_snapshot(${JSON.stringify(exclusion?.identifiers ?? [])}, ${exclusion?.retain ? "true" : "false"}); +$memory_before = memory_get_usage(true); +do_action('wp_loaded'); +echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => '${phase}', 'completed' => true, 'callbacks' => $inventory, 'removedCallbacks' => $removed, 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); +return;`}` return ` $wp_version, 'bootstrapPhase' => '${phase}', diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index eda01fb9..d5b9a702 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -171,7 +171,14 @@ async function assertHealthResponse() { async function assertCanonicalProbes() { const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json() const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json() - const lifecyclePhases = ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"] + const lifecyclePhases = ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"] + const expectedRemovedCallbacks = { + "canonical-wp-loaded-exclude-rewrite-flush": ["WP_Rewrite::flush_rules"], + "canonical-wp-loaded-exclude-core-template-header": ["_add_template_loader_filters", "_custom_header_background_just_in_time"], + "canonical-wp-loaded-exclude-playground": ["playground_save_wp_env_info"], + "canonical-wp-loaded-exclude-wp-cron": [], + "canonical-wp-loaded-exclude-all": ["WP_Rewrite::flush_rules", "_add_template_loader_filters", "_custom_header_background_just_in_time", "playground_save_wp_env_info"], + } const lifecycle = await Promise.all(lifecyclePhases.map(async (phase) => { const response = await request(`${origin}/?phase=${phase}`) const body = await response.text() @@ -190,9 +197,15 @@ async function assertCanonicalProbes() { for (const probe of lifecycle) { const evidence = probe.evidence const schedulingAttached = probe.phase === "canonical-current-user" - if (probe.completed !== true || !lifecyclePhases.includes(probe.phase) || evidence?.bootstrapPhase !== probe.phase || typeof evidence?.wordpressVersion !== "string" || !Number.isInteger(evidence?.memoryBytes) || !Number.isInteger(evidence?.peakMemoryBytes) || evidence?.wpCronInitAttached !== schedulingAttached || evidence?.updateScheduleInitAttached !== schedulingAttached || evidence?.privacyScheduleInitAttached !== schedulingAttached) { + const isWpLoadedProbe = probe.phase.startsWith("canonical-wp-loaded-") + const lifecycleEvidenceIsValid = !isWpLoadedProbe && evidence?.wpCronInitAttached === schedulingAttached && evidence?.updateScheduleInitAttached === schedulingAttached && evidence?.privacyScheduleInitAttached === schedulingAttached + const wpLoadedEvidenceIsValid = isWpLoadedProbe && typeof evidence?.callbacks === "object" && evidence.callbacks !== null && !Array.isArray(evidence.callbacks) && (probe.phase === "canonical-wp-loaded-callbacks" || (evidence?.completed === true && Array.isArray(evidence?.removedCallbacks) && Number.isInteger(evidence?.memoryBeforeBytes))) + if (probe.completed !== true || !lifecyclePhases.includes(probe.phase) || evidence?.bootstrapPhase !== probe.phase || typeof evidence?.wordpressVersion !== "string" || !Number.isInteger(evidence?.memoryBytes) || !Number.isInteger(evidence?.peakMemoryBytes) || (!lifecycleEvidenceIsValid && !wpLoadedEvidenceIsValid)) { throw new Error(`Canonical lifecycle probe did not stop at its bounded phase: ${JSON.stringify(probe)}`) } + if (Object.hasOwn(expectedRemovedCallbacks, probe.phase) && JSON.stringify(evidence.removedCallbacks) !== JSON.stringify(expectedRemovedCallbacks[probe.phase])) { + throw new Error(`Canonical wp_loaded exclusion removed an unexpected callback set: ${JSON.stringify(probe)}`) + } } console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${lifecyclePhases.join(", ")} completed, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 2c7b275f..bfde3246 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -318,9 +318,10 @@ test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) const lifecycle = worker.slice(worker.indexOf("function canonicalLifecycleProbeCode"), worker.indexOf("\nasync function bootWordPressRuntime")) - for (const phase of ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded"]) { + for (const phase of ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"]) { assert.match(probes, new RegExp(`"${phase}"`)) assert.match(lifecycle, new RegExp(`"${phase}"`)) + assert.deepEqual(routeWorkerRequest(new Request(`https://worker.example/?phase=${phase}`)), { kind: "probe", phase }) } assert.match(probes, /packagedCanonicalMarkdownSeed\(\), new Uint8Array\(markdownPrimaryBootstrapIndex\), "https:\/\/canonical-probe\.invalid", \{\}, bucket, true\)/) assert.match(lifecycle, /substr_count\(\$settings, \$needle\) !== 1/) @@ -329,6 +330,17 @@ test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch assert.match(lifecycle, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) assert.match(lifecycle, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) assert.match(lifecycle, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/) + assert.match(lifecycle, /function wp_codebox_canonical_wp_loaded_callback_identifier/) + assert.match(lifecycle, /function wp_codebox_canonical_wp_loaded_inventory/) + assert.match(lifecycle, /\$remaining = 100/) + assert.match(lifecycle, /function wp_codebox_canonical_wp_loaded_remove_snapshot/) + assert.match(lifecycle, /remove_action\('wp_loaded', \$registered\['function'\], \(int\) \$priority\)/) + assert.match(lifecycle, /"WP_Rewrite::flush_rules", "playground_maybe_flush_rewrite_rules"/) + assert.match(lifecycle, /"_add_template_loader_filters", "_custom_header_background_just_in_time", "_custom_logo_header_styles"/) + assert.match(lifecycle, /"playground_maybe_flush_rewrite_rules", "playground_save_wp_env_info"/) + assert.match(lifecycle, /"_wp_cron"/) + assert.match(lifecycle, /canonical-wp-loaded-exclude-all": \{ identifiers: \[\], retain: true \}/) + assert.match(lifecycle, /do_action\('wp_loaded'\)/) assert.doesNotMatch(lifecycle, /get_option|\$wpdb|\$_COOKIE|AUTH_KEY|password|token/i) assert.equal((lifecycle.match(/do_action\( 'init' \);/g) ?? []).length, 1) assert.equal((lifecycle.match(/do_action\( 'wp_loaded' \);/g) ?? []).length, 1) From f284345061720823adc6d4696bcf8de1fc16c8b8 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 04:18:55 -0400 Subject: [PATCH 23/87] Add short canonical lifecycle probes --- packages/runtime-cloudflare/src/worker.ts | 18 +++++++++++++----- scripts/cloudflare-local-gate.mjs | 12 ++++++------ tests/cloudflare-runtime.test.ts | 13 +++++++++++++ 3 files changed, 32 insertions(+), 11 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 6f489fca..de6d437a 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -566,7 +566,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"].includes(phase)) { + if (canonicalLifecyclePhaseAliases[phase] || ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) try { const evidence = (await runtime.php.run({ code: canonicalLifecycleProbeCode(phase) })).text.trim() @@ -1003,7 +1003,15 @@ file_put_contents($settings_path, str_replace($needle, $replacement, $settings)) require '/wordpress/wp-load.php';` } +const canonicalLifecyclePhaseAliases: Record = { + "canon-wpl-no-rewrite": "canonical-wp-loaded-exclude-rewrite-flush", + "canon-wpl-no-core": "canonical-wp-loaded-exclude-core-template-header", + "canon-wpl-no-playground": "canonical-wp-loaded-exclude-playground", + "canon-wpl-no-cron": "canonical-wp-loaded-exclude-wp-cron", +} + function canonicalLifecycleProbeCode(phase: string): string { + const canonicalPhase = canonicalLifecyclePhaseAliases[phase] ?? phase const wpLoadedExclusions: Record = { "canonical-wp-loaded-exclude-rewrite-flush": { identifiers: ["WP_Rewrite::flush_rules", "playground_maybe_flush_rewrite_rules"], retain: false }, "canonical-wp-loaded-exclude-core-template-header": { identifiers: ["_add_template_loader_filters", "_custom_header_background_just_in_time", "_custom_logo_header_styles"], retain: false }, @@ -1020,11 +1028,11 @@ function canonicalLifecycleProbeCode(phase: string): string { "canonical-wp-loaded-callbacks": { needle: wpLoadedNeedle }, ...Object.fromEntries(Object.keys(wpLoadedExclusions).map((name) => [name, { needle: wpLoadedNeedle }])), } - const stop = stops[phase] + const stop = stops[canonicalPhase] if (!stop) throw new Error(`Unknown canonical lifecycle probe phase: ${phase}`) - const wpLoadedProbe = phase === "canonical-wp-loaded-callbacks" || wpLoadedExclusions[phase] - const exclusion = wpLoadedExclusions[phase] + const wpLoadedProbe = canonicalPhase === "canonical-wp-loaded-callbacks" || wpLoadedExclusions[canonicalPhase] + const exclusion = wpLoadedExclusions[canonicalPhase] const wpLoadedStop = !wpLoadedProbe ? "" : `function wp_codebox_canonical_wp_loaded_callback_identifier($callback) { if (is_string($callback)) return $callback; if ($callback instanceof Closure) return 'Closure'; @@ -1073,7 +1081,7 @@ function wp_codebox_canonical_wp_loaded_remove_snapshot($identifiers, $retain) { return $removed; } $inventory = wp_codebox_canonical_wp_loaded_inventory(); -${phase === "canonical-wp-loaded-callbacks" ? `echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => '${phase}', 'callbacks' => $inventory, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); +${canonicalPhase === "canonical-wp-loaded-callbacks" ? `echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => '${phase}', 'callbacks' => $inventory, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); return;` : `$removed = wp_codebox_canonical_wp_loaded_remove_snapshot(${JSON.stringify(exclusion?.identifiers ?? [])}, ${exclusion?.retain ? "true" : "false"}); $memory_before = memory_get_usage(true); do_action('wp_loaded'); diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index d5b9a702..c5eedeba 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -171,12 +171,12 @@ async function assertHealthResponse() { async function assertCanonicalProbes() { const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json() const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json() - const lifecyclePhases = ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"] + const lifecyclePhases = ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded-callbacks", "canon-wpl-no-rewrite", "canon-wpl-no-core", "canon-wpl-no-playground", "canon-wpl-no-cron", "canonical-wp-loaded-exclude-all"] const expectedRemovedCallbacks = { - "canonical-wp-loaded-exclude-rewrite-flush": ["WP_Rewrite::flush_rules"], - "canonical-wp-loaded-exclude-core-template-header": ["_add_template_loader_filters", "_custom_header_background_just_in_time"], - "canonical-wp-loaded-exclude-playground": ["playground_save_wp_env_info"], - "canonical-wp-loaded-exclude-wp-cron": [], + "canon-wpl-no-rewrite": ["WP_Rewrite::flush_rules"], + "canon-wpl-no-core": ["_add_template_loader_filters", "_custom_header_background_just_in_time"], + "canon-wpl-no-playground": ["playground_save_wp_env_info"], + "canon-wpl-no-cron": [], "canonical-wp-loaded-exclude-all": ["WP_Rewrite::flush_rules", "_add_template_loader_filters", "_custom_header_background_just_in_time", "playground_save_wp_env_info"], } const lifecycle = await Promise.all(lifecyclePhases.map(async (phase) => { @@ -197,7 +197,7 @@ async function assertCanonicalProbes() { for (const probe of lifecycle) { const evidence = probe.evidence const schedulingAttached = probe.phase === "canonical-current-user" - const isWpLoadedProbe = probe.phase.startsWith("canonical-wp-loaded-") + const isWpLoadedProbe = probe.phase.startsWith("canonical-wp-loaded-") || probe.phase.startsWith("canon-wpl-no-") const lifecycleEvidenceIsValid = !isWpLoadedProbe && evidence?.wpCronInitAttached === schedulingAttached && evidence?.updateScheduleInitAttached === schedulingAttached && evidence?.privacyScheduleInitAttached === schedulingAttached const wpLoadedEvidenceIsValid = isWpLoadedProbe && typeof evidence?.callbacks === "object" && evidence.callbacks !== null && !Array.isArray(evidence.callbacks) && (probe.phase === "canonical-wp-loaded-callbacks" || (evidence?.completed === true && Array.isArray(evidence?.removedCallbacks) && Number.isInteger(evidence?.memoryBeforeBytes))) if (probe.completed !== true || !lifecyclePhases.includes(probe.phase) || evidence?.bootstrapPhase !== probe.phase || typeof evidence?.wordpressVersion !== "string" || !Number.isInteger(evidence?.memoryBytes) || !Number.isInteger(evidence?.peakMemoryBytes) || (!lifecycleEvidenceIsValid && !wpLoadedEvidenceIsValid)) { diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index bfde3246..f13fbb39 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -317,12 +317,25 @@ test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) const lifecycle = worker.slice(worker.indexOf("function canonicalLifecycleProbeCode"), worker.indexOf("\nasync function bootWordPressRuntime")) + const aliases = { + "canon-wpl-no-rewrite": "canonical-wp-loaded-exclude-rewrite-flush", + "canon-wpl-no-core": "canonical-wp-loaded-exclude-core-template-header", + "canon-wpl-no-playground": "canonical-wp-loaded-exclude-playground", + "canon-wpl-no-cron": "canonical-wp-loaded-exclude-wp-cron", + } for (const phase of ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"]) { assert.match(probes, new RegExp(`"${phase}"`)) assert.match(lifecycle, new RegExp(`"${phase}"`)) assert.deepEqual(routeWorkerRequest(new Request(`https://worker.example/?phase=${phase}`)), { kind: "probe", phase }) } + for (const [alias, phase] of Object.entries(aliases)) { + assert.ok(alias.length <= 24) + assert.match(worker, new RegExp(`"${alias}"`)) + assert.match(worker, new RegExp(`"${alias}": "${phase}"`)) + assert.deepEqual(routeWorkerRequest(new Request(`https://worker.example/?phase=${alias}`)), { kind: "probe", phase: alias }) + } + assert.match(lifecycle, /const canonicalPhase = canonicalLifecyclePhaseAliases\[phase\] \?\? phase/) assert.match(probes, /packagedCanonicalMarkdownSeed\(\), new Uint8Array\(markdownPrimaryBootstrapIndex\), "https:\/\/canonical-probe\.invalid", \{\}, bucket, true\)/) assert.match(lifecycle, /substr_count\(\$settings, \$needle\) !== 1/) assert.match(lifecycle, /WordPress canonical lifecycle probe needle was not uniquely found\./) From 09a9bff31207e56f749f463135be6560f2a3cdc9 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 04:23:23 -0400 Subject: [PATCH 24/87] Add direct wp-loaded probe aliases --- packages/runtime-cloudflare/src/worker.ts | 6 +++++- tests/cloudflare-runtime.test.ts | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index de6d437a..0b8696f2 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -566,7 +566,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (canonicalLifecyclePhaseAliases[phase] || ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"].includes(phase)) { + if (phase === "cwr" || phase === "cwc" || phase === "cwp" || phase === "cwcron" || canonicalLifecyclePhaseAliases[phase] || ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) try { const evidence = (await runtime.php.run({ code: canonicalLifecycleProbeCode(phase) })).text.trim() @@ -1004,6 +1004,10 @@ require '/wordpress/wp-load.php';` } const canonicalLifecyclePhaseAliases: Record = { + cwr: "canonical-wp-loaded-exclude-rewrite-flush", + cwc: "canonical-wp-loaded-exclude-core-template-header", + cwp: "canonical-wp-loaded-exclude-playground", + cwcron: "canonical-wp-loaded-exclude-wp-cron", "canon-wpl-no-rewrite": "canonical-wp-loaded-exclude-rewrite-flush", "canon-wpl-no-core": "canonical-wp-loaded-exclude-core-template-header", "canon-wpl-no-playground": "canonical-wp-loaded-exclude-playground", diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index f13fbb39..176e4f78 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -318,6 +318,10 @@ test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) const lifecycle = worker.slice(worker.indexOf("function canonicalLifecycleProbeCode"), worker.indexOf("\nasync function bootWordPressRuntime")) const aliases = { + cwr: "canonical-wp-loaded-exclude-rewrite-flush", + cwc: "canonical-wp-loaded-exclude-core-template-header", + cwp: "canonical-wp-loaded-exclude-playground", + cwcron: "canonical-wp-loaded-exclude-wp-cron", "canon-wpl-no-rewrite": "canonical-wp-loaded-exclude-rewrite-flush", "canon-wpl-no-core": "canonical-wp-loaded-exclude-core-template-header", "canon-wpl-no-playground": "canonical-wp-loaded-exclude-playground", @@ -332,7 +336,7 @@ test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch for (const [alias, phase] of Object.entries(aliases)) { assert.ok(alias.length <= 24) assert.match(worker, new RegExp(`"${alias}"`)) - assert.match(worker, new RegExp(`"${alias}": "${phase}"`)) + assert.match(worker, new RegExp(`(?:"${alias}"|${alias}): "${phase}"`)) assert.deepEqual(routeWorkerRequest(new Request(`https://worker.example/?phase=${alias}`)), { kind: "probe", phase: alias }) } assert.match(lifecycle, /const canonicalPhase = canonicalLifecyclePhaseAliases\[phase\] \?\? phase/) From 8c1b797f1a8b3391c1b2ab5bb31d702948189a50 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 04:28:05 -0400 Subject: [PATCH 25/87] Probe rewrite rule persistence --- packages/runtime-cloudflare/src/worker.ts | 5 ++++- tests/cloudflare-runtime.test.ts | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 0b8696f2..b921267a 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -566,7 +566,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (phase === "cwr" || phase === "cwc" || phase === "cwp" || phase === "cwcron" || canonicalLifecyclePhaseAliases[phase] || ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"].includes(phase)) { + if (phase === "cwr" || phase === "cwc" || phase === "cwp" || phase === "cwcron" || phase === "cwu" || canonicalLifecyclePhaseAliases[phase] || ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all", "canonical-wp-loaded-suppress-rewrite-persist"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) try { const evidence = (await runtime.php.run({ code: canonicalLifecycleProbeCode(phase) })).text.trim() @@ -1008,6 +1008,7 @@ const canonicalLifecyclePhaseAliases: Record = { cwc: "canonical-wp-loaded-exclude-core-template-header", cwp: "canonical-wp-loaded-exclude-playground", cwcron: "canonical-wp-loaded-exclude-wp-cron", + cwu: "canonical-wp-loaded-suppress-rewrite-persist", "canon-wpl-no-rewrite": "canonical-wp-loaded-exclude-rewrite-flush", "canon-wpl-no-core": "canonical-wp-loaded-exclude-core-template-header", "canon-wpl-no-playground": "canonical-wp-loaded-exclude-playground", @@ -1022,6 +1023,7 @@ function canonicalLifecycleProbeCode(phase: string): string { "canonical-wp-loaded-exclude-playground": { identifiers: ["playground_maybe_flush_rewrite_rules", "playground_save_wp_env_info"], retain: false }, "canonical-wp-loaded-exclude-wp-cron": { identifiers: ["_wp_cron"], retain: false }, "canonical-wp-loaded-exclude-all": { identifiers: [], retain: true }, + "canonical-wp-loaded-suppress-rewrite-persist": { identifiers: [], retain: false }, } const wpLoadedNeedle = "do_action( 'wp_loaded' );" const stops: Record = { @@ -1087,6 +1089,7 @@ function wp_codebox_canonical_wp_loaded_remove_snapshot($identifiers, $retain) { $inventory = wp_codebox_canonical_wp_loaded_inventory(); ${canonicalPhase === "canonical-wp-loaded-callbacks" ? `echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => '${phase}', 'callbacks' => $inventory, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); return;` : `$removed = wp_codebox_canonical_wp_loaded_remove_snapshot(${JSON.stringify(exclusion?.identifiers ?? [])}, ${exclusion?.retain ? "true" : "false"}); +${canonicalPhase === "canonical-wp-loaded-suppress-rewrite-persist" ? "add_filter('pre_update_option_rewrite_rules', static function($value, $old_value) { return $old_value; }, PHP_INT_MAX, 2);" : ""} $memory_before = memory_get_usage(true); do_action('wp_loaded'); echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => '${phase}', 'completed' => true, 'callbacks' => $inventory, 'removedCallbacks' => $removed, 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 176e4f78..3bee3cc7 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -322,17 +322,19 @@ test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch cwc: "canonical-wp-loaded-exclude-core-template-header", cwp: "canonical-wp-loaded-exclude-playground", cwcron: "canonical-wp-loaded-exclude-wp-cron", + cwu: "canonical-wp-loaded-suppress-rewrite-persist", "canon-wpl-no-rewrite": "canonical-wp-loaded-exclude-rewrite-flush", "canon-wpl-no-core": "canonical-wp-loaded-exclude-core-template-header", "canon-wpl-no-playground": "canonical-wp-loaded-exclude-playground", "canon-wpl-no-cron": "canonical-wp-loaded-exclude-wp-cron", } - for (const phase of ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all"]) { + for (const phase of ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all", "canonical-wp-loaded-suppress-rewrite-persist"]) { assert.match(probes, new RegExp(`"${phase}"`)) assert.match(lifecycle, new RegExp(`"${phase}"`)) assert.deepEqual(routeWorkerRequest(new Request(`https://worker.example/?phase=${phase}`)), { kind: "probe", phase }) } + assert.match(lifecycle, /pre_update_option_rewrite_rules/) for (const [alias, phase] of Object.entries(aliases)) { assert.ok(alias.length <= 24) assert.match(worker, new RegExp(`"${alias}"`)) From 42032cb4942b6da66b12082d75293ffa999eb27b Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 04:30:26 -0400 Subject: [PATCH 26/87] Keep rewrite cache out of canonical state --- packages/runtime-cloudflare/src/worker.ts | 12 ++++++++---- tests/cloudflare-runtime.test.ts | 12 +++++++----- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index b921267a..e608c49c 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -1130,7 +1130,7 @@ async function bootWordPressRuntime( siteUrl = SITE_URL, authConstants: Partial> = {}, runtimeBucket?: R2Bucket, - shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit = false, + shouldPatchCanonicalRuntimePoliciesAtInit = false, ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, @@ -1162,7 +1162,7 @@ async function bootWordPressRuntime( materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed) } - if (shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit) patchCanonicalDisabledCronSchedulingPolicyAtInit(php) + if (shouldPatchCanonicalRuntimePoliciesAtInit) patchCanonicalRuntimePoliciesAtInit(php) } : undefined, beforeDatabaseSetup: databaseSeed ? (php: PHP) => { php.mkdir("/wordpress/wp-content/database") @@ -1190,19 +1190,23 @@ function materializeRuntimeFiles(php: PHP, root: string, files: RuntimeFile[]): } } -function patchCanonicalDisabledCronSchedulingPolicyAtInit(php: PHP): void { +function patchCanonicalRuntimePoliciesAtInit(php: PHP): void { const settingsPath = "/wordpress/wp-settings.php" const needle = "do_action( 'init' );" const settings = new TextDecoder().decode(php.readFileAsBuffer(settingsPath)) const firstNeedle = settings.indexOf(needle) if (firstNeedle === -1 || firstNeedle !== settings.lastIndexOf(needle)) { - throw new Error("WordPress canonical disabled-cron scheduling policy patch needle was not uniquely found.") + throw new Error("WordPress canonical runtime policy patch needle was not uniquely found.") } const replacement = `if ( defined( 'DISABLE_WP_CRON' ) && DISABLE_WP_CRON ) { remove_action( 'init', 'wp_cron' ); remove_action( 'init', 'wp_schedule_delete_old_privacy_export_files' ); remove_action( 'init', 'wp_schedule_update_checks' ); } +// Rewrite rules are derived for each runtime; keep them out of canonical MDI state. +add_filter( 'pre_update_option_rewrite_rules', static function ( $value, $old_value ) { + return $old_value; +}, PHP_INT_MAX, 2 ); ${needle}` php.writeFile(settingsPath, new TextEncoder().encode(`${settings.slice(0, firstNeedle)}${replacement}${settings.slice(firstNeedle + needle.length)}`)) } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 3bee3cc7..6927ed62 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -367,24 +367,26 @@ test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch assert.match(lifecycle, /: "\$stop/) }) -test("Cloudflare canonical runtime patches the unique init call with the disabled-cron scheduling policy", async () => { +test("Cloudflare canonical runtime patches the unique init call with runtime persistence policies", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const patcher = worker.slice(worker.indexOf("function patchCanonicalDisabledCronSchedulingPolicyAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) + const patcher = worker.slice(worker.indexOf("function patchCanonicalRuntimePoliciesAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) - assert.ok(patcher.startsWith("function patchCanonicalDisabledCronSchedulingPolicyAtInit"), "The canonical disabled-cron scheduling policy patcher is present.") + assert.ok(patcher.startsWith("function patchCanonicalRuntimePoliciesAtInit"), "The canonical runtime policy patcher is present.") assert.match(patcher, /const settingsPath = "\/wordpress\/wp-settings\.php"/) assert.match(patcher, /php\.readFileAsBuffer\(settingsPath\)/) assert.match(patcher, /firstNeedle === -1 \|\| firstNeedle !== settings\.lastIndexOf\(needle\)/) - assert.match(patcher, /throw new Error\("WordPress canonical disabled-cron scheduling policy patch needle was not uniquely found\."\)/) + assert.match(patcher, /throw new Error\("WordPress canonical runtime policy patch needle was not uniquely found\."\)/) assert.match(patcher, /defined\( 'DISABLE_WP_CRON' \) && DISABLE_WP_CRON/) assert.match(patcher, /remove_action\( 'init', 'wp_cron' \)/) assert.match(patcher, /remove_action\( 'init', 'wp_schedule_delete_old_privacy_export_files' \)/) assert.match(patcher, /remove_action\( 'init', 'wp_schedule_update_checks' \)/) + assert.match(patcher, /add_filter\( 'pre_update_option_rewrite_rules'/) + assert.match(patcher, /return \$old_value/) assert.equal((patcher.match(/do_action\( 'init' \);/g) ?? []).length, 1) assert.match(patcher, /php\.writeFile\(settingsPath/) assert.doesNotMatch(patcher, /mu-plugins|add_action\(|WP_Site_Health|wp_schedule_site_health_cron|\$GLOBALS\['wp_filter'\]/) assert.doesNotMatch(worker, /materializeCanonicalCronAdapter|wp-codebox-canonical-cron-policy/) - assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalDisabledCronSchedulingPolicyAtInit = false,/) + assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalRuntimePoliciesAtInit = false,/) assert.match(worker, /authConstants, bucket, true\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) From b2472950e39d8fc629550434c8eae4f68d798f70 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 04:36:54 -0400 Subject: [PATCH 27/87] Parallelize canonical R2 persistence --- packages/runtime-cloudflare/src/worker.ts | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index e608c49c..5a25dc85 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -392,23 +392,20 @@ async function readMarkdownRevision(bucket: R2Bucket, pointer: MarkdownPointer): const manifestObject = await bucket.get(pointer.manifestKey) if (!manifestObject) throw new Error(`R2 Markdown manifest is missing: ${pointer.manifestKey}`) const manifest = await manifestObject.json() - const files: RuntimeFile[] = [] - for (const file of manifest.files) { + return Promise.all(manifest.files.map(async (file): Promise => { const object = await bucket.get(file.objectKey) if (!object) throw new Error(`R2 Markdown object is missing: ${file.objectKey}`) - files.push({ path: file.path, bytes: new Uint8Array(await object.arrayBuffer()) }) - } - return files + return { path: file.path, bytes: new Uint8Array(await object.arrayBuffer()) } + })) } async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], current?: MarkdownPointer): Promise { - const manifestFiles: MarkdownManifestFile[] = [] - for (const file of files) { + const manifestFiles = await Promise.all(files.map(async (file): Promise => { const sha256 = await sha256Hex(file.bytes) const objectKey = `${R2_MARKDOWN_OBJECT_PREFIX}/${sha256}` if (!await bucket.head(objectKey)) await bucket.put(objectKey, file.bytes) - manifestFiles.push({ path: file.path, objectKey, sha256, size: file.bytes.byteLength }) - } + return { path: file.path, objectKey, sha256, size: file.bytes.byteLength } + })) if (current) { const currentManifest = await readMarkdownManifest(bucket, current) if (currentManifest && JSON.stringify(currentManifest.files) === JSON.stringify(manifestFiles)) return current From 9a7ce519a22b02598aafa16e0a6428fbf7db97f0 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 09:10:09 -0400 Subject: [PATCH 28/87] Add authenticated canonical state reset --- .../runtime-cloudflare/src/request-routing.ts | 2 ++ .../src/state-coordinator.ts | 7 ++++++ packages/runtime-cloudflare/src/worker.ts | 23 +++++++++++++++++++ tests/cloudflare-runtime.test.ts | 7 ++++++ 4 files changed, 39 insertions(+) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 7221ac74..51326da6 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -4,6 +4,7 @@ export type WorkerRequestRoute = | { kind: "r2-state" } | { kind: "r2-mutate" } | { kind: "canonical-auth" } + | { kind: "operator-reset" } | { kind: "probe"; phase: string } export function routeWorkerRequest(request: Request): WorkerRequestRoute { @@ -13,5 +14,6 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "r2-state") return { kind: "r2-state" } if (phase === "r2-mutate") return { kind: "r2-mutate" } if (phase === "canonical-auth") return { kind: "canonical-auth" } + if (phase === "operator-reset") return { kind: "operator-reset" } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/state-coordinator.ts b/packages/runtime-cloudflare/src/state-coordinator.ts index c636376b..528557a4 100644 --- a/packages/runtime-cloudflare/src/state-coordinator.ts +++ b/packages/runtime-cloudflare/src/state-coordinator.ts @@ -59,6 +59,7 @@ export class WordPressStateCoordinator implements DurableObject { if (action === "release") return Response.json(await this.release(body)) if (action === "abort") return Response.json(await this.abort(body)) if (action === "commit") return Response.json(await this.commit(body)) + if (action === "reset") return Response.json(await this.reset()) return new Response("Unknown coordinator action.", { status: 404 }) } @@ -120,6 +121,12 @@ export class WordPressStateCoordinator implements DurableObject { return lease } + private async reset(): Promise<{ reset: true }> { + await this.env.WORDPRESS_STATE_BUCKET.delete(POINTER_KEY) + await this.state.storage.delete(STORAGE_KEY) + return { reset: true } + } + private async record(): Promise { const stored = await this.state.storage.get(STORAGE_KEY) if (stored?.initialized) return stored diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 5a25dc85..e353e4b6 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -74,6 +74,7 @@ interface Env { WORDPRESS_STATE_BUCKET: R2Bucket WORDPRESS_ADMIN_PASSWORD?: string WORDPRESS_AUTH_SECRET?: string + WORDPRESS_OPERATOR_TOKEN?: string } export default { @@ -82,6 +83,7 @@ export default { if (staticResponse) return staticResponse const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") + if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) if (route.kind === "probe") { if (route.phase === "canonical-session") return canonicalSessionProbe(env.WORDPRESS_STATE_BUCKET, await coordinatorCall<{ pointer: MarkdownPointer | null }>(coordinator, request.url, "state")) return runBootProbe(route.phase, env.WORDPRESS_STATE_BUCKET) @@ -135,6 +137,27 @@ interface Runtime { let cachedRuntime: { baseRevision: string; promise: Promise } | undefined const LEASE_ACQUISITION_TIMEOUT_MS = 100_000 +async function resetCanonicalWordPress(request: Request, env: Env, coordinator: DurableObjectStub): Promise { + if (request.method !== "POST") return new Response("Canonical reset requires POST.", { status: 405 }) + const authorization = request.headers.get("authorization") + if (!env.WORDPRESS_OPERATOR_TOKEN || !authorization || !await secretsMatch(authorization, `Bearer ${env.WORDPRESS_OPERATOR_TOKEN}`)) { + return new Response("Canonical reset authorization failed.", { status: 401 }) + } + const response = await coordinator.fetch(new Request(coordinatorUrl(request.url, "reset"), { method: "POST", headers: { "content-type": "application/json" }, body: "{}" })) + if (response.ok) await discardCachedRuntime() + return response +} + +async function secretsMatch(left: string, right: string): Promise { + const encoder = new TextEncoder() + const [leftHash, rightHash] = await Promise.all([crypto.subtle.digest("SHA-256", encoder.encode(left)), crypto.subtle.digest("SHA-256", encoder.encode(right))]) + const leftBytes = new Uint8Array(leftHash) + const rightBytes = new Uint8Array(rightHash) + let difference = 0 + for (let index = 0; index < leftBytes.length; index++) difference |= leftBytes[index] ^ rightBytes[index] + return difference === 0 +} + async function runCoordinatedWordPressRequest(request: Request, env: Env, coordinator: DurableObjectStub, route: "wordpress" | "health" | "r2-mutate" | "canonical-auth"): Promise { if (route === "r2-mutate" && request.method !== "POST") return new Response("WordPress state mutation requires POST.", { status: 405 }) let lease = await acquireLease(coordinator, request.url) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 6927ed62..9de38f4d 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -41,6 +41,7 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-state")), { kind: "r2-state" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-mutate")), { kind: "r2-mutate" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=canonical-auth")), { kind: "canonical-auth" }) + assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=operator-reset")), { kind: "operator-reset" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) @@ -543,11 +544,13 @@ test("Cloudflare coordinator serializes leases, promotes with CAS, and recovers storage: { get: async (key: string) => values.get(key) as T | undefined, put: async (key: string, value: unknown) => { values.set(key, value) }, + delete: async (key: string) => { values.delete(key) }, }, } const bucket = { get: async (key: string) => objects.has(key) ? { json: async () => JSON.parse(objects.get(key)!) as T } : null, put: async (key: string, value: string) => { objects.set(key, value) }, + delete: async (key: string) => { objects.delete(key) }, } const coordinator = new WordPressStateCoordinator(state as never, { WORDPRESS_STATE_BUCKET: bucket as never, COORDINATOR_LEASE_MS: 50 }) const call = async (action: string, body: Record = {}) => coordinator.fetch(new Request(`https://worker.example/?__wp_codebox_coordinator=${action}`, { method: action === "state" ? "GET" : "POST", headers: { "content-type": "application/json" }, body: action === "state" ? undefined : JSON.stringify(body) })) @@ -566,6 +569,10 @@ test("Cloudflare coordinator serializes leases, promotes with CAS, and recovers const recovered = await (await call("begin")).json() as { token: string } assert.notEqual(recovered.token, stale.token) assert.equal((await call("release", { token: recovered.token })).status, 200) + assert.equal((await call("reset")).status, 200) + const resetState = await (await call("state")).json() as { pointer: null; version: number } + assert.equal(resetState.pointer, null) + assert.equal(resetState.version, 0) }) test("serialized Cloudflare mutations use MDI flush paths and complete canonical state", async () => { From 721ce3b0b967bd0e5b1f924807d9847a379fd0fb Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 09:58:48 -0400 Subject: [PATCH 29/87] Persist canonical MDI change sets --- ...n-database-integration-canonical-seed.json | 2 +- .../markdown-database-integration-runtime.zip | Bin 69079 -> 69961 bytes packages/runtime-cloudflare/src/worker.ts | 92 +++++++++++++++--- .../build-cloudflare-canonical-mdi-seed.php | 2 +- .../build-cloudflare-mdi-runtime-bundle.mjs | 2 +- tests/cloudflare-runtime.test.ts | 4 +- 6 files changed, 81 insertions(+), 21 deletions(-) diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json index 5c8d3657..71ac002b 100644 --- a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json +++ b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json @@ -1,6 +1,6 @@ { "schema": "wp-codebox/cloudflare-canonical-mdi-seed/v1", - "markdownDatabaseIntegrationRevision": "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3", + "markdownDatabaseIntegrationRevision": "6244f244f47f99af3261ba0948262cebe79e5a73", "wordpressInstallSeedSha256": "7212b89f1c01f98a23b89e9a2dd2b214306a85c10adb0355b9c7a6005b4fe822", "archiveSha256": "d69b700c671ebee7f9eb01fa80d58c66072cf3966560d01cc188bd26b4818895", "files": [ diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip index 030f12e7d9e66490914b0ed1269d53d9fd4590e7..c62afc51e0d1de0f9b42871d36a249a77f398ad7 100644 GIT binary patch delta 19731 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 Yw6_g)Z;<{ypun#_ydW2Xh7ce91NKD`!~g&Q delta 18822 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 D)p~H* diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index e353e4b6..0aa1773f 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -24,12 +24,13 @@ const PHP_VERSION = "8.5.8" // Browser assets must come from the same immutable WordPress release as the server corpus. const WORDPRESS_ARCHIVE_URL = (wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest).source.url const SQLITE_INTEGRATION_ARCHIVE_URL = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip" -const MARKDOWN_DATABASE_INTEGRATION_REVISION = "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3" +const MARKDOWN_DATABASE_INTEGRATION_REVISION = "6244f244f47f99af3261ba0948262cebe79e5a73" const SITE_URL = "https://wp-codebox-runtime.invalid" const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite" const MARKDOWN_ROOT = "/wordpress/wp-content/markdown" const MARKDOWN_INDEX_PATH = "/tmp/markdown-index.sqlite" const MARKDOWN_RESOLVED_INDEX_PATH = "/tmp/markdown-index-8133b4cf3c66.sqlite" +const MARKDOWN_CHANGES_PATH = "/tmp/wp-codebox-canonical-changes.json" const R2_MARKDOWN_REVISION_PREFIX = "sites/default/markdown/revisions" const R2_MARKDOWN_OBJECT_PREFIX = "sites/default/markdown/objects" const SERIALIZED_MARKDOWN_MUTATION_CODE = ` { - const output = (await runtime.php.run({ code: "flush_canonical_writes(); echo 'flushed';" })).text.trim() - if (output !== "flushed") throw new Error("MDI did not confirm its canonical flush.") - return persistMarkdownRevision(bucket, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT), runtime.pointer) +async function persistRuntime(bucket: R2Bucket, runtime: Runtime, changes: MarkdownChanges): Promise { + validateMarkdownChanges(changes) + const changedPaths = [...changes.created, ...changes.changed].sort((left, right) => left.localeCompare(right)) + return persistMarkdownRevision(bucket, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT, changedPaths), runtime.pointer, changes) } -async function runSyntheticMutation(runtime: Runtime): Promise { +function readCanonicalChanges(php: PHP): MarkdownChanges { + const raw = new TextDecoder().decode(php.readFileAsBuffer(MARKDOWN_CHANGES_PATH)).trim() + const changes = JSON.parse(raw) as MarkdownChanges + validateMarkdownChanges(changes) + return changes +} + +async function runSyntheticMutation(runtime: Runtime): Promise<{ response: Response; canonicalChanges: MarkdownChanges }> { const mutationOutput = (await runtime.php.run({ code: SERIALIZED_MARKDOWN_MUTATION_CODE })).text.trim() const mutation = JSON.parse(mutationOutput) as { revisionValue: number; previousPostFound: boolean; postId: number; wordpressVersion: string; canonicalChanges: MarkdownChanges } validateMarkdownChanges(mutation.canonicalChanges) - return Response.json({ schema: "wp-codebox/cloudflare-wordpress-mutation/v1", source: "entry-worker-primary-runtime", ...mutation, canonicalFiles: collectRuntimeFiles(runtime.php, MARKDOWN_ROOT).length, markdownDatabaseIntegrationRevision: MARKDOWN_DATABASE_INTEGRATION_REVISION, sqlitePersisted: false }) + return { response: Response.json({ schema: "wp-codebox/cloudflare-wordpress-mutation/v1", source: "entry-worker-primary-runtime", ...mutation, canonicalFiles: collectRuntimeFiles(runtime.php, MARKDOWN_ROOT).length, markdownDatabaseIntegrationRevision: MARKDOWN_DATABASE_INTEGRATION_REVISION, sqlitePersisted: false }), canonicalChanges: mutation.canonicalChanges } } async function health(runtime: Runtime): Promise { @@ -422,23 +436,47 @@ async function readMarkdownRevision(bucket: R2Bucket, pointer: MarkdownPointer): })) } -async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], current?: MarkdownPointer): Promise { +async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], current?: MarkdownPointer, changes?: MarkdownChanges): Promise { + const currentManifest = current ? await readMarkdownManifest(bucket, current) : null + if (current && !currentManifest) throw new Error(`R2 Markdown manifest is missing: ${current.manifestKey}`) + if (current && currentManifest && changes) { + validateMarkdownChanges(changes) + if (!changes.created.length && !changes.changed.length && !changes.deleted.length) return current + const manifestFiles = new Map(currentManifest.files.map((file) => [file.path, file])) + for (const path of changes.deleted) manifestFiles.delete(path) + const filesByPath = new Map(files.map((file) => [file.path, file])) + for (const path of [...changes.created, ...changes.changed]) { + const file = filesByPath.get(path) + if (!file) throw new Error(`MDI changed path is missing from the canonical runtime: ${path}`) + const sha256 = await sha256Hex(file.bytes) + const objectKey = `${R2_MARKDOWN_OBJECT_PREFIX}/${sha256}` + await bucket.put(objectKey, file.bytes) + manifestFiles.set(path, { path, objectKey, sha256, size: file.bytes.byteLength }) + } + return persistMarkdownManifest(bucket, [...manifestFiles.values()].sort((left, right) => left.path.localeCompare(right.path))) + } + const currentFiles = new Map(currentManifest?.files.map((file) => [file.path, file]) ?? []) const manifestFiles = await Promise.all(files.map(async (file): Promise => { const sha256 = await sha256Hex(file.bytes) + const existing = currentFiles.get(file.path) + if (existing?.sha256 === sha256 && existing.size === file.bytes.byteLength) return existing const objectKey = `${R2_MARKDOWN_OBJECT_PREFIX}/${sha256}` - if (!await bucket.head(objectKey)) await bucket.put(objectKey, file.bytes) + if (current || !await bucket.head(objectKey)) await bucket.put(objectKey, file.bytes) return { path: file.path, objectKey, sha256, size: file.bytes.byteLength } })) - if (current) { - const currentManifest = await readMarkdownManifest(bucket, current) - if (currentManifest && JSON.stringify(currentManifest.files) === JSON.stringify(manifestFiles)) return current + if (current && currentManifest) { + if (JSON.stringify(currentManifest.files) === JSON.stringify(manifestFiles)) return current } + return persistMarkdownManifest(bucket, manifestFiles) +} + +async function persistMarkdownManifest(bucket: R2Bucket, files: MarkdownManifestFile[]): Promise { const revision = crypto.randomUUID() const manifestKey = `${R2_MARKDOWN_REVISION_PREFIX}/${revision}.json` const persistedAt = new Date().toISOString() const pointer: MarkdownPointer = { revision, manifestKey, persistedAt } - const manifest: MarkdownManifest = { ...pointer, files: manifestFiles } + const manifest: MarkdownManifest = { ...pointer, files } await bucket.put(manifestKey, JSON.stringify(manifest), { httpMetadata: { contentType: "application/json" }, }) @@ -1179,6 +1217,7 @@ async function bootWordPressRuntime( if (streamWordPressFiles) await materializeWordPressServerFiles(php, runtimeBucket) if (markdownFiles) { await materializeMarkdownDatabaseIntegration(php) + materializeCanonicalChangeAdapter(php) materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed) } @@ -1231,7 +1270,15 @@ ${needle}` php.writeFile(settingsPath, new TextEncoder().encode(`${settings.slice(0, firstNeedle)}${replacement}${settings.slice(firstNeedle + needle.length)}`)) } -function collectRuntimeFiles(php: PHP, root: string): RuntimeFile[] { +function collectRuntimeFiles(php: PHP, root: string, paths?: string[]): RuntimeFile[] { + if (paths) { + return paths.map((path) => { + if (!isCanonicalRelativePath(path)) throw new Error(`Invalid canonical runtime path: ${path}`) + const absolute = `${root}/${path}` + if (php.isDir(absolute)) throw new Error(`Canonical runtime file is missing: ${path}`) + return { path, bytes: php.readFileAsBuffer(absolute) } + }) + } const files: RuntimeFile[] = [] const visit = (directory: string): void => { for (const name of php.listFiles(directory)) { @@ -1283,6 +1330,19 @@ async function materializeMarkdownDatabaseIntegration(php: PHP): Promise { } } +function materializeCanonicalChangeAdapter(php: PHP): void { + const path = "/wordpress/wp-content/mu-plugins/wp-codebox-cloudflare-canonical-changes.php" + const source = ` { if (!bucket) throw new Error("WordPress runtime corpus artifact requires WORDPRESS_STATE_BUCKET.") return materializeWordPressRuntimeArtifact(php, bucket, wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest) diff --git a/scripts/build-cloudflare-canonical-mdi-seed.php b/scripts/build-cloudflare-canonical-mdi-seed.php index a0cf5f90..30514783 100644 --- a/scripts/build-cloudflare-canonical-mdi-seed.php +++ b/scripts/build-cloudflare-canonical-mdi-seed.php @@ -1,7 +1,7 @@ assert.equal(markdownIndex.subarray(0, 16).toString(), "SQLite format 3\0") assert.equal(markdownRuntime.subarray(0, 4).toString("hex"), "504b0304") assert.equal(canonicalSeed.subarray(0, 4).toString("hex"), "504b0304") - assert.equal(canonicalManifest.markdownDatabaseIntegrationRevision, "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3") + assert.equal(canonicalManifest.markdownDatabaseIntegrationRevision, "6244f244f47f99af3261ba0948262cebe79e5a73") assert.equal(canonicalManifest.wordpressInstallSeedSha256, createHash("sha256").update(sqliteInput).digest("hex")) assert.equal(canonicalManifest.archiveSha256, createHash("sha256").update(canonicalSeed).digest("hex")) assert.ok(canonicalManifest.files.some((file) => file.path.endsWith(".md"))) @@ -134,7 +134,7 @@ test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => }) test("Cloudflare runtime pins and bundles the public constrained MDI runtime", async () => { - const revision = "1870fb41279e7eb5946e506c9c7406f1f1ea6dc3" + const revision = "6244f244f47f99af3261ba0948262cebe79e5a73" const generator = await readFile(new URL("../scripts/build-cloudflare-mdi-runtime-bundle.mjs", import.meta.url), "utf8") const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const runtime = await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip", import.meta.url)) From 121517290c868d98a3179a78d8e82172fd37e350 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Sun, 19 Jul 2026 17:57:49 -0400 Subject: [PATCH 30/87] Document canonical lifecycle probes --- packages/runtime-cloudflare/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 1b509863..c23d6eea 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -26,6 +26,8 @@ Every `mdi-*` lifecycle diagnostic materializes the validated packaged canonical `?phase=canonical-wordpress` performs a full normal WordPress boot with that exact seed and artifact outside the coordinator, then returns WordPress version, memory, and non-sensitive table counts. Its widget-state count includes the `widget_*` options and `sidebars_widgets`. `?phase=canonical-bootstrap-setup` additionally runs the normal bootstrap setup with a probe-only internal password and fixed probe origin, flushes only the disposable runtime, and returns changed-path counts. Neither probe returns password, option, user, or content values. +The fixed bounded lifecycle probes `canonical-current-user`, `canonical-init`, `canonical-site-status`, and `canonical-wp-loaded` use the packaged canonical MDI seed, R2 WordPress corpus, and the same disabled-cron scheduling patch as the production boot. Each applies a second unique `wp-settings.php` stop injection to the disposable MEMFS copy, so `init` and `wp_loaded` each execute at most once. They return only completion, phase, WordPress version, memory, and the three disabled-cron callback attachment booleans. + The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-wp-cron`, `mdi-init-exclude-privacy-schedule`, `mdi-init-exclude-update-schedule`, `mdi-init-exclude-wp-cron-privacy-schedule`, `mdi-init-exclude-wp-cron-update-schedule`, `mdi-init-exclude-privacy-update-schedule`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group through WordPress's public `remove_action()` API, runs `init` once, and returns the exact removed names, completion, and memory measurements before subsequent WordPress lifecycle work. The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` reports only fixed widget option names, grouped by `get_option()` presence and direct `$wpdb` SQLite-row presence; it returns no option values. `mdi-widgets-get-settings` calls only `get_settings()` on each default factory object, with `mdi-widgets-get-settings-first` and `mdi-widgets-get-settings-second` as fixed halves; each reports completed classes and array-shape dimensions only. `mdi-widgets-register-one` preloads the fixed `widget_text` option, calls that object's `get_settings()`, then calls only `_register_one()` for its fixed instance. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. From 1f08ffe24a6eae0422c1ca629ef606eabb61b79e Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 13:33:38 -0400 Subject: [PATCH 31/87] Honor coordinator lease retry timing --- packages/runtime-cloudflare/src/lease-retry.ts | 8 ++++++++ packages/runtime-cloudflare/src/worker.ts | 7 +++++-- tests/cloudflare-runtime.test.ts | 8 ++++++++ 3 files changed, 21 insertions(+), 2 deletions(-) create mode 100644 packages/runtime-cloudflare/src/lease-retry.ts diff --git a/packages/runtime-cloudflare/src/lease-retry.ts b/packages/runtime-cloudflare/src/lease-retry.ts new file mode 100644 index 00000000..cd6002de --- /dev/null +++ b/packages/runtime-cloudflare/src/lease-retry.ts @@ -0,0 +1,8 @@ +const DEFAULT_RETRY_DELAY_MS = 1_000 + +export function leaseRetryDelayMs(retryAfterSeconds: number | undefined, remainingMs: number): number { + const requestedMs = retryAfterSeconds !== undefined && Number.isFinite(retryAfterSeconds) && retryAfterSeconds > 0 + ? retryAfterSeconds * 1_000 + : DEFAULT_RETRY_DELAY_MS + return Math.max(0, Math.min(remainingMs, requestedMs)) +} diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 0aa1773f..5e0e0f04 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -6,6 +6,7 @@ import { bootWordPressAndRequestHandler, type WordPressInstallMode } from "@wp-p import { dependenciesTotalSize, init } from "../../../node_modules/@php-wasm/web-8-5/asyncify/php_8_5.js" import phpWasmModule from "../../../node_modules/@php-wasm/web-8-5/asyncify/8_5_8/php_8_5.wasm" import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "./health-envelope.js" +import { leaseRetryDelayMs } from "./lease-retry.js" import { routeWorkerRequest } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" @@ -228,13 +229,15 @@ async function coordinatorCall(coordinator: DurableObjectStub, requestUrl: st async function acquireLease(coordinator: DurableObjectStub, requestUrl: string): Promise { const deadline = Date.now() + LEASE_ACQUISITION_TIMEOUT_MS let lastError: CoordinatorRequestError | undefined - while (Date.now() < deadline) { + while (true) { + const remainingMs = deadline - Date.now() + if (remainingMs <= 0) break try { return await coordinatorCall(coordinator, requestUrl, "begin", {}) } catch (error) { if (!(error instanceof CoordinatorRequestError) || error.status !== 409) throw error lastError = error - await new Promise((resolve) => setTimeout(resolve, Math.min(250, Math.max(25, (error.retryAfter ?? 1) * 100)))) + await new Promise((resolve) => setTimeout(resolve, leaseRetryDelayMs(error.retryAfter, deadline - Date.now()))) } } throw new Error(`Timed out waiting for the canonical WordPress lease${lastError ? `: ${lastError.message}` : "."}`) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 9cc256db..5eebbc33 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -7,6 +7,7 @@ import test from "node:test" import { decodeZip, encodeZip } from "@php-wasm/stream-compression" import { RUNTIME_COMMAND_RESULT_SCHEMA } from "../packages/runtime-core/src/runtime-contracts.js" import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "../packages/runtime-cloudflare/src/health-envelope.js" +import { leaseRetryDelayMs } from "../packages/runtime-cloudflare/src/lease-retry.js" import { routeWorkerRequest } from "../packages/runtime-cloudflare/src/request-routing.js" import { toFetchResponse, toPHPRequest } from "../packages/runtime-cloudflare/src/request-translation.js" import { WordPressStateCoordinator } from "../packages/runtime-cloudflare/src/state-coordinator.js" @@ -106,6 +107,13 @@ test("Cloudflare runtime declares the paid-plan WordPress boot CPU budget", asyn assert.equal(config.limits?.cpu_ms, 300_000) }) +test("Cloudflare lease contention honors Retry-After without exceeding the acquisition deadline", () => { + assert.equal(leaseRetryDelayMs(90, 100_000), 90_000) + assert.equal(leaseRetryDelayMs(90, 12_345), 12_345) + assert.equal(leaseRetryDelayMs(undefined, 100_000), 1_000) + assert.equal(leaseRetryDelayMs(Number.NaN, 500), 500) +}) + test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => { const config = JSON.parse((await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")).replace(/^\s*\/\/.*\n/, "")) as { rules?: Array<{ type?: string; globs?: string[] }> From c4fbc710b41f8474929254bda7b2e189f1efa750 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 13:50:22 -0400 Subject: [PATCH 32/87] Add bounded Cloudflare editor probes --- .../runtime-cloudflare/src/request-routing.ts | 6 +++ packages/runtime-cloudflare/src/worker.ts | 38 ++++++++++++++++++- tests/cloudflare-runtime.test.ts | 14 +++++++ 3 files changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 51326da6..34f5e2d7 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -5,8 +5,11 @@ export type WorkerRequestRoute = | { kind: "r2-mutate" } | { kind: "canonical-auth" } | { kind: "operator-reset" } + | { kind: "editor-probe"; phase: EditorProbePhase } | { kind: "probe"; phase: string } +export type EditorProbePhase = "admin" | "auto-draft" | "block-editor" + export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") if (phase === null) return { kind: "wordpress" } @@ -15,5 +18,8 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "r2-mutate") return { kind: "r2-mutate" } if (phase === "canonical-auth") return { kind: "canonical-auth" } if (phase === "operator-reset") return { kind: "operator-reset" } + if (phase === "editor-probe-admin") return { kind: "editor-probe", phase: "admin" } + if (phase === "editor-probe-auto-draft") return { kind: "editor-probe", phase: "auto-draft" } + if (phase === "editor-probe-block-editor") return { kind: "editor-probe", phase: "block-editor" } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 5e0e0f04..caeed711 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -7,7 +7,7 @@ import { dependenciesTotalSize, init } from "../../../node_modules/@php-wasm/web import phpWasmModule from "../../../node_modules/@php-wasm/web-8-5/asyncify/8_5_8/php_8_5.wasm" import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "./health-envelope.js" import { leaseRetryDelayMs } from "./lease-retry.js" -import { routeWorkerRequest } from "./request-routing.js" +import { routeWorkerRequest, type EditorProbePhase } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" @@ -86,6 +86,7 @@ export default { const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) + if (route.kind === "editor-probe") return runCoordinatedEditorProbe(request, env, coordinator, route.phase) if (route.kind === "probe") { if (route.phase === "canonical-session") return canonicalSessionProbe(env.WORDPRESS_STATE_BUCKET, await coordinatorCall<{ pointer: MarkdownPointer | null }>(coordinator, request.url, "state")) return runBootProbe(route.phase, env.WORDPRESS_STATE_BUCKET) @@ -99,6 +100,41 @@ export default { }, } +async function runCoordinatedEditorProbe(request: Request, env: Env, coordinator: DurableObjectStub, phase: EditorProbePhase): Promise { + const lease = await acquireLease(coordinator, request.url) + let finalized = false + try { + if (!lease.pointer) throw new Error("Canonical WordPress must be initialized before running an editor probe.") + const runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) + patchEditorProbe(runtime.php, phase) + const response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) + await releaseLease(coordinator, request.url, lease) + finalized = true + await discardCachedRuntime() + return response + } catch (error) { + if (!finalized) await abortLease(coordinator, request.url, lease) + await discardCachedRuntime() + throw error + } +} + +function patchEditorProbe(php: PHP, phase: EditorProbePhase): void { + const path = "/wordpress/wp-admin/post-new.php" + const source = new TextDecoder().decode(php.readFileAsBuffer(path)) + const markers: Record = { + admin: "require_once __DIR__ . '/admin.php';", + "auto-draft": "$post_ID = $post->ID;", + "block-editor": "require_once ABSPATH . 'wp-admin/admin-footer.php';", + } + const marker = markers[phase] + const index = source.indexOf(marker) + if (index === -1 || index !== source.lastIndexOf(marker)) throw new Error(`WordPress editor probe marker is not unique: ${phase}`) + const stop = `\necho wp_json_encode( array( 'schema' => 'wp-codebox/cloudflare-editor-probe/v1', 'phase' => '${phase}', 'memoryBytes' => memory_get_usage( true ), 'peakMemoryBytes' => memory_get_peak_usage( true ) ) );\nreturn;` + const insertion = phase === "block-editor" ? index : index + marker.length + php.writeFile(path, new TextEncoder().encode(`${source.slice(0, insertion)}${stop}${source.slice(insertion)}`)) +} + interface MarkdownManifestFile { path: string objectKey: string diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 5eebbc33..50a4acff 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -43,6 +43,9 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-mutate")), { kind: "r2-mutate" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=canonical-auth")), { kind: "canonical-auth" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=operator-reset")), { kind: "operator-reset" }) + assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-probe-admin")), { kind: "editor-probe", phase: "admin" }) + assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-probe-auto-draft")), { kind: "editor-probe", phase: "auto-draft" }) + assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-probe-block-editor")), { kind: "editor-probe", phase: "block-editor" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) @@ -114,6 +117,17 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui assert.equal(leaseRetryDelayMs(Number.NaN, 500), 500) }) +test("Cloudflare editor probes bound the authenticated editor lifecycle and discard their runtime", async () => { + const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const probe = worker.slice(worker.indexOf("async function runCoordinatedEditorProbe"), worker.indexOf("async function resetCanonicalWordPress")) + + assert.match(probe, /await runtime\.requestHandler\.request/) + assert.match(probe, /await releaseLease/) + assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) + assert.match(probe, /\/wordpress\/wp-admin\/post-new\.php/) + assert.match(probe, /memory_get_peak_usage/) +}) + test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => { const config = JSON.parse((await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")).replace(/^\s*\/\/.*\n/, "")) as { rules?: Array<{ type?: string; globs?: string[] }> From a56125c56f51d31414e850525ae718813da9b51e Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 13:56:33 -0400 Subject: [PATCH 33/87] Probe MDI auto-draft persistence --- .../runtime-cloudflare/src/request-routing.ts | 3 ++- packages/runtime-cloudflare/src/worker.ts | 19 +++++++++++++++++++ tests/cloudflare-runtime.test.ts | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 34f5e2d7..f04e8cdf 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-probe"; phase: EditorProbePhase } | { kind: "probe"; phase: string } -export type EditorProbePhase = "admin" | "auto-draft" | "block-editor" +export type EditorProbePhase = "admin" | "auto-draft" | "auto-draft-no-persist" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,6 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase === "editor-probe-admin") return { kind: "editor-probe", phase: "admin" } if (phase === "editor-probe-auto-draft") return { kind: "editor-probe", phase: "auto-draft" } + if (phase === "editor-probe-auto-draft-no-persist") return { kind: "editor-probe", phase: "auto-draft-no-persist" } if (phase === "editor-probe-block-editor") return { kind: "editor-probe", phase: "block-editor" } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index caeed711..da1880ed 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -106,6 +106,7 @@ async function runCoordinatedEditorProbe(request: Request, env: Env, coordinator try { if (!lease.pointer) throw new Error("Canonical WordPress must be initialized before running an editor probe.") const runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) + if (phase === "auto-draft-no-persist") patchAutoDraftPersistenceProbe(runtime.php) patchEditorProbe(runtime.php, phase) const response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) await releaseLease(coordinator, request.url, lease) @@ -125,6 +126,7 @@ function patchEditorProbe(php: PHP, phase: EditorProbePhase): void { const markers: Record = { admin: "require_once __DIR__ . '/admin.php';", "auto-draft": "$post_ID = $post->ID;", + "auto-draft-no-persist": "$post_ID = $post->ID;", "block-editor": "require_once ABSPATH . 'wp-admin/admin-footer.php';", } const marker = markers[phase] @@ -135,6 +137,23 @@ function patchEditorProbe(php: PHP, phase: EditorProbePhase): void { php.writeFile(path, new TextEncoder().encode(`${source.slice(0, insertion)}${stop}${source.slice(insertion)}`)) } +function patchAutoDraftPersistenceProbe(php: PHP): void { + const path = "/wordpress/wp-content/plugins/markdown-database-integration/inc/class-wp-markdown-write-engine.php" + const source = new TextDecoder().decode(php.readFileAsBuffer(path)) + const needle = `if ( $id ) { + $this->mark_post_dirty( (int) $id ); + }` + const replacement = `if ( $id ) { + $rows = $this->driver->query( "SELECT post_status FROM \`{$this->prefix()}posts\` WHERE ID = " . (int) $id ); + if ( ! is_array( $rows ) || empty( $rows ) || 'auto-draft' !== ( $rows[0]->post_status ?? '' ) ) { + $this->mark_post_dirty( (int) $id ); + } + }` + const index = source.indexOf(needle) + if (index === -1 || index !== source.lastIndexOf(needle)) throw new Error("MDI auto-draft persistence probe marker is not unique.") + php.writeFile(path, new TextEncoder().encode(`${source.slice(0, index)}${replacement}${source.slice(index + needle.length)}`)) +} + interface MarkdownManifestFile { path: string objectKey: string diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 50a4acff..0e86d1cd 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -45,6 +45,7 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=operator-reset")), { kind: "operator-reset" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-probe-admin")), { kind: "editor-probe", phase: "admin" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-probe-auto-draft")), { kind: "editor-probe", phase: "auto-draft" }) + assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-probe-auto-draft-no-persist")), { kind: "editor-probe", phase: "auto-draft-no-persist" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-probe-block-editor")), { kind: "editor-probe", phase: "block-editor" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) @@ -126,6 +127,7 @@ test("Cloudflare editor probes bound the authenticated editor lifecycle and disc assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /\/wordpress\/wp-admin\/post-new\.php/) assert.match(probe, /memory_get_peak_usage/) + assert.match(probe, /'auto-draft' !== \( \$rows\[0\]->post_status/) }) test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => { From 9c014188c47ebd44fbb800fd9fab0101ba16cd49 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:08:40 -0400 Subject: [PATCH 34/87] Bundle upstream auto-draft persistence fix --- ...n-database-integration-canonical-seed.json | 2 +- .../markdown-database-integration-runtime.zip | Bin 69961 -> 71211 bytes .../runtime-cloudflare/src/request-routing.ts | 7 --- packages/runtime-cloudflare/src/worker.ts | 59 +----------------- .../build-cloudflare-canonical-mdi-seed.php | 2 +- .../build-cloudflare-mdi-runtime-bundle.mjs | 2 +- tests/cloudflare-runtime.test.ts | 27 +++----- 7 files changed, 13 insertions(+), 86 deletions(-) diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json index 71ac002b..a4d2b3fe 100644 --- a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json +++ b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json @@ -1,6 +1,6 @@ { "schema": "wp-codebox/cloudflare-canonical-mdi-seed/v1", - "markdownDatabaseIntegrationRevision": "6244f244f47f99af3261ba0948262cebe79e5a73", + "markdownDatabaseIntegrationRevision": "7cf025f2d64aa933d937f1a18a129e278c231783", "wordpressInstallSeedSha256": "7212b89f1c01f98a23b89e9a2dd2b214306a85c10adb0355b9c7a6005b4fe822", "archiveSha256": "d69b700c671ebee7f9eb01fa80d58c66072cf3966560d01cc188bd26b4818895", "files": [ diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip index c62afc51e0d1de0f9b42871d36a249a77f398ad7..e30bef701e25d0abca3d31b7b894fd8bfd3330c1 100644 GIT binary patch delta 50580 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

oVh)U-r4BNnFNR<6#gMdmc3SC~Q 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 delta 49318 zcmV($K;yrwtOUuT1h8L03k$QQ(AG)-07^CilTeu(m%9`J3X_jP4u49v5VXhk?1*jm zV4!VwXYQTz*UUPBoWY@=8R(Xrcc9t6Xr7>PA$^WFiuCgoXNBZVOV!m z98KYHS(+>aG#8_3m`?19?c%aLk~uH=SzjFHGAyJBlSF)z<$uvdE|n5Rc$-Kig1a~r zuNGH-I~9kQ#|PKP;`-o=(_^8Eu)w9LE{*eXA9q&V&xhH(h_h6Qz>bzm87WarrGf?L z{N?xaq24C$!-M_F4pu6eE3vEpoXH}jHlNrwh0JH%2L2ot;oB_DW()hscKg(Jn`f|R zfp^k$$hbP_6Muz8h&$R7qbx=ENWyz{1PBu?KbdiocB8m0>|Ps&N0>Vqw& zVIijBofHV$_@Ov6gT-E7T*2FaxjH`+voMXv64spMVt-r~WiG|N22REBWuVo8tf?Hm zp&<}V^*FS_xkRW(fF)Rscy$J(u8Ocg!0gW==V)@dGY@kmy}rAtyzhxfCbEzsm$U3n zM!d93IaUMlWn6q!-ilbMQY!Jmv!3|iV{Y)*c{q9#PNaCna4z7Vh$fpk~*2m-sNlT#6CiVmRtZ z5O@mtcAmtexJVY_b^*7KULf+rL5itdbaFr+qkp7?9tfS#a3WKg$0HADxVK%y)ilgy zB<|y4>V4Hw9^c7)FgQHFJRTyx7+!t-^5x05$5(iKZ={@yI7KXl17*3$ z^nVb#AxeSA9zrn>yi2X0+TQxE!W-wN!cV-M(5k!d9QyTC3V=3)WuiEjqaw=}c;y=D zkUoXK-i5hPMINV<$_{PwXu~MZ8)x%pNwahZF;iZqMLd&)NV88s%tkiK^0jJ$U&x)IF%xR%|f{8)|YQC!4-9??-V}v=89)=I$;!?io;2osUjX#;eV=e zPB`tB8848gGX80ztN~&Rm-fNU*v0!s+iBRc2(T712)Nln7m2VcXU-dEprIWos2)sq z`|om8y#As3CA3iH1R6i+FdR*RF3B7|Wj+vrMh1g{O3F!B{P%zUm+-K}FplU$z9{1a z8HSiJ@I)5G6U@RneoZ0?flg&A(tk`OSvrw9GD|XQI*5O!+t(9uT73RfS|&*i9C&!~ zT)=DhOU~rmf;f+pBe;;jb&ZhW0GP|O`CLXjS-JxZC?6J{Rho&LI2zp81)ZdE5r;|q z4;ksND>8BO>fqbp(8({J~nJ+{t?{PC6swbt#N{A0W`M7deaauHbxa+CN@rXs}UpB#y( z1h^3)?M#t}sR~Cxz>syw;RHQPAMzh(?c#xcLuMX-5JB12q7+A&c@jiaU{oKnG_0+zj?>t z5X;*n9*J?85-1OcBYjN3Yx2_FqX2HOw#B@^{H}HX;Jx00=c02zA0l)4LQiAAi;&6Y z21*+n+rU8YeC`4S{PSGoMIO%re|@5}+Y#M;+s)lAzngvY;(x`8L0kZ$K@8{SOk|;i z+Zf)+G^%iD;<<>`P{T@KAf&swv9a+&|AZd{JYx5-g03i)6i%mszDe&yE{ifxML-X_ zy3M|L*x%fEaPH+LtpriTsLT;*RE0I_>s(tNoo74bZnee^nlP)T*MYOv&5aGcTG6sg zUBD&YvW@O%K!2Jhq;16%1~f`1Kvx;xb-Vb(FiA9v0v}!1l*MTz-!>_?7P6q*NeU)~ zg1nYk(@310T^(Ou(_>nza*cmse|^U~liTIHJ3w ziZCz4b1{oYc?PH=5Jg_f+7+>ATXbNWA*ca6K$SZ{X@BrKY;J56`2xq?*xhwBZ5Cig zAnenY^r4x)yV!0BZ-?}jr~{32YX&l3&bta5;3x@Ez-}1#-&K|lbzhnQA*R}nW#=KD zux-cd&>BCj?z4Km2EE@*#Iu=<;;@j(!d(&`0}tU0t~G$9{sU;yLVK+An*99OB~!`^7=f0CSW_oVlH_ha&{|`-;YVYGh-^vEoz0H9_8A-(ux#&Q@~nz zcCUU#`H!>wbt~m8q1kitDRA;wZ^2R{&EIr)lk;~7vDDELr+z` z9!0RYJiM#mLxPeAF$#;(R0QJt>uH|dBdy++q|n7_(U>$2uA5l3HWn-snth>qxsZMpOLL>8~4QsG1fUC|d?-=|yl z&2=7*O4(@b#( zwD`pTka{2K%2_DN`6Le`*=^ub?71yrQGOm)1~7`@3m`gx|bBD@ZrU*qs4~Bc z?_@+|_JdFU>>d(q)lhE{Z!3Sei<|r63Q-30h`=4rWxj*6s4}0#qcB1J3s3Utryh0& zFs?I)AL~)UE<{(6;mk2B0zsh*hm+ZR=8uj5RHG?>;u$KG9fmJZqYU;dvVBjQU;(u%7a;9YJ=DV*)gJmH&<1K4KO%#w5z+ty@fIVnsZkCew~W3G z#LF;A0MeW0Y2rUnF2koJ>;9a?jb$gKVy}))fX=-EizMnItsE7mgtg!?XdGm|lidS4 z7=cB=HN?ii)B=f%vlOXhB7adzwPFz7wK6TT5{(J4g>#vYGvJ5Q3;`6!nl2);G$VOf zq@!t`rSU)TQ5&kC=ioRXhkCI?T%V8d*TAc5qSdvqs<+*EkKm5(z~LJczr!+slYk;! zzags~MRal(iR2QJ-*?F^z>aZUq04UxwM%K6R4sLMus+S4M;R!vTz~Bs-bhdorV_^w zy7XioWsGQccUjJKbV<%Zphva=VRS^<2R0JD;NoBM_ado z{>0tnzj04rUY@@aHw`?_O-FQhJ|D|sG(AX?Kzx63bUqloJib2sYIy#|zwm7xXE~l& zAZQt(!ap=lvx|R?&wqD5HwzL!xCZ)-jg1bj4f%>7PK&PFftJnfVBHer(^K2Vvrb|m{j&Nfehtutq`9h?J-WYw+=>sr# z_D#Rx8@T}0x!%2{oyz#Us(Oj7w6Ys=^-k7Q=qakA8C&Ol)fBeA&&3rQS>NHovrGZ?Z@4|xX#NI z;Ej!m_tPwqdJNLClg;lwp3ADKaz~Fo5A!01kHhcP-G3W+I^rA*x326CI8vV32usAY z#0-e0O!Y78*ajCYk>f(lVx`c>hWUK>Z0oL=kq-`7>p_mPIyUV?A+9o5joARlQOvl< z;U~aO;M#8`cpl*PWvvGxK)t8fPVX{+a2kEx(kSGV_~5foM4*9)7l%jv!DpY*3q|OG z@dJ5cEq}od9-ob^hvv{6B@<*ftaT^T)5xWIJoX|R#*yY}m#v~nq4%?_ zr`bb|Tws>2O&{Vp%2keSf*eJGQ+2!(=;BxxW`YSlMy!Mi$E4n6l4YrtwrW-JF)f7n z8Sobi#MaUA>GAb3b1Uzt^~24U{)VO6#>!4w#(#jREn1XDNOIAo%k_11=x;$D2@Bc< zN07vGjFBh>9)&X0VmnL{%VGhuHc7JEphnIL7>A|#*jL`1*mQv*7U-GGhgFudQAStRxZJe;97p>ojgsv4R_0puM+k<9Fn>_sS>2%K`F%SwEm+8urRFZ}CRbjwV&)ZS z**H?5r1Ttkz#&TxkI^}2FksC+9~*gygL=VdhS+8EK%`-OCw+*(p8Mo*088G|YyX=nK+W7(6B`zE#V7=gD(gN)Tv#TIw%e-Pi35w`2s#i3XU*wUCUcpC%QN^1T7Q)! z@<}+%ZC0k>9LJ7fzQNBf)8UI82L(7^--Aq>D?e%T>+-1<`90w+(cr`WD+ct$Pff7b z;20&gnLQbfvIL`*%2`U#XWZ{;?b=N=;11#^Vfcscat+E--lXXhdyT8}lrNxOaUF#o zp=Yr^RF&Qu$?Y4F+L?g6G_WVz#D92l2e&CRu-M21RCrUee-o`wHyI*d%?Jz8yPVC7 zMIh`TU0-Eko?}mNR~yHZ5|m=-id+?tfE5dq`*5LXb7pJIm-L$3+H*UgpO$bndjT5kUv$4ZC7LwgvG~+CK;(i*BrflD}zD-~* zfz1IP7sDIoxl}So&#*8>cj_(;W7NL=O^iF^)9y+(;;YOUC3G9lH z#k#(eBQt^noewuW1`dEb0Dqm|+2eXZzd6y}+T7Ui2HIhOXU?Qd{+#DX>0_1|qDTt8)oL0e_QZa(S;E4#04n z;KrXFe|s%*`C}DyaCe!URWNwIwP+Cyx^6}g-<(p%sdhyqTI2p@WjXr}F-tss%@ zx{F4t_h^b|$XOFh-+xG_{+C8QmPR2i^6(I%u_k2UX)tM!nhunW?PTHtlUW@A;c3wFi{Zh{G%uKcK*^6!=Bjb0)|bqd=@4mn@ci{_J4r(5?VgE{MV!NZ_b8C zUks1GJv{yT==f-O{oTd!6)_Cp+qt94`8jY|h6;OmQ{gX?3{{Y?9GfzN0Gkbg~vz=5z`$Rpl#IdXl!(0hs; zbF_zHmZIZ+cOV!gnB)R#DgCK(6~DZl`&nQ7UFPw4;W-8Ru2tFz&@pf~8MdEk%XPzH z-H6kZS0~rv8EYojvH;WcsK#FA3RA!UVq030;^5@Y(dGFCr^YyWDUQEAxw^h`*T}F( z+SZ$n-G5F;8tCW@8^^EoSgj+F5U_D$0s?QuHLzwEmnW|dF2581di-6_X@S5B!o&6P zm&cdl?EG4seSLcBHUUU(ZD9fsovA8K;Etsrs5paseR>KnyPZw8%WvziP{ph;t_$qK zLYqK$K%8HS%j1jFgTrGO?tIx!IGfND?)U0n_q`&c8aw&j+bWk>x>`+&N@oVomkISu=#UxQ{+!bBuew1lnbxX$`x3 zn16A5S_Mvhj0U^*oK)Af$ND%MUYUc{@4Alphj)+yQP=X&qYinNBwvK1H*8_7BLqI( zVS`@|f;MBY(YS9Qwr75^gO2`*4lVG@16q7yJ`Sb#DmJX#j?oxfot&K=Uy6S@KRIi| zoa0!WpLuH&&&4>lKOL=vw`TDN+m0TbUw?X^;Wogdggdp;!^nYPZ(l2pM*5}aW`wJ+ zxEX0LBNU64dm3p+qqe5j*~r2mwxl-xiBUTXkkAeTRMFOp9n4EX#vU>7<}|NyEXUCw z@W$^D)=rOKUjJ;M<~|1D9B9g;0my7r6?hEh{r?esIJ98M3dSn+(XUrP*f{R<0Dp}j z;-7mveAGZmppDA|5f@?_u+e*gi1)QsFsbeoia6dL(+e%iyf1553B!2Z@wU@YU9@8Q zgt!{PQR~X+61!qb7>i%)@gKF`sl4y}z&a`2+j1|f`}0P6v)91T@LMNi5OYug#7pdn zdkG1vq73r~__`?b8RCzBG!LECxqpxAlbXYBvKf<7WTnx#u6A;5PtB$$*Phg7P68`m ztH}vnkFXgqR$BGvHEZw)+OLT)UmSmVawe`X56-R*4zEwn&zj`WialZ)|R=FKC**s2;SpUT{vG!}#PmAO#a{a;+O6$t^g^*Va-b3c zs0~cE$PrrAwQD1PJ2|fqRDb8$eSp-ord)vE)ru_+WdD8Gj1E-d4T5mKgp| zxV{=R4$304$(+WyRA8(Vu@jtmA1a;VOzUcNZ*851ykWt?i3lg4=D+|aOpk)wWle^Y zxDa#9wnN>sF=yHG(k}FbEJppVm!JTXMYUrb`bgev`kEnRuNuZt-~mAE2z4(<2X{5V z+A>6R5TV1<{~{p|34fgsf~jP*Xu&m4zuX>Mc6k2k)ycJ+GU0+0i(vHBa4#b?Pix&Z zk#Kg<9-A~EXuKTD9L%s}(x(({GzFQzK*@!}kaHb|GKx|M4O8+)yb_}%q`Z|dG5V0f z74H-}!AHaiblnJ9Ow2Z{r9gXx4vshF^8EDli-W^|t;CKlO@FS|tg-_kWN6j0%_0%C zf?K{^^r3c|GhQy@=xgk+;B&D@Ub|*S|A`*^b9?cfpxdo#`0zly^Cx()lGHmp$%DY$ z$?yD09yE`?wIsX5W5grRfT!c_SH#(~5l?4EsJC%4S*ICBy-PLE5Du&hNzNW76ufLA zcQhN<7-sLQ_J6C6CP;Z{1Wak*$Jke+>jT-J^m`&Ir6@8S1xYO@LCEDTTGySBeIVqD zqVBtB?`uMzWVAL>`rnuoh_UK9&O{Xa1fy+L#OW;xbrK(n6dY{46a-SoAvqq%O%}za z?mv&SEI!uLr9pgd_jVzb!pk^~EX46T2h|J?A*aM_On>3vn<#0lCI#8XR!u|Jt7$pc zQJ8rev#Pi|XFZwaRp*O+6xDP=^CKPtmP-x*vTEt!kY2oQyD|gVN1}(4++g55!CG9oKmEVHv8^dF|=` zd5QV@TYpw1#%?%*<9~qrOcIlKij0uK?Lt5f+i~XHXv!c!0ZBUDag%A#y!US>!rbkG zAgtLb#EuNYh0E2u{Ci+Ic!+J;^Fp3tkFa^uzQ(tx@Pwy+EY~CTSla*$jz!h|3Vp8d z4fMCaoS`d=PytK}wE!FeB``&XlSnk6B`R)2|f5<_#94}H!6F_1z!73CZeHZ6za zrruAF1fnYcZROQgt;nTEH?(4;C;aeX)yfz*Ng_|qJyea&wIjfmUWNTY)@|L~ECtseOU+P2CQ(UqK=%wQ9l!q z@W}pv*7H!Q`z(+6CuWbqu0=MSV@7lMuhj2nSrm`s+Rx^t4#_9qv?hgMPfSX{Tz@l} zmP0H{<@5w6Q9X7%ll)tM2C`Jh&NjPlJ?&o|3&RK7hyp8$$X@N4dzzaXVe(@<*QSSN z+d7?|5S(;D?;g^6Bs@L00F{$ZBc_xW?x+YBM8Ik@GMKcyC8)rcm~KiLnoN2qT%E8jcT96yJ^le-I_Nxb6ZW6;CDp_YG$}! zg$}S?)j|AM$JZ9k5uy;!0sk0mQ2B`<2#;!b)oDog-Tu~h+&veJ)-=N8=0dT)cQJV*g(x z{)-~&#tJRvy9r9aN;wKNY^iHNRmf)MoVwy!-vrd}s+pL&2 zx&nz3V*SpR>2lljqA9+ zwNhZ?0mseTl`HKsndkmtRVw)Sja=|w7!OzzIJ%@(uja)h5K>|r6AXPWwkev>s1xt# zP_U+rwo>aRFZ7=SQF&F%gzKk^NGBzZhm(3)4IX!4TeVzMJ%};d1J$Ne#L!_{Y{Bv^Uk;CfW|-!Q#kf zTWP>|jw@^9t#5T`eS$W|_1FrGvsSNG(~+MbA5cXBdrl4wH-GdBH;j_2GQa^I!0=za zi;Vsp9H3vfXGL|UpvEu-{AG~~B{j42_C;qfK-D0$a}?S-Ynwi%*MzUUY;DafGyBvu zb*!Ne@5+BE$xxv0X%FugTI^$U%S^Vu`tS@ znv%AVB-zd&xPP514I(w1YuVP=)LO(^g!I;X&ymh6iv^n7?q4H2Lzk%oo)exX#(!Xr zH&*lNJL%oo>f4%f*j5pBBSO4HO6{qXc-tFX+Wn$O`vxHm-Gmi>bH3P1lU{GS*7l<4 zH{9H!uYdPM(FaJ`bgb|1Dyx3D5k)}_wAI~NC%gr#yMLYunKtwHnkM$u3AFvA&;d#f z7aE`A?XQNVR5xN>%LLS8qQswVwta>p%G5Fo=Y0)9tpL`Lf*)>NLjrQT=#IlpbIBF7 zaoXs55Qvru)t@JkT8!k%bq)*RM zi7DWBAQwEnpuw=H5Xr};rh+x`_N!Uq@k0EZS>o49Wve69*Uw<9 zyI2)D!OEDNq+6HYMAzOK)GsxuOvE=5>z6LUDk>8(I9$b%oXxX>s?#jRgITVnv+G6wNj+)MJz47IBxNmOGl{I^X{jsHa>lv0~rUNSR*>c_;wZN>4z8Ph_q z`hPLOAmc<18O6W`)!LI50at*<@tjr7leieF9~0>EV^XK$PA6o?0Z$oX&RENo(w!L} zm#S9@bu(AOFg}(g(5R9Vqj;mOqU`H=7VxFghi1NPnR7useQ(Xxz&pYxIN_lJB8frc zXzm>(af;KJkZj=IK19D!{i^9p)^*UNUw?#KKc!4Y%joz!z6uYFwSkBWaYMf_7s_os ziPItgm06jxtG=eh;^6D+^Wn+a;pOqGt?g~JdCaRc}StPOTZSWtb$g;Y4G!*hth`J67i{QB(VZ(kqdJAZ$< zf%g;JVk0|yVE^uJcFnwpUmaXt9bZ2$i}63rZa)!+=clI!*T?$zuuS7o7RlkKAMXuE z@zxX12+P9q37s50{%D{-+?zPH+xS`IHg;@#FpfC8{^_SH+NbeI`iKANswtp8Y2pCO z-l`5|{;1};oB%(Q%jgE8Iwa=!sDJlpw#4W`AWO|1s%t=<`j{Od`_l~w$icIwd$5OX zqgc(8a8bEAA1~QGAIMLR9Ei0?O{)#u!>=^6Cl*v5R2uR*1@5ok<`#f%m@h(!dg7;G zD*TF-9sNVn>h#L_>qZ7&S!KJ$X!RpAYp-l{9GPAAUfS6M`!^!jU;h};@_+Rn$jC8I zKk5XGsrqSm*|T)N#}Leawb3hU`o0k@Z`t_W`d*`rqWG6{c%V9IKXP?ycW%_=se6)do3qrkH}kF)4~2GC-8F+tEb$^Uz%8%H8mm5 z-xIN7-b@3(X4Re|?SJXMNS#*>P{mHvS2I#YoG$8U&u{f8ol>Q%r(Ie~GAkWVE*?4I z)5TbKgs00VmicC3wtZXQg!wF7DK(f4na}g=POenfKCA5G1Xr<^ETj9ZBcpIzzvC0= z1A?qbmV9JtKi z47dFpG)8~b?~Kg2%L?3IzfWS5#OWJNY2fcC9R+su@%J(ug3YV?_EqiUSvYxAha$`; z(nD)2I(SU}`t#hyD!xrtBE#>TF4=u5*Hynhv)ytGk61|!=rpM8>C*@l%_bjro>>72 zm}fGI%UPYE(SMNZ+O+&jrs2}wAb;Zn|M&Y|BG(a@th{KK2irXv!>w(-&9ZLn{Hx6B zYZ%o>?DPg*mm1O^+CudoX+L+pS5CP$pLRzw$z{Ov)zqc44l9=u%9xTrJoO<*FertZ zi+9FF<6&11xr@;Tx>Rxg`&lPtB}GBb>)1Lhmj-~1!4u<>P6?Y z`5fc{oPTBrQB*RI!vt$)U3_)1Pkvy!1SQwnlW)V3H`HIL+}&RPlm4E#l#n5>kSUcu z*PoPxi>xfXyp`a0*5XElCB%4x)=u^Xul30<^G@S7lGblz=e=rO zQA_$O_oSYZ{)&cU1f*xA_&j!>b@-O%m<{hdpPKHC<6fSgbORcBE-gLZiKSO@sOdTG zD~ioQ+LkQCac^BCotEp7UvUg|;j!)+ zYJZ7xfF4)fR&P-BhBpv`S%>+%oaDov7oArZV^vCF3rH<_j;tg7>yFt_TWiRD4(HSS z27##ZN*_qIHqjcnm)d9T7);jaG~08(>8}lrv9GJBFp2#f8O&7~4}b1vI^9+y9*^aKQ*J|$^|T?j=qJ1o%t64F z$a1G=5-@{P;9UkpsN6FP$flKWWkx+t+GI6i^g z+L(@B#Kq&Ty~|!+Kf{pSYgDFK!9)alQ`)l{tma!Ame*^X-jxyf#{PBo zrpCwi#NMAj{ZwCX;IdcoyX(u}Mg+Ed0Eb5(2{D=-?;=~GunfO>Z2x*_&VOJyy`INo zINkFxyFhrq;mWmsx6Y`uSIPHkBAXQcU5jTXbeqkKR#e*%*^HQ!vkHB4G1MP1{07pw z50BGHoXWw#p-If;G)=>q;R)hV*r>9m26sc3ea;pcE|6L9tG~4)Yr3|g zG^1*_R}-3Efv2&1D~@LVe}7|JrXJO$pAf)Lo6|$KWq5IUe0}}h@Z$31>{|Rp&%Qo8 zzB)X(I6fMFeRgtqeso-aadmod_0{o}8!G196g_o2j5s|Y(wQ#gy!0+4oqEq5&d3!A z&?ZGFgL!MhPo`6D7H3z7=8=gat11*YAE=-3!YNZt+O4V$>d88ZDAmcPV+>d&V{$MvahB>hQc&K>TMh7@IN?cpp znH1PIkW82;VNR3xxp?QMXy$-pD!yt6NKa_&EFRX%d!XxZI#aFA1l>5Hn-QT&C)N46 zG8^Dvo}t;?&k`hQfv($?-Dw{^*`t>wxO zSrzh#{0mfIL6_h#)#;&vO*xtFvuOrz@qL^mSP(#LrSg7@YAsZu(TSn_xG%0i0@3KX zTIXbajh=Wh@DhXo`wBE?#U-ee8TKv$@~2paRXkLTcveps-kPxTZ7Mmf^ImwWEo{aS zy-x%M;mIy!L8nPGL^|K5RxY@^Lo>tf=#Ad?%DHh0Ijn7EuhkF8bot6d zyAMt_F@FKu#4zK#+gyfkyx958jaoJuw?8)h;~#~=1ApG&KfHGDGp&yfdA)vIw+p)w zy8}8%`VJ@zkm^!Ps3Uk2&k5vu^!L@@PPsGy!*XpJ1Kgrh>ah^*SV^tMwDOrB^30%y zllQm)635Je7a8RGp<)geK>C~OsKYHFFHwd!NL=&oR?6>R^LkqKw9BhT(T!W6xR zu}tl1M1SA>q6O5rtzTq&yS=FPwn5V#YWq`P9L3`?s$9eQ9Ic;^HD{D_vXZYX@=i*j0FrGHM* z!DV&YZDQyWKtsZ(qoyoqp6d}sFx!<;-jMrRMi(nmyAhA=t)?$JjvkUps;?nzR1Ipx zB*+`0KJXJ%c5G2w?l7PgU(1YRjn*;jhsJ-+G4PXL{=?oA{j5{N(A0utM&G2%C516? zH%h#b3&osRmXx#9r+;0iT=>uC zRJFCtsp_3~TOI~t>ru{CPrr6GS~RznoGUe)TME))-gwu_d_|Q|2LrznY8O5>M^~;S zF_l^rC6IQEW$bv3vr#tbotd10hXG}rb6iz`W7vHa&c#7Kn#NGuy3FOkFBcOl0i{`G z1RzA2cA3D$DcJ!jStPnI@P8-lP?bbojZhpk7LFzgOy2Im>~Ycu^oTaB365*()L_+U zUKsFzau%jhUsn#%>&>Ogk~?G!sR%sczK#-Pu;}W_BIu42jz&@`87(KWd;~uS5v|mM zb{OTctr5!nOv|@Bbq<&pwY%zhLhtUnK#BE5fWL{tA{6L4WYmuUSbtdW7uIl_MO613 z7swln3jSMJFB+r+ILv84uPZ4BEdVAW3E@d1%0_)t)^}AQ;31Xy$a2q6y1Pr^9EbzL zbRp6#-7(j0?iE#Mwvc_L`kM4FPy5Ln48bvTEV`p!cePM~&&Sv~EX`lSayZTRODlQJ6jb9YtVS8IbccfFI_!Wjhe^gS^)lh#X_XApvoZWLgWwvo5JOh(FEM;lw^K~ zK57_8)zqFfR!HkhkAO9CQVf}&;I`}ifWY34R&F~8+cd(&Wq;-uCn__)DLIlJz|T2` zjchhC(?{nCGY?ST!$WYEFWpz#RyqY&?Lpfom@He)%sGD=B+7#S+bme#2oOka=h11b z;E9)`s$-S*ziWQ80T2gO5HwH?_sG+0{H6a}@T|ML;({iLY|dWenrdmBVmqbxjBD<$tL4Riz*e8JQWaCK+dS-`H1(ScVF`jby}?Dr)NHd1IyeN@uNuY^lq& z$u`Aj<~mLqRXC2zjdSBy7This`2xuinmd)A0tUL|r>oyhenMXpyXa*b+{ zE9*rrt$!G~r0{Qh>EEXmntW=h$+ZgruUnO}tuE!7l_^)&rd(Z}a{c<0j>*b0U9a)J z|9~0q^#!kEb3m{9)?RQ}cvT#$3S#=kB33jK1XnY2pW?STRw-D}=VFh72~4(qr$d9U zr2pE%eXHX19JFB+9vP=ZyCce29bPxDm#$dRjDP)@np!!g-YUJc=Cw^{k;@8LR*U%g zN&ey9tJfWmH86A(4Ol*D-Cxp|#ZCqn>Rbb!WdqRpkOdX2XEcDn0fuElF}+QCMIk82 zCFG>YQq>b_cn5VawTZ$=EmuHlK_)plGICF&WCaM`{J4)(RrLmNM8vx1q~${bw$Yv( znSYpr1{-YsO0F`J|1x?_eHGtdtEm}XsQ`&d zsv}o`V%@M?gyp9N-IBuYQL12{8Aoh&a)gyYebs}mZVVYhQt2s$wSE$6=4Fy}k)wxzz?=Roy3zJ=DufK0InCLJskn)w!Hrj#cO1(kqB5gw8h;Jd z8-S#kvZt`7R7-CN94ALTu3=9q5vNk92~q5sV#>K3WqG75MYRQ0cYvJ7ex=}Ck}RPz zlf#6z5v52~=6A?Yb0}ja?~0XM;<4K;Rv?RK3N!jZoTVqvfL0#n#3A_JcK0!26Ep&9 znw3dJQjGaryj~&SCr7s7lrt>fh<{K_rWqN+sJ(x~?r~%ieVo7=DaDs@@l|>2NkP6r z*(fGa-+{myt7Rm`e>~z?z)@g&W)oXA~s3i0bAA^MG8;wE{t}4Jmj}OQoLVq5DyxuW4 zi}*q!*cO=vkPri~Sey+xPRT@H@A=^=);*RvpJ+HLbCu;4^RRtxnnIWrc(3zr4-pGF zF8150ydTDq8;xQ{A-2ot5YUwxTkktKu!eYF`4&&*l@AIrxL=a*E+%qby0{ zh}_P3?{n|p8K8o3F7fMwS@_n5^qd@t zkHoWXPt4*po|QB4ti6s}GblJ0ZWg|!0-<}AI;KyHS6a)wrF|VmC4XK>=o+bq=Le_9 zSBJ*|A1)Ly76(^?7h7?&-4Z(Jr!C@#1T2qJHQzl8-$E^P{MqwDXt>E^qGHHz2g=y{ zB}t01Q&XFW6GN8(v61{lNH!6;rLa&$PR7Wu_; zm<~QW1U|{w+BFx6B27qv#h6qsG`MOOgHOOQ>>Y+EG0#Li2M6EcqJN;kUM|%-EbK-1i zz4udzoFIhOXof-wRgOj!M@yI>PRd;3EyGcgYywI{^3qRt#$gdA5QheCs1N(*_#H}f;i>8`a>YnLkB(rXE;MB^pLKR9R^>ufbV}Be#`dncaL@f|T$+EGN zqNdYb=oGHS$K4(~R7WhcLPC^eA@1T33)j<4hzn!aH}h7>ZTP>%Mf{*_(3H(h7!w$C~_J39V$ozEINYdt?}H|*MG?T+BO zJQBgBhxQ$29DiC^a6v-kF-09ET$F%1Xn<-mMUy9-nF(lrzXf-Gw%n<#I>*oGUk1Z< z_J1u;p7(PxU~ujUoxPm@Lh-O(0KLUOmcEZQ&24sdmWL}=rIRC%BiqJvwf3AED7<*# zJM&a*%Z{%%HeaIa)^?}Alja#jid!gn93~-h5h3Sm6Mw<1Oo|v|WN^$max$uvdV~)? z-s|?o2|CGvUaXi3H4W#$eq=gK-?vPhL>^D3g-FZUEpRd88kGB6j&ER- zv0(b=tU#}+;=vQfveCND3;pLnR9@}Z_-h^qrn+z3l{DB_t6M^uX4{Jaw_rJ|Rq z`+uFxkbmw0!QMViTU`a$C#2{GTbuAoC$%HgdYPripjNc%k^8T%RfMd^Yp6MfXt^ z!3u9IcPi?FB@~i!;t~TSLe2{MqB9sE0)>{IvPIjaHQ>J{66Tqumy?&quFJGDzc1>7 z2Y|%%bAkB7%B>Mt3zRHGP)su=U6rkT z3r@T-$i3lhb|<^;YNm1)@?5|n>hcSo{C^14@8A^lyY$<5-ZK3=BE=Z#kng29KRf+S z%<~L_U;L45PdssFP*T)^bZ_#MVuC%HlQN1er6A?!WeRp1R9nFz^~Kd(j^Z(?+bCwq zUGCvm2WMX%i-5#>wMdKbZ5MTQsVe95ENAT%2OGg`_aT|6M`5V|Q$Vc0C>&{=2}PTg z%Ugd4OLWeroWlr4l?{Fo8QbJPKW>6n7NZ?m$3a9N>*ZwWcW_(*7BhG@2U{xG(rxIdI zY5i6gobVR)6h5Pph^(=sfYu69+h24!mP7Rad9;uZC>&hj049cfWoRI@o$OQI-CPbK zty?*-OvZr`NjwjPlF4{5I6S{R9^$R^^O0aK0XFNL}bI-aRpvhvX*RCn>sm-9K9h|ikLN>=;sqU zqyp>j*vrk~8gw2@Eos!Ik|zy<>eY`)$JJ6-d?K8o?fTg%!mh(c1v+aF5FKb|B5n-` zTFFEzMJ|XB3B*bV(U>{xp}?T(E|`BG(1SFy z1t(YpqKHRj66U=oo**_FDkQm-@}4*ZBJWH(wRwQ~g&w6}D+<^pC)Ok;+hLK%lZnjN zWgOU)wxdU3whcKtx*vEw0?lA_{V0|{yHPCcjH8BOf-Eh?o9k_T#Ce{e6|16SA6Hly z_rR11*lEa%9A&V3V4j3~!rXr=*;-{T^$wT;7_M&@B1|!LG7scu*4blEdAMgL%8NEY zrmD>4Fdjp(KL+@A-hepj7>)tN;6fc`tYuH`>H1{lgsk-3|YTscvALn!0xG29-%b9)bY8x82{o_aN z`|_Qy?fqJIYfT{aK4*WqcxPA+lV{`O#uAc_E~Hf_Vi+%P;mcVolm8l}xQZ6Co)5|4 z*<97eH{i|0Xbv%)o*WF%Rk=(9Axm7{9*KEKP-?gYt{cWuj>-Rp38ITDG_is}N|5-= zVpHQ8BWjV93qBtjDSO%Q5dncqJ~}aO5fw@^9%!7EX|zS9l^}oQfXE#dfk~34HG`ja zs*1cEu{E8kRU>%&@(n*7I-_!jV1=#Xk+M*&s1>@Cf}g=COULn~1gx#SUBGK#o+a^U z!7k_3oOAFlCPy5|QAEV7Oi4)(&~MUn^(zLt8(12K5d%+mL+T9ITn8r)%ZenRT~qU` zFJ2v;hyvhD>CS&x%qKMpAR203>||xJkE$lZ*3Ji#nar!RS^c&rWgTm(-F2H~2^H`< zbaohbwPy->p^VR7Y|gmPs+FsBaPBu?S2`DvF2#KRByWEmM$7=^iHXP9_=~IJ(v- zR*Y(Yfg67WyDP49@(WCz!X%B`(O5sERS(g2V{p!Q9)xSeyYvQD26AA?aigUURVOp8 z=Rlt?SB6Hs)+jnw;+!0j(*#nqh@5fiYeQ>_H7HC`m;@3>-{Q(m!2BrK zmRL>Q!$T5T=IyY8TqJm6`uhRJzhJN0fP|xN*57}?U3&<}C`*a3Me>96r%4$}<;#k$ z>)#ux6+OrX!QRETk{vmzw@ZvT2#2fDC?I2NUiA^O;$wEy-$-tI?QJ1q3ST}Z*G~|5 zba|WB>(?xydW-4~^zV0@R>y)t`-?U)+$onc&*fx@nf(LN+5NZg)rZvQd-W0a`d)ps z8+U(tRh57awSlMmT*1vzZi1T8|7et|Fo-m^^`zIB{N&~L>Z7ys>+jV^G}7Z1)Tp4~ zXjf-%`WD;ZnsSLTgyc@R`)@(h%DS)&Y_R>zAD{J7K`ejsK2vmdVOt?d5TprGMP6j(d=9?IiKqJ0 zHf9U)6zZdf0O!D~n>l)-UB}&tt#?*dsXC#1i{tz)0awLG_OZZG9n?ZS<&U@K5meB?Qy~} zd~-1*0eE=g$abWWA6{CC5iE#Q_6=esiZV=Cdqc)>=CQciTg$rO7CAzwLg9x?58*K2 zvl#36d7zG|1Wk6BFPOpNX&g~LD+_-Gaeh{Th$CTo<(?<#BcxHi-RQHsr72Nyj7jol zh$-0eh(~KtA!>Eu2(vkQt17Qs17sLov^_*h8&%;ijGo2t=L8KA%PJshjEsNt2M@wx zw70-*XYFfQR`*>)m)?x$xI^Esb5`Vr_I>lY3 z&jGxO1t1E2gBi{^)e&9_I&U~nHZZ_xf6q%F3}6LxIOg^TM&YWFF_$e|s2`IdpaR-3 z8jqFsd}J;Z^~{-j-!>TFx;AZoLK>Kt_gpxOHQ6Nn<6Z~wJWNAh=uLmb3-&?US(G=U zG3K!8qg@lY+<38uE1T9p*3rarcQ%hgk~NqF*0HJaZH>NF?#-!sxSI_MOa z!SwE?E3U3DPY$p5-Hw0#e*eaCH=`t~Hjqm}7E3A6)=iE_A%$Wg&rK_KcO*`M;*z%! zu~P}A?oAxRjkYMjF-J$I{>KKs_Z6;_xDj~JWg{I4p4tz`(7?!!t^M!QEfL5x z+R4T{aKz+VwnYcKC4yNT@pF@ob{p*MN(&#jY7jm4zgHjbqD1#&(i1z+dg9{p_{-td z#p%iQ@a%ke{OaQRJ5TN~8BsA@(#dMt0ciz>qM5oq%5$nH&*`>8EmEg0i+m~38vwS> z_?$)Zwwz2T)mmyLaRSb$PXjUqj_hHezdM;20`CNTT+K}y)&Yv4UG$g59CM)NfaToE zbTkFsu!!Ptl4ih3G~{%k`M#))(OZ=1Ofm600;m80001H zZ;~SE3IG5eCIA320GII;0W*K?T3d75xD|eue#J>fv!qg4NjsBvMqbQ6!Y7N`kXiR^eSd(wv{?_8=qPSwaf|7bNF*pI`(oT&N8ez|XRV;SX|>80b>?RYoVl9X)^E=W3OoXVy1 z(8fJWDQ;L6cqmEdbcnnRze@~=sw{Ixv%q^nEVzvpLk~Hd(sY^TbO`6@15=VK3R58s zlh3_WWX<8uDAi4gg&N*KJHcfr-1>N%C5MQHQDQH?_x zH8$XO{b|Po>1~=<8O;*CEUAKXp)}Y>|HdBVlR@h&3_LXG*PV>jDTevhn;Jye6 zk}8qkP-%ZyV^*jpIZI8}XDLi8ZvAKC^FiG0bd2Gg-wwK+&PKNkybo!dkhQDe+L2>a z8~6aw;`qu~E-=UeXjz#}ZqEvI2iCLVK9YD{4od}_3OiwVQSxn-sbYU;B`8BF2w>kUBG|Gw_?W9)7zAm2w4mf5*v?ucXC#D z8@_W0y9L`n*qQ(nIu(*O_9_u|llgpBas5gJ{rzsIbKgb4+#(u)E(}(&&r61?=XYCz zP$yTCk=`=dI}8)wV9ivPVNGl`+Q8VFA#OQR;6G5yw8Ai~svFpbHbZCbY?0f%@*P{7 z&KZBfELvAvA!xFEx6|3E@7|%JItfJ$0c@$TvOg7k%4U_Mi4!jIA*|vWlR+KtE4!VS ztixhm^HD%HV$ecqCqPi9w2~5;2hks#?5kn@IyRcP=nXQ1izM^H)Qf|IgCM22*I}f& z<}r-5@Yq{j7HzHSpQjfcd5I?IOCr+_ko(bUFfEGt(F;I&cats zP1&s8|7(feuK&8$t7q+|zt6;_q$PimbTuigP=BXf{6}Wr@H(PSYDrQLUNMAO%mr~z z&zWjth$^_%T7F#jfpr~))X^t6I)W$~KzrH2X&D75!{h85>VSE$*kWA+4~Df(q!5{e zAHl(4om#>63b+(m7GeFnuol65(Ni$b#f>C1iFksZvnljEQ+mn0$W+MWxN1 zottBSt*B&FK|wOCSyEIpY$cfTOe^JTnami+%!paiW<|y|l`fC26s+2)GpZBIDejLp z-K^aOK4xL^*J~}MU+0yYLrHmtxz=gvL^RSUSTH>om4*dj===tb^Ec<9=9R98X**E6 z1D)$iiVecZ&+>zGPWUWb$Y_5~LyCJC3yD2NN%S0>#++z&L$USHbIN4U6rdMu!)A&) zAuQI}jgPysvcd71xV&~Q^C|GHFum;^8qP_^q+Z5mkam7ZrrKnxZ)6^14C|s;QUkxW ze9Z{pJQr8-DTYYR$=_c7JsQAoO7Mj8RAiW#?G|$vo&5X#>G;+0`RRX2GJbnJesem; z!aWEk2WugJeDZ}Zl}iyx#->wTXjpm-`%YrZGteA#O3m5`W~98d!of4I0z#+Mgo z@BeA_@C262b^-_127-SuW@3okQP%$Txsf}-E&J0V$JQ=;gY?5<(hA94ueD#T4{}z;KabWXleljxMjRgDri(jp+Qol%q3>z-;ob(S?^0I_ zLEReL77t5WEoQtf0v0Cd87h`REznA9*(ll{!I7B!MpNxaT(Q7Or|0|1-zzH7=f|gS zPhVY{lA7R6_rN}4&&9R-g8etVzWDGCK1VQIJ1p+Q5F(^hpW*7w>BT9Wo!|gvXuZfL zwjEkbB`)TyTz!A|;RkpEv?z73c4VKfUwnd*xfZc=dw(6hf@<=e(QAU{QS8j=U+8jR zpKeHA?FJW;d&InO-v*n52u5Kh-w2VWvQYm+jOwv@UETeMKz!;z)Ox7Nt>B_q4nPl8 zUdmL~u1{rTPq7!Y?1r-mg-5R-Zv@`0#%ov>dk9Ez)@A>gon=c#L7< z$nDk=*e^!@RIrj=CehMu_> z{xe>Af5Ie9wUEn(pl7G@kFODsrP8$U??$8Xzs}B+lMk0;-xmfLHA0|j2xl#^?e1`C z?~o~E9Gmow4k;7LGnL@fQq#<+Mk1^&W(*t_nY(}P+L0c6Yx^AP_84nNJ`%T;&R@PX)SN-BzO(WOijb<=rxE;-O z{P2k3$hVIeb<8~wHFnnn=dKP z_zi!_S#~UE6<(?b*OgE2%(3@SR7%5yn#=Bq5_zTF-LY|9Gnx*c`uoSNv1NLR;!CqO zfEWLG`EuZY*iHxF1P_JkHG}d2NJ?^)x<04={m1@Sh=H`( z@U24ljhJ#bh~m6XYRQT?ILFz@ILdwZzfhw%nGOz07chq?JOBW*+yDR}0GII;0T`2B zrwxB=lN&d7=x5EZfR$O1lu6FW_U6Iau`FBm?$r9BSlV(DNz1{o!67ouMsK5ABU(}_ zpY9L1m0y@&a&sPd^@B5#?MhwORx)A(2f)F>!Fl5k56ew?aCHCvfw(Welhsvbt_$(B zHkG_o;_Kp47wSOZ*B_P4tdK<}t}9(DD}H}6RraJ(){4^D+6rmK)6g>y7x= zN8f!b)|Dyht*mQR%>mdG>QO0SYg4IAY_zImwc6;F%=6tsWV%wT+EhDX>#AARO{K(E z)~k)OqTVPR5&)I5Qbj#mZFC-h?B?PL{?0S2H?kHR`AP|FSE)i=sFm3&Ka5rupy7Xn z>y6TtSec>*2)You$kglkHk&ghzR|f7x!jqiUckSFc*CuinXcX)y@64e^{!O!j%?ms z&bOIJ{`>F${4bF!xyI>H7U!iaN%eJ<8o`3Un!!dB`;@PnJt1wg4)?y zZAB^TO|lhhQ*EW&irH*Nf9Px>KKq3Ju63QOg}D3O4k=GOgMZ(pZ?=|oV;7=qE^=)* z++kO$g(os0WK(ZUwGa>JpP8%y+$YDM{Qd0sv)O}B#U~FI5B_2C**`MSt;v7%TB|H> z^qDw5UVQrL;`hgHEjm7BEmrtKOdiJN0 zn23pZb8tt!*7eE3JKu7ZOKX?vwYIe#i_uR{mVVT40vRozR0gTT!>QEY8eOTSfe9>^ zkI#NwF2!7oju7DNx}3Qs&mwZ1l?-QIZpsnP1KW#uT*n#T930#Mu6ch)+!tR;8=s1` zsakw+4$ZLX7q4Um$3_>I@b9C8J7uL`$y$lS=y;w)z>!>m8m&;T%UrK?o$rKty~>+R zWpqCLXqINzJKpn`?GYW2C4oN`vZ~~6Jc)-tD`s1@g=HbL5r%oz*<~g+{pk;QX{7Z)>?|C#*7&aH!H{#4cQ8*mz&o=|x832Vf)5-VAV zizdEd;F`jPN9*`e)lF5{g&3QQe^;w|BHj=$E96!!cv;h!y)AzPF4*&8*GRlekpp+4 zyvS8AO0YMtxq4sJY*j5g$nXonO@TdsgAJEgY8Tdlzr2!p^I_A*?jJhsT2)(nt9jS* zwJFRtnm2LX5126ydb=ruyiuykYLs&Dn9PWD=^P-Fpw$HWd8#i93D@aDteav5j8u$) zLU@mL=FWyVtWkdRq`;TZ8k~h1)bHb>M3#6-bsU zuqVP=n`))3(15C9rC>rMOY#xUp*CWx;by@<&z;nBKAC@upLD%3O)ct;wg_uu%oWLp zgngLR`O-1SCBR!EfOAoqYf&0qz_2xJhD6y<)(EIDA~(gQss!CXz_>DPucpXI&h4&P z1+XY(jwk2P2nH|IYl3nutF5>&rWSRjWKE*00htO0L*!;ESBNrzOP#O7XF^b=>_+Iq zs;XwWtf_wrpaW>{a3<%YU%UoNP&EMlpBY-8+V&94Wny=hg zY!xggz%{Z)v}@}zpbJ~WhTP`LcV{_*y>zfo#b}hU7E_S4ZK?qJQJxb6qzgRKream> zVh19ZZ1S2fX^?5!mw#=9DQjU%wbE<7Vu+@R*wYuKtmM|sS|kz+C^feegHkZhUv_2^ z2|Rzbae~esIR_95lKUz!&E<0C&^7j0j%GXM3t~_LA~Es~(_m|zr=qGWy&bnej*doR zauRxVV7Ep)Ik@vK+F%esoL(#Z9plbO*aZ;f{pVUVYlFxr+ z9)v^d#XLV_`0uw7`O;o+Q@S63yGDo^SrM_bIr15YT7;xg0aYtd;%XzB5@iXz$W2u# zTbd$+d4gD_ivgjF1OXGtMJux{Wu>httguRXTMukWQ>-?!xKx>_ByK~!kwp-TNWc*v ziwcBmoK7iqbU*HwY&u7R3F-`~i{*dxgt`W(K9(WIU3fhd-;z4GWe>H>!fXA#A_!cv zRm(0rbYma7PEWl2vTs#AQpvajs%LHe(v0iTTH{Z6uJ{70jgYCx5SH>K$eD0R69qg- z8%W(?hRU1_&YB`g$JLI6kF#~SWH)%|>5NW=hQ@%vTERI+UVu&U`^bi|JS2aa*jz8A zE=Y+X!v|_P(-cO~X;@t$)1K&A15>G*E@K%4XAoR`;?%Y9Zp)QsryomsLDtcwHsi><)Ib-3SJB$sS z8whak;xT1Yn^{)Lb#0xZvKv)OsB)vWs**X(;E>F(t(EYPu$Iw@o98G19EsD@Q{npV zo6I3vuu|wKxp~-!Iz!PEg*GvEEzcf9lVdS)c>Ie+y+vBwYlN|O7n6TpgVu`<3pZP} zEo(q2j_(oXr=v6R(2Irm&xII)c2?&qn~lU=!2L9ygoW^xsI%@|W7#vEG#>uM!O(O1 zaUWoRQBFSF?+Cxo zx0ye+$Rt}_jnrkd$mVc$?VN#StmkS@pB;ZDDBh7ZWjRb*&l?@9)-|f$N@ggw39B!1 zxT*LS0Wi-1@|990Sz-FzSh^5k*zs<_$f4-@jN3_%*l;WY%1(cy?|>^e-BHY6h^!Nb z7yyZ`dB4GEW{I!lv16_28DVNiK+e~bvJLkua}MhK$Xc}pnZ-$82*5OT*+Bohv)SxL zarY$paqg$4ij~RKawoU>II(3g0fr6)rU#Il(_Gxeukg82w#i{|LTck@ZebLM=&4{h zQ}OK&U;TOc^}m0_=Zq6RlRN~%qf>*C6~{-6Y? z8kUZ0U6~RaqDaL$1^}5RXS^))9U9dL21s6+D(j8dsHW1k)~k7Io20NCndj!3`G_CZ z!5~YC$drF7RmIz|GUZN;)%jZ0yB07{kE+Lqt!dEVvuO(eWTI zcEYY?Q8Z%Duwg&{aQ#xlPH`<+f=XgD@Mi&D70AZ z+&X{SR1?VK-GL*zAJx{pBEJMFC+nE!WnzriHT{bi4IP5XytVZob+`WBz4kBIC|s4E zHol<6k2}P3l57d{*(HjmaKYL!+~vUfx?QJ1I1@2dzl%ien3NeSEg?MCSRo3^DXg>u zsmcn($%=KXvc)Ey5RTy-mrfxgtqC0!tk-|da($)vcT~pk8%pZ6SevHEhO{vtC|1Uj z!6ysxkhQZn2W_E+Rd`)wWVN~2Ep;}II>$n7)b;IK@e$qBE?Ohm;Z(PABp9RG8G2=9 z)sk!tF@)A8JSNLe@M8BH4PtFy2W6kaqawj{GWE_k{~KAVYMtjI4yKxt<gxr;z#kIAa07NlysrSoqA3aQai|~s$mUOPN`UXk&kjl93p^#I!(J`(vQgG9 z-S5492AS6(X^t_UI_PrMzT3Mgtg3&z`6zwXj?m{`E7pkESFEaMt#?!RBTtXc0|IcQ0)Bb9+W6n4VnkI|;e=K$MJ#b`MSD1BdK zc(L+bAkB^x$`?dTo__>mc$YRM+Y0DHOJcEj@SJ2tD{%RJv?m^S<4!s3f@4p6Oa+U; z{&^;qv-8?1{X||9GEc--5NLnt!;n$Fm38<$vORX+$Dl&w8ptMzdg@UZ{wehO2~pOvx&_l{H0d!}(Sja#r-~ zTV3iAU%N&1wCrj}N41l1S zg94u^@TZB)tUBBYFR|A8;eN*TV;G7vJn($RYg9{s2w@GAKH=>7OYz9LM%x0K72~GR ze`{C>oD8wGOI0sro^#kx?>tZWCZD)qwl_V-#4b~4-=&d0=dIdc*?^;Dn~V5Lgk8ro zVhY95k~=JD4f=3?~_%?<>aqkiC>Q- zC>GQ+jBaWQod43vIzoYO%A;Ro|R#mS!*FqPsWTj`FmQaDqc}sSyOs)qr8Z?87YsUgzRF{TjYL`O}k;6^F8_p1&kti!N4`+9K~kvrc=X z#5G!7EVwVg6Q6&>p8c#UGdp|C1~T--<4zHbE45>01GK*~c@8`o$t6OtC5BH9e`4zY z{h$9u5b8=_UMdtp?*66V2zEZ1S88_`9NJ)xAnTSCYgx-&JlmD(>#8z54Q~hx)Dlt2 zMJ0>XMv#pRV_s3wEKD&&q1j_3#^dutz}eE{Ol1(8@=||*5vAhI#rtpWTy74FBDMA; zMBo0Fl2Q6?Wr}sKf%l2D-$B375yadelc5{xG!`}5!m#tk!M0XK%%v^Qu?dikNXd@_LaSyCL^cwh2%9^BZFhxrrYthVk4 zZBx@6WGgrvB)bJdZmwRb92AzZUV{UfY*T*|Z#j?M|1E1!tk3p@FPw_bL5jhlpXQj! zEV2$T_rGj%NfOB3wY8a`hXBig^|4 z@cc%TIT4vDMm5TjLe~r=-J9EPv`1gO&7M;p1XfM)FFsP|#50`ljngo$2yyQE_maKm zKWC3;q@v(`<++Ao!8(hqT_?%`w)bN320*-<0;*m*i5Ls#GDbc{qYPhF+^W&u}>(+J$gJ0a>~M+c1Z zi5VTBM>?%ipgze=7f(E-1EZ?R!4vrRSoBSfS=eEruDJ_K@o~Q{(~jDIF4}*C27Kkl zstV6ChYz*KKS>%Qg*%M)H@wzG;Xr!C{%X6E9`|_q=_bbWZ4J?y;LwsC8}P~#lB+E% zUDWF_!5W=C7Qf3D;!2gZ_?@Nwt|n6-SMob6?*7i+1&;nZJDPeS(Ea7ivQhl^SR^x? zOgjte4Z<9527!LqZtSA|CZm7-<40H}4&QQ8936>o&86^|nRhMwLFM$PQ-PMf%+2Lk zjC>V~$6qeQk93YQ0oMJMX^Of9U@k^MA9se;v7LylL3JM#p#^95#8PR10aEGKUV6fh z=OV-mN2Ugym42tH%ZP^)gB%Cm2Yej@7bYPhjkz4PW#a^JX;WepP=kLV6J#KxJAqPE z?0ABgW~~Y*Mvy5BY$FHm(f~`Ysz9<^DUJEI=&%yUl)S1kG!e}YQv2O<3(FX%!=*MG zaKxupgtki;4E(vi+|<_!{;zOyJ85iaaZ3H{&PoOn18fw6jf~%rVSvGWDEJp>oTiX5 zPMUyx33B{W;R&A}zQKQ-(Ar?224%ZOkw`AF)Wu4mI0ofLa27OE{(?c>k$*yPZs~*L z5UByzt23-~kVa(vj`!vdob`HJWve18P#rewvM(#8P#Pt{_ zMs1!gBtXd8RLk0UYYO{T2LUZzC&<`q(bE}zQXfWBq}jlJ>bi^A7k^X*jr*%$_r38K zEqf77=EGbB!O{L(82CN(U(XbHJ9FOKx4*7gZio6X3b)Q#Ndy-5C|Jupp(ml?;m>0i z)7?w4pt^ao^gw^7#HEnawG3;y0pmhykQ#K=hfwgxUKy^9vk`aOd-R9ITvQp``7lxJ z3giZdICw6^)aT&r4{Z63ju$6g`}1|H{fn?R?VyD7?xK+Y&T%|+L9eHXVLFrEcn}3C zqdMan4;)a=J`CEXI3@Lxv)DthzF@_)8+%SeCXqN%=MaA>(Kpt9(_#YV;euw?KKnif z*wWuSHz4K4%trwcJ2=CmdHU0y6ME=^l#+N3>=rSOC=nxG?+{nP?wyc_UM=oJIRuBX zM~tG2d)Cu>Rvm?Hs3Pmkn@wo@(8j_~veQ0Dl(r>U<9fED7eF`^G!$x9J}iYJn%ah? zen=f`bhv-=fTqDaeEK=t@^Oq2a*+|8kUMiZ`U`Jx{Rq3E6|Y!IVir1+w>j zK@}G!+eL{e$=yJqtr5#iE~54lARpa@yw-+{VQGK5%m>8Inu5I(H)Jzn{A`-_8l|1= ziP@Nnhpl^o4pb-d_6FyLOK|D&V9-gz{`c<1q1S<9Fdy$VStds*2@PahMxR*X$)Zex z6oRNs0yW1FMOsfF!f!%RNDy8mhVMkW?gi*+Xw)K85-UKg;Z1!^0Rl^dXeNF_8s)XS6>EVK0JW|Z>tFNM%m9R&9I_XxrUGf7_#4L5#=0_w(}0;(T8KpY#ZgFu z!Vm~Nw3K}+gGWiw5^f4GYRfESB5FH9qg(ua^fKqOTk2wMJXzk-Rf(OR2iE#B4siS^ zvH!MCQ*?U!A+-wyZ{H^4(=GK9%&TeB z@X0T{1?w~dlp36(9dujVzdOG%Dtj>5M&!HhWyqGiI4&S9HsrGb(j|0MKs*%%oHMZ{Do}psff+LZ}!MS&+bm71IC5ToxAU>rj6Q z04N>Mt^zPls=N+|Uk4rSS;w_^Ydy_%UHZ#cjE}{ss7*@0lh@B?jQeGWHbI1OoQt~QR zMtq%v(73nU!M^GQ9!e-o5GEB5=J$%6g*T@6~Lth?i|8g*fF@bb*LwB$6GnPKQS7zW#;e%k{UX~s1tWioRu_y z3S&T9m_8TB2GG0b+l*mIE*zHg5VUO+~ys=a+RCFvOKDU9FhZ;1acuwCueL z?_V}zq|Y+M9V4^^%b_dXkklI&7fl^s)9zx#Y&r)clxXnCor~|lxq+n|SQm}5CTt7S zzc~(XqK{i`HM5%b`SM*l8d%h_lX=SFg;=eW{1wu=tbgaPY}X_ULc@Pwu%eao{-tG| zoobXd98JS{*HB3t^sz_ZKjw56D6MAO6;^u-vbiMdr~mw|tMt035-aEcS2iht8mI45 ze+POR6%gZaa4sDd*H-1E$hfd76#34BS7b9+PxYn>s$Y|S z6P_)=I8+zF1B(S*jBI~m+Hkq6@Cl*72`db#*H~VRk{iZ$1M7#_rHw30Rd7i#q6yBQ zxM*rFn6_3$5G$Z=TZ8#}5LMvf>0JRpE_Q^qYy-Lx4yNpIaSXH=X^X14kAejm>y?&b z13^MZ=!P8>UY#DX3>GP>aY;*H+_+Yu*m%E0*>}qKjFi+b^WA@Lp`G@@id|tbtbTKT z92VC;j;;=kHNwg*qzTY5%#FEfN?$1Nz=yd(5C{YV`lI8fvNwUWoj4lJ86~3bBK_=# zYfHGYc7v;S&g5BbW^DSH0bfKUx+gxaHyo(QZ$qFeWif?<^FnG@gCPowbTP$Hrr8>f zTtkKV2&*GT#Rh*}stmCWz_O#0P`!9$xm1Vq}I8JTuUa&jAvFX4aDYFc;sp44=?2BMX&OrXETpqw(+80cCy$ms-x1i4bSHYG_T zn39X8+4d+myN5e^JyIma1EG1fGTUBLk>UuUL&5XPrLWP~Iwkk&B#EpDVRm5-=`2K| z0ifj1mAPOW4CNHjG{2~}?Ui<_D}+4xRzMh(ROo+3l{xbk=HFapV`7VS|Te?#q5Zh#G4EIRH zmlB!87e;_?&yQbrl7;uxMR1*XqlUY5m6kM@O}4DIw~JUAid9LrB2LZrgkcAH%5aKX zJaT^$t9IzJfE zmO=5G2AzkpcH%d-KCq4yS7t_C?i)AF8%m9}x2#hUs#9^Sy=!~R5)K#N>nR!5Eqi|y ziXf&p(S^5?xA7bo6zr)oQ-w=xxF*Zl;{|8*wMcar$ahSQO${iHv{H3Td!JH5s5_%9Kh5N6ls zK$d37VNzrsgZeVAwBpp&=eU$rmV=CGxZELD5A8!dmEb&dOCym>+g6;qBjJbX4~xVP zI8bi${UWzpP~5~$e*38I_Zazxic|f;-nT6I@axod@OSym*LF0oXk9~D>F< zGMFgHW?>3GN=~zkP88J#b#GH4-=XeU7ZvV~rhtkKnu@jMySaE&?4mNfc*fj8ibOEy zS>{3evLwSSPTTF0x&xFW2RA_I(J3m{t~%?RGN=}wSlFX7LhX{m^e8Q{fso^yRuz1| zVxycy*Lzk|I5(|hXuGjkF7JOaF+>deq>{qE&M|H^@w{*+?I|hD)u7WY?U`u2tuD*6 zzwofj!ldg4>~jMbobxJRWm`;N#c&Z*`2M(Etg^Rm3{zsYwvryQ@h|lGu8pN&Jt=Cf z&{zSP-IB}vrnR5xV(?ur6nfPdiC~wf(T3|W_hj29=2DL^PYRtZ+oXSel4jf00n@SW z-HSg4z)6n&mjQYZi~#o#aCjmLrSVe5S_)A0IPi}7(80RZydaG2R>{H9x$7Y6 zc84!?+j{`_)vtDIOVj)ZIKr>S61WmTnrwX2B%^YP2XXy+5RlsP92XQsry%>~=v1M2 z-edP)Q$5;J~i3`6LxE{2gc!cP{=uv3q=U zJ5D#S6D?&s(Ja`(+x73Z6u_2?H&y>6DSDNxp1iR450AEBjimt~O15Kra2})Q4_>CO)fU`q zQ)N(-&jR?v`Td7~#rN&gvFry7mSUuveA2dIL`ltYdPShe?@g^1?b0jX|L_d;OJoPI z%UA;2WqWjsDIHMpZCCo}V%LuL5AV~_Ek^IpK|~R~iDrLQUANPBe@Qu1m>uM`P!tNA z%Az+uD5W6ZiE$Y}vI1*{M@-Q@#Lkh*uhX^9<9cjD|5Fiw2|Mafb23dK9HUCjK)KIPa5#GzBm`3Fc$t)93f1*TFmi z;~=37BF1I=D|_u;5%k4vW5T7N#lpS7_OyQ~0Y#)B+(kJst4zs$sk>Mdc?~K$nSRBi zGa)Zw3Z@d5+La~-ZoaCN#0&oXT3_j-?QT|^Qm^LQ>>RkI73c1!b5|oCGYAg+Gf!}h zLQxp9V$-4^3283QU7Yc`djJ71XQ)(;~lNYg_(e2-#RNE+bq7A6`9gL`!(Vfmj8K!fSg{LTc{@eFr zWeS>VO{HD?i8xfH8g;?&Yn!(K6FuL_&vka^1gh%o=9CoF>Ovw**FMC=&H6Ef4;J5Q zHrNtob9{V!Jj4Hqe0t%5hg^)25EuAlRbaQo)$ z$LzpID!&z?L)KCk3NwhCwmi)xg5ay@ZuM0$PXu_wDxGDj0DWoTb<4II6{fURMYA*& zXu|uZ6gBG*;mT~G%4F+^_2H(~5)cHsd<#inHoV<&jam-C0!*scD^->C4X}UWmIFKI zta@t6zf|Kl9jb!F1UG>zGW9uZI&e!inxi)UCW-?XnZ{G z7BIMF7sO?wI{^AYV9PE5M`&32Zd zlE!WtG`4el?pf=ud*JT z7^xDZ(Vk0YJbFwNd0&hoed~hs(H{L;r1YxZtfer5fAIw2Q*ly9$HL&Y+4uF?(S%&(uot)6 zjdji4^+|rUleIp0#Bq0cquw6^(J+Rh%n(0cu8C z;1V>t`r`=e<9=57*k&Q@`F!4}dlss!*T0wd$hySbtu!eK|13%fCY_u}lkKqLYZEb8I)8EC4b)QaqgVt9M~ z>DP2IKDb5vojG!5{mR~ntR$H856UD-Qrs6_IyLvdAxdQ}R_l9=N}1Np@Zzn}SNLLW z>98`_G_EGSj-&t*e8kjTIw~@nq>d>)G*Hc4d24HrA~=9ONd&jxp}xBp>BkRzSE?$wl+=ojcCUV+}?V z8gzgQ@&0FvePzXW{QzDt+1HoVH$U{bnYDaOEc6Z-hwy%3#qQbjDOD$6o+uVcRebLv z`)9E0F~BL2Nh;ub7ibo~$|NWEsLD6=ms4GuYpYlRYOWRUg76uA!1n}83+ZmE`&j!%Zb zQtb%{)oCqKKH3y=MQG?Nf?}qhV)Sh2ljsU#Ar&Ij$*D7pa_M7)B`5yw^9b>riBTiF z5w~_0D+_XsjtoPsh4we{@H?{e(i}|*r(KjaIMyQa0Vq>hWyhY1GEGS!^hp)`)x@Q@ z8LqZRh{@w$D(V^e5Mc-vZZ2m;Fl114lG1VDr%FQ(D)@<>6YJYk_NOylJg}ZK)lbBJ zy5%>bKxWdz2}^7E+tQs<&~pDdN1Wu)nMA`#OooCXGmWR}UBZP(!_~{+jTo^nL<|`x zxuGb-q|a+aao}wSHj?1)=sY-o%mH)H_l)~)yxiGlRk!(=IwlTrg{BVhl8+JEX&GKX z?_aad>wD~dWH6Ca{o9=(x2Ype5ucK%LskTAX5x71=emLMIH?eN$fG$|SPylbJxHnw zy?+&Hdv|^5*z>2T0XSMhJt@*a`hc?x#|e#$dIii6Miuj0wC_sqa3ui&U;8np@|gwq zg`X*ZLximQdfzn<0 ztufcwmE1I7Df|v8;Ev+%Qla;&B@GZ~x!@(rR9BtMC%;}0=tM(ES_`>mLZ#lyE7l;0^Me7xiK zt6hOwS?-);tA>9b+6f-E9E_AZsJf8;I^gzP^ndKv2r>76X85nx2+`K*wsIXm;#b${ zkK_U8y}7w8Hyy%`KKBe%@tSz2SJU>oi5)7J;m;7CX}n1L0dk{i$)pwDiZq=jO%tf{ zYgsYJl#Z<>?Yc0l^J*6#0G-TvuwSO+rmd@|;4eHbcpSXL%3z&*pdYp9cplYkq$@d- zN8udPN0rw7t?wt+l7buh5yU$bXeXTl0#Ee{`a-DpM=G8V1U4_Un6y3~;|N^j)GRd4 zcue^d;^W+zV(^9&v)XW$8fQsc8}8otj$^%cUlA2^P07-|Y#Iq4I2ho8Gxw$lVEe~k zh3ogz+80~DSam?VX*v?X`OTV2mj$yoxn?s4h;&W#H5oxvZ4PO5AO;yX{F@|~iEA5 zQC<7`G`2K}Q>$`-*9N>wO!!Lfu6%!M30by9A3~>4SmSf0_L4{2lm;_aJmY8*hWd_x zB(B#hGT6CQoPQC~60Mca!B&|j_n_FzUr1IbeDl6I5c>kIW`y+}v3=#Ut-KcSSbVD2}#6iO#j<_Y@ z{!5)Xi_PhS?yu5ZAlK6$w z7%vaRpFCX!-VrZGH?=?b-Y)H`6Th)AvZlK;YT*xXN|+Kj(oVZ8aj4ECbfiAw&d+%k zpAHIz1d;gQb|q-ADWaQr)se8HGNa9o@PTI-=z?$xuzKO6pRAZe*7@hVP&5%lJwY6?_VcBAQ!cwSzAr<${j z;F-U+rN(LSV+?aq3tR^odBw#HRA2wc9v=xF51ct5iW{8#IVCpumi-z&jay`F1Eox& zJjr7O1LXE`L(j8yb0aXVGWVjvcoAqH1+~Eg#Z* zpEppPVz!-e^MXc}_`6KEo4AOzQj~GzePSpINKrc2J`wZk`NGx=G4<#Fatqk{#K5=A z@5hKRnV)||+<(1#QSvl>J+4#HVMAlU-v@SDM5o#{($c#=bu8-dP&bLaJ#ZM?5Lz6k zn^MX1U|PTpwhxCD1(n)S;fOri?Jx{Q!f6i+>fN}5HL{3jGCp*VyGPFUKkf=bE4ID> z?W}T$gRtY}I@C?7h6e;V!=)GNYOPny3VG@&R(Ira*do0%qu@M{&m#qBYt<2*0dC4w zapx?!Ln$MF@J}_!2j>g*ck>x7=LAs#Q=P2Z!3vx8U!W4@B(hPj1=F#H=cC{9$#>z9 zDT55^R4R-!vu8%hTd}(GCra{(4XX$NSMTKh$Xxz>=NJu##)5(TJHF|aGk?k>KA-g#+&lTi!`Bz3MQH8>#H_~P-D95x@ zK&ztylX-YxDm4RzxdAvQ!jR@Z@%%qPHc|0g<}>8BKD)9?iKi} zvsm-KbO#drB*7P+gm;cfTBKML`(Z}Ro~?@EQJq+NFodr>r@VsF3VrGTn2$L1XFc}? znOaqYn0+99u=QMQVDyknn?VjULvU2C&J?=(t4!LiHcc;>(~8{A3s9wdyh!D8ehBDm z7f1fIIRw>Rah;UoeikM4IAuxE#B8yyrTn!?Af*IFDpuyG=92qLdALpBY!uBi{v9H0g6{Ss&#)BSqP|p z6Qqk+-FEJfq}r@yKg54r+2_@jAK)16+hbUpqPyhDEqD56yz;8n3n$Zx8;$s`FMbmS1F>wM<655bZ&%~aXwD*{CbtZQU9$#w2UP~o7q zp%;x5DXMY~dxcCqYx@Or@MI%0M+JskZ|?g9n2@!1X?K%D0sT*`W*|$IzHEOV2j$|I z%piNQDzAJ>799TQSXaJPX38#hL{)$;2#m~FgQ`pN3Vv&1f1@TsSg|`!hbUz4Nh-r* zO4-;rGoM&XBE)Y7K>0Yy;1d<;bOQMfyt3h#P01|v3)-(dTz;aph53^98K8X*qKKrF z^cTj$g5gaS3Ht4J9(fdo1Dj@q zMHG0eM1=fZCqh%w<0-Yju-Pq}pL?j2bp14s*h?n4+bOsTOf4R3>S4@H%QmGVmy|rs zQCpYrK0Puj*woM=1{1_`?<3b07P8jHxbm~?yj&=*cZ7M{MR!>C ztoUnNTZ5MrLC&w&ari)N_K~Q)BbTDyxTCtCh9g)5oMApoXHcgwVK}54L#w65+D2TU z(kVas{t=ZS<6(TLRRIxD;43M3l$`HK0908haq)+MBl2-z?;g^O{HvR^u~b+%t~#h$&<`_6M%pHNoR&HLfx zeiHnF+@bPS=i7p>vR~Y-vJ+MB>SUZ!ik{#LClVPJ`E<|#Ab`!N%zOyVhDzX6e0)$J zIQs@L`Mtk4#AFaFH72gVe=?7OOppH1sOeQSUp=+CJ8cM?Ed0iclpiVKyFhS`R#i6Q z>CDI%zMm=pk*fBG5@^5!`yU zTDBF#FMDmh3OonBvvDW+nYjFcyZ)9!7gGC%TwO+7|8P zjUhbC6OcBGMej=PK|d7J9~&HdUn1t8y4?QW@4rKpeU0Y(c5fU(k><|%kY&(j~$IYe)v_{EJz(b&D+^bh5yw)jsYEXQR-4=?QJ-EA=- zI>OUuffFgRA%*0Nv7RIUmx`kX&qAmPPYvJk!KrqY*`px5joaJnZA1J_m0dlPE(3lreD1ab)W=7@ z)oD`$fn^s#kVluFC0C|6((~ZNJCr=Y&MS=7@r30n}VZxANE`lqOXJ%oLeDlOG&op5`)O^V4zWX%2L3Hm4Rq+Ma)b zS}ZM;<{nlkyXXCTgX`m6$$CHG{HG0%7@<8!NME5&uDC$f)pZXv`&_2yrZB2BJgY67 zpPKu!3~u?$ahESK0nwZ^vGmW=X#=w_?xE{x;nN-iF8&vA_RbYE{T&Fe7^{w8kI(7d z&9jz}4<~_E_a#2PAKAw&n#f{6ywSb9(Y+ZNb+~v~r^I}YzkX%wTc-M}q|q0;PhIRJ z4LfQ^R({W>pzv{dsp0np-_sqaR67?>&3%GL*Z;6FfPfs9fPhf_SHzqFi3N%RZ2NDz z;!CCPvAFP!R#+rcovJ*^+Oh=6qVO=N52k698b_htaiHGu zHWf4){W5BHVaiI=!A?xQh>t2FXK1GT`}-m{&j*3>L3={!AKa*kw2D=O_s}} zxp@M<`VYh{By+Z4@({3WlPu{0m&ai{Yd3I(xCynU#C46-9g58_R}Vi5OI}UOPFj?m z6I}~>7cVzXUV*C^VkM-V+UEvvs0ewv;`|NqYw1A5N5&9<`{6gc6x^Ihz!;RS|J232C)s5z&KD_F>yV1$F zyj6YRXE|kCwJ|+2KF_%r9|qWIk+uw;TjG>%LS&c1rY!ns`Rm%}G}O4S8K9sXu`2hGp?|-$Vt9C^qJ>GQ}SGpe= zpGABSIiBZo;^L}k;-FfC7e8kue;l8kjk_3680A`J3;FmpSq5+y(6gq=WS>;UZl6G$ z_e8ptmwRW|g|o!RS(G z2T|CRX??_f^!FKj#P7Z)e*;npMH z<^4?%XlsoCBk=%InS|c=FNb-fj3hm%gJrTLc;nZzkla6}q<5#ivbM-MJBvF{XJt!w?BiVuXhjVm#%Pg+ zEm;6nfmt8wG{)0ss+KCXv8bmAq_a!-O^K7$sqWFI`VykYH25-2u`zOaFk3`7=T)7B zH^r?i;rkxG*f6j#sNP8!F4;_@Y;q;0(LZB`B#N0{!KCfWtzN42vxI6iy6DUZNvCPh zKGcfPED&6Bu3JA|YsTtUWG*l4yoWxQu5Exr$(H4%&TT7A!`o6kvZzyis*Fb2s0k5@ z!v}{=+XHH|T%ZGbizf&&e@a~5j55WGGM$Yb4wv|WBRn2unL!Tw8LaDSLF5qC-M5%U znh=jt<6OKH1bv-54uf=^heZqBpo2Y-{J#B|m?sNQeAE<{54q9t5ABUk&@o(02_5hY zJ`V&=Ed*3_hQj_s{D@1qW{D#UOQ$?pf2(voZ3-c{XjUa~!t~p~Yz((7Z&zYetd@}05s&sDG-Dy|G&fdHBax(rCUXHj8WzJFMLp9WwkXrI zs4k9?Pj+`$E4M#~qAa<>TV}?Gw>9n`yOoONy&4_*eLXYF-*Ugm)RxFX)cOG*63z!l zA*U0UT^SweZ{KbHObwiZ%7OHqwz3RiELE{tYL4{crL; zIrOI|D$fsnomI7THGy21N@$?n3-N#5Tw?GeL1g0vVZGFPefdH6&R5hA$A~iKEuHiP z{obgnlmHH*8U2nSUr#3A4#OMM2JTszctyjKBa*t&RY-wP~3?ZuV65{ zj_B~a1)%hr8Lr)E2DrNuX}G_?e`9ei@-d}Dya|K`<1azAF_v)cD!o&Sw#fzKdqoK3 zozzI5;5O88m&W1$CeNf*0NNmZ&oGNM%-asgMsq*vrk~LXaX(-h8sz|j%IrELJo(*A zlF1+k3{*||(K<;f17{IiMeOZ^8d<$m%AO69jfdYq?rx9rgheHr{+`~~ ztE7E;%C&8^RW*cgn#ln>+r0264vzNry|n@n6?~mA`SID z(Rxk;?w{Mwrt(&>jSq2p2`vR^3(@gL7DCq^NlYL*gXxhDhn@UzNLja`BPq1A%JtU> zXf*N3ac~T@#xd9Fn`a35wK@(Y;l=E7OLLI0U)U=WT`;wf?n}T>qw8gtKoemqeq&dY zNr!N&u|z|A{BDsjnYNx8DFRl|5ih9~`!`5LkXi`MKTKNA#J}JmR|ld+apNxvXpY4AIIRlzd8NX1MkPUw$}K2{BhUv1{>aJR zFr;UcD-f*}Q=$OY(r*Fo|9TLhHLX3=Y55l;h5*EA_f3u)&sbG{Nph)mh392+Z%5Ob zGA%syBJ%ip17cAJdA%AT=~>}q`r??Z&j=N6O`wO9+$&?5$O5|X$;LzrrvCsGpV~|y z5}}3IlN{%v`^5n%*OqZelLI%jiogAFIy*9a;p6#9?><0k*;55!wziE)h|$S=$tOp? z1%Gzl8|(2gl}2_QY4UdyB)q^e>eD(Hu|sBDa!R7C>5~Ol4OnuhqVOkd!G;~y*VVCx|5HyXODiwz-GN6uA#U(Wkg#MoCeb23Ia}ZBc3&udw-(*|FTpE0zh4LbKHmb_0*p5i|@CPVQ46U}9gjO$K zfz>G@+VAw{oVLDMLrLW^l_jbna|hpKT5rmoQXRl8Imz49Ow2+jJMQqlnVwhil;=6U z1A6x|G9D+I@tc=R)$LUp>n&QXjKBf20z&x8?A{jGo+w*GS~!m{_IXAp6}kj36^^DH zxn)DzUub%_OYY9j#TR}?S3yTTB{QH6U*xs+SG4=WyF=Nw@3^u7AKUj>{M4@vo4K0c zib{aOjw#zk0H1kNOQXdhBjG#C#2%5H)gB{aPi5u`LU-#ZOsJyhFfsB?>2UoZ7ym5$ zvRiEuc3clHDt4zqZ+Ksfps4JCMP3NXK0Wegl^P~IY_s;hJ_cYe zW#VVmA8qoTL3fo5vxc9eWe5qnQZn6Upm{WzlLj)TDFx7>g4BZ5@ulS)#On@?6=By_ ziI7113$c|m`*{CNZ?#)V!u2apw7(MlaD%hBUT(3wfnD1pNO3dCW?QozCQD&##@%|w z{=JpPf;e5Q(d;iAwdicyX-s^_8xD}5tP`ij?Zj<$D!>073&|)NR@s7Ip@iu-yQWJZ z%LJboJ?~Q1q=+kNKO;G_&hA9N%5T458IJOv3D8tjk+sQEOXg`vQ6g#^w)R;j*;Oc8 zUejVr8_C(5QxeV9schrL@ife+hoODz)*CO)S#)f#UD+}D8i{kBbeWMIylEiceGXyW zYK$fecOizJJ3yrbw^4c+%kp%VTVaewmRqiac}gQJ%_5!Yh|<_DpBFu_C8v2Tmgd$a z8`Lwwqn598-qidh5I5S_d9yhEg`nK~gy9#+i|H|?KM1jIm^g#a!8CJ@VdHKCwrac@ zvX>}f)7wz?_*?OCOEv#d)eE4s3TxqBhYYF3t5{PSyn`p)Fp(6JS!n}@REgGsAtnNE zeX5HWB1S`{<V=BRXaU*YzCj4#i{ht4{*r z28eDd&G$pHgofp*RzI@1@uQc!z%Wk6#FI0Ms~6&KC9%O4HKWLQKpJR?H~!jX5uHCe zaJ%ZM7)P(6x#Jk|tx3`tUGb*2`Rz%6mKN0k_NcemdbJCE_WJZU9HbI?sS_lsZ==jg z|77XPjF3m-`-k)yRj`qr{OnkS+pUxB2FnlkK8~8oQ16Z$%<~#AK2ZjQ1rBGo-R(qz z?x7`aF3$Fe@b&xe$T7gf*ccY8-uM2iE|(76=2LhgMV7E?$~+UZ>7SjJ3FS?%g*I=+ zZVWdn<7Q}=CR%P=;ZmH}Y8gQVW||Fr{`-Hlb+OJx%#8&4H#dxN^_*olO!m#1%BGMu z7!XG)uUa|YIjPGsZEg?!?;k51CjxP|HYPLy5{-(EV{K~Q9!&rT4;Ff7-P1Ruq2Plk z?_$`psuz;gKgz7;kHGHQ+SJch^Q%KUTiGk34mdetb~AsdwVG4?suErI((;Ptc08Th70a-+8171Zuw~+ zP|&*Tdd|gNh*fx!lEedPNm?a*gIs1GtSM=UQ9#@>e&WA%EupdC38feMT45f|`$)!gz%}7hWS#MgO+0KnxGgAY<*-y@ivd*1;M-uvOdlA zz?tM1eJTqMQv{V?oOp{Zw^-8_*@Lo&TL_^vqh&%C&Q)mKJnGAv`y?c{s4>GOcV;fE z1OSq^CU&v!e%+pg(VEcBNqE0SQiTg$NlgCpYTv3*{G50PxC5U*D*=)(!mg$F_BNQjo(-t01?8K zy|{nhi=!Ja+MqTm8O->2L5h(L4NjKSo2|75S*BVvk(j@JiNBT-Vjp*~kFLM~} z8|3IuRD`z1(?CHKe7=wuCNaAWT}!z82|wPe=()$;oviWg3_gw&CH{n+p2_y`5+Kit z(D~rnH+!D%?%4qQbM@52=(0DYV6LJA@Ll!Z_1*Gay$O66{Z)MVR{y=QLsOd(4LxMd zBp`ac71yAt&Fc;G`eQ*IZG@zDiK#EakDiN>vgDDI+~zL*EL6oEZyJmoXSJ45fuagd@nxpcmf*qUT}EEl_zPfc1Ixrgq4j z3(`LMT(;d3{h?LYG^{QGRv}*hJC9Au%K#3x#eaCEL}X|#iXCVnKEz;%Cd3?kq;ZMF zXsZpIa~IJFxui9rz($Z-_4G%M^F2Ac(qaz&%qaXxhdo2@yn`BfX*Ag*U4b!Qt_D3K-Y4GC`RsR>e|{ zpwAx-k1Fw8!!uems#TaAXZUiF0!0M1&d$Yq;&as9#|MohVWR#(ea1k zdB-{KE$LtW4W@6emM$80%h*a^*XLO==VEdTKF0P6lquMd?+JqPE6KAz^1r!D^@^5` zRs!l|_*6pY5LDQ@^=gq3V;Z2#R@u`(@zu&z$WNEL|3YMS1Bk7V@W115l{6_{IyG{u z{CGXBhZ+>ReCNBq3*{zyZ6cOBly_~kyE#r^&lElEsayRClA!__c?fA&I{@RKgN>Bo zR`lcl!(9}OnYN=6k@ilpD=2LS4G*E%Zi*OTW2ZL!T5op&sp-QXRNO(}5JHCt4y|al z)O4VcMe0cE2Jq-u7OhV)tGiXPs6jDwjbMvxMSO!DLbS@sAmBHwPZ7Zly8DZS2?g!7&MoOWmnblaT;+I*F6^zckXa0# z&lkL4xmE+N8tY3K=bIXWKM_Juev3 zLz`?c9iqRB1$vPI7$n#m>H$|SK>(+Zz=jYtdYJskJ}Uh%jLxj3PqhY{0pchstCr$4 zc^?9fSZtYA@GA-l6@HSesN?=8va*sS(t1p7a(P@{xrUCypPbzTc z6{*X!5H3(E$9}9MK{=&yo+0>dw`0X`osF&P2*Kh_u%NU~L?+HP88~-oMWG%$56_ZL zMLv=f((_Q#MR;Z$FZn>wSudF*XSH7z7g;|)d{c#|Y-?50rL!$Y@Pa`!SSV5)v!2+% z1bmwQY4G0_{TrRuK%eM%m?PJFibbVpexl^~k7)mW?6}(W+#>F*^Ectb5VH7wB&EBm zXIFbaD;6PD{I(eX+Wu5&{{8RvWdS9;DN}AkPnM;TuMz)+nGq7Ztj(&V#XTXVl1Wk$EmG(~86%U;RCEAuIa&5GV_86f0<->OX)Z1fKkrAe8TLGioit1-=I8u%b= z2&b?W<{U^tD50ulUaa{9dq4hu9phWp4r`tAR!{4x*^*d)pc(nOWq`gS6Vh2Z?`c|2S^u?Mcq-B>795&#MG4NH z&Px%+cN2Iy+B5nLtjjK@dq*rr3=s?Osz)&^s!}7zH%W-Hy5jTQ+G;XlI#Y^SC^7Fh z1^5;Bh^GUPzraJEIXRe^fOLPZbt5)|IsLMAQKgxF73_MpzfNp&P|HG`&D((3y3~F0 zL-Rbwm2=+}UW!ErA+H>YJfaHBy1?n+|_T41BorYa^( zmH|T)(r6;p{`cHIoDGBV?+kl&uq8or4Oc}S^fx;;=5IIs%r!o(fDjiBlomIL3Ly3*HGuk>`(X?&AlnU#wD$AU+{~{AF|KJ(OKnsWDp7V0qr16#g z*L9+5CYAAHZZ*OiAoW<9BA0V`IxZnknpn%QKI|FtM8m5lGvojh24-X? zJ^{I?po|4CPr@S9-%OXzXXwqurs~ zLq$&q5)}$z&7#CUvzWW-%3xE@Olq#%Sg65ENtlQRF1?Xm?t-u+m%@_PPVhn`S+VB0 z*rQiZMn$ftJE}{XI97;$tuD`lVmzo;!h+qDC^yyO*@nOfW%m7+VxVd&KNR8731pP1 z@kDyG@wsLK2oEBmo?~$vM1teWwpFV^=E14)`n1T`Z5vRE>w(ygi2q_lNL?!nM7450 zweSDPiEz4tx+2BsWWT%zS6sQhc$Qhm%YmqK(4jOZ%iHff%=?*#Btv+QS>yapD5p%Q zO$RaC=ei@`kU5e20|>e#nSj1Co2cvVvS+!7nf& z!n4?@v2S;1+%h;i7*Io^?{M-r(Q>DprF(jkGs;NF)sz;ah<^@()5@|90Y>yVciu*A z2=5xQd9Yy>H8Vn7SE)qWvDvQgUdDtA0ly@~0ZkZYcQ%HuKktF5-=Yu;GHh@Q_UCl| ziqYmxh!}}DT8Nbc>#6zyCwKOe`n~Q!X9b1Wkp~Jnaw{B`!tLTyBDF|G=bR?zoaD9z zEJ5o!khq!lFc5#t5l0Q1(PWwe2a4UO_Sz=dDY4u4v^r{8g2N)U2R6*xwTPaus>&G# zfW6amc|MCIOS)~bQFfd!8!F8AuX`CcFV)#p&`o7_Si@?+T6DLu)5^cz&iIM-?M?Q) z*X>*rhvy5b9r1s-N)z4?+u~yGh)CGSYn|x6oqfOOXvunZgO|Z|J(Y-HJoEeEbOQlx z6{CD0bDTUu`KwjqH5-#d?}s}GBz=hWf?KV*^I8V6)EYKMIr!f~yA~ zoukO^UL^k{4t$xLzB%3A5527u?v|%Y1-aPGi^_z-H{={Uf}@!TcqEt6Z4?-;dO96hlyfuw4T zP2rSWC7^tkY7{vECn1KEOD9%5WqW?l==`Yt*^3ug95OgAA8386JwbsNp3aU@Grb7B zSVYw^kEp%B?OWL^=Xd=e8A$HK0yv>J@hZ1?yG_#M@wdiPU-l}LOiKc^F?~+hw6dHu z2ccmT5e8xR1S#eMYR8*G|)KL^IR?N3@RYL}$PCE5i41bmuVG~ZU z11&4N>X~2KNf+x&gJ}mN05y%TxsT#MgspEc09ng|ljci0xs+P6tIeiz#WjNeNcU}s zQzM|(gn14d8AkZPc6OW-w!i9AnjW-dL;>@o5QL;&q{DDOMEtv!ek$dIq4f12c7fCj z%ygdOUmLu=p@Vm}?9qZZbf(X(2%lBWY`>6JDXyNRH7L+p`;=Uv}3nBtjOCW&d|LMt$aJUBi5H0snT6jr&OQX*$b*& zXO!tAPjv)e-^IWL_iSxiJBW}Y&7qgT(Np{kXtTYd(bW4$XSL^7FzeldVE4E&(+`^I z7LhaTt>--Qmy`Gw*SpJI-l)hVmDCQA47HaVY&C|w40Ln=qXm1MgWP{k%zf3;+*jY( zFxSI^{U3z;o`ZsP1-k~w)oQytc4HO&HN3cfUF9VB3e}`VRV&11NnzqDY-O@}L8FU} zs${WK=TrEN-0G7EZYS1}{gFIBlsetd3s?~fUoR&E^l9tC;x{MJARIHfCwGiP@787R6cJwqi}8q$MaYB)IE)o4Lvt)-p24sh z$yIbNX%x!fVYoL&bwdowhBf`r9D_qNw7{s>k3L#)s&_OG&zECJ6a!(ywBsr2bYdXB z9{yVdtQFA1-m*?G!AI&%{**DfCFDmGZ(?{Wu$Y=TTnWr2+-Q%8K zm)ggB=*?4?;8AKWVl}4r4>aZsuKF=c<)YL8R1uUIBUsH9k$d_$dbL^j(>uV%cY>#N z6hl4I-zuO=Qd7uRdbc3Oq&BF|{^mi;9&$&+eT8 z#DyBiH){i={EN^T)K&t%gGB=iC!ri*j;Q_*4`F5WGeqLYg`GZhcIT2~HeSjmy-O6&A@-P}nTHDrFB&2&pzo@G^Aa5B=0F(f z+b)L1Jzxl*Zhr_jyrG1N{54t_ox&dpo5*u)K!R1Kpz#!-MP1lv2a&6#+it*XKL7CX z@XEhFRZ0K5!AIoh;~JLUm)?H_z~10;g4dn}n`rZDg}6rHa|Zc?q$M7sdsXmk?v%Y^ z!J*b_Ge-{f4C2l&;UdzjvLjAK){vxAm1`?2^=^_t z%!N@fFOBBm8}-NiW>*|sP`!nXr3hVl+hJq-4DNt8G=X9=JX`|Mf)j3$q+RaC^bIctQKw16Fg}={wnD6{kRtCYj2@~s+0cEm$3tsP=w%e1E zlrsLk-__=7$&WQ(e?i_hfFPHsVBrc=h#vsQk0rh@N{y2F{c}bQP)#59-RzpSW1Nin z+`hrzf10+Wb!c>rK@HPNw!ovexodcLLP5i{jWT0GwR^=-51$lKc1Ll7&ETDM7~Q}m zLXjGGYWzczSwVvyqv~^Tk9L>VtAX;};iUXuzw*{2v~}?St#&v#=+E@xzrfh9f{{Wd zW4G9WpJ>@>;AwCS$nm-CzWEtgeStIIkKSYG$F*TTRKnu2+EPCBUQHX?)Arq3Xv@rd z{&vR47d#pK{&M(DWa=H`ZTNJcy~UwrtD!k{ofcEe3=N`M7U2d0M|fj^*F%YV<7;&wV~6ueX?mq6k6nU|88(Q@x#Zz_v`rPggGdv zgM)o%lMZFedsy1+yKTUzoyVXIOb>xQnIjujs1KW9Iqxq{{+XWNc*f@Zr#L(Q3`Tf| zn6%-t2Q4=l0NMzu2})=J+dy+(i6J${P69N4{ix4iNQ!Wcu9aL^?A_V>e1Mlh^vM%V zSuw`*s+Q~@co5&w|xZ!1$l({1@VY4{(aUMN?uhcEN|~K zCMqfd|K+)rSo2MlJ=7qr48L}rmD}OV5?=hcqshLt#>dI*?qsOM!VEig5JY@zd=e)#%kIwr%p_t36ZVZF`yqR&=r z(dFrz+pdI?PhAH)_c&9}%Kopg1(E9*shi)|Fb$_CKR)!XK2mmi+(Tr>c64L0UDq)Q z^M0?gUEVML+FLRG4Nt~BOZ|)#y_6!pvc+g{S6ZB$zkwJdPi z(3srK7_U|L6!ud{`*k{5+EaatETv&LjJ{1`On;Y9zFWPN{hvMM8Jfls?Lzjy)Y3}E z?e>g3vX&R%EnCeIUB`z>i?WhSLLSX3lR2H{Wi{h^=b_{^*ge>&EOD7o&AN(ww5%K` z5mo&=hJlh2r$-4lLguHQ;W!o^KmjlYYsqlf6*q!Iwkw}jYriaO@)#6RuTn^JGRM5j z*@?R=>(Mp`;}2F2wREK3=eb)Hr!*WZ(~zXdq%w_V9&BOueY-(wN`pOjJqu)K`_oH!|V zS#TMn7@#Nw{~Ou#|LXs}B$oZR)UX2nzoq|6jQ;;ipZ5Q6>3?X_Bv1}Wp#T2;A8H+& A2><{9 diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index f04e8cdf..51326da6 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -5,11 +5,8 @@ export type WorkerRequestRoute = | { kind: "r2-mutate" } | { kind: "canonical-auth" } | { kind: "operator-reset" } - | { kind: "editor-probe"; phase: EditorProbePhase } | { kind: "probe"; phase: string } -export type EditorProbePhase = "admin" | "auto-draft" | "auto-draft-no-persist" | "block-editor" - export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") if (phase === null) return { kind: "wordpress" } @@ -18,9 +15,5 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "r2-mutate") return { kind: "r2-mutate" } if (phase === "canonical-auth") return { kind: "canonical-auth" } if (phase === "operator-reset") return { kind: "operator-reset" } - if (phase === "editor-probe-admin") return { kind: "editor-probe", phase: "admin" } - if (phase === "editor-probe-auto-draft") return { kind: "editor-probe", phase: "auto-draft" } - if (phase === "editor-probe-auto-draft-no-persist") return { kind: "editor-probe", phase: "auto-draft-no-persist" } - if (phase === "editor-probe-block-editor") return { kind: "editor-probe", phase: "block-editor" } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index da1880ed..382f302d 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -7,7 +7,7 @@ import { dependenciesTotalSize, init } from "../../../node_modules/@php-wasm/web import phpWasmModule from "../../../node_modules/@php-wasm/web-8-5/asyncify/8_5_8/php_8_5.wasm" import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "./health-envelope.js" import { leaseRetryDelayMs } from "./lease-retry.js" -import { routeWorkerRequest, type EditorProbePhase } from "./request-routing.js" +import { routeWorkerRequest } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" @@ -25,7 +25,7 @@ const PHP_VERSION = "8.5.8" // Browser assets must come from the same immutable WordPress release as the server corpus. const WORDPRESS_ARCHIVE_URL = (wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest).source.url const SQLITE_INTEGRATION_ARCHIVE_URL = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip" -const MARKDOWN_DATABASE_INTEGRATION_REVISION = "6244f244f47f99af3261ba0948262cebe79e5a73" +const MARKDOWN_DATABASE_INTEGRATION_REVISION = "7cf025f2d64aa933d937f1a18a129e278c231783" const SITE_URL = "https://wp-codebox-runtime.invalid" const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite" const MARKDOWN_ROOT = "/wordpress/wp-content/markdown" @@ -86,7 +86,6 @@ export default { const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) - if (route.kind === "editor-probe") return runCoordinatedEditorProbe(request, env, coordinator, route.phase) if (route.kind === "probe") { if (route.phase === "canonical-session") return canonicalSessionProbe(env.WORDPRESS_STATE_BUCKET, await coordinatorCall<{ pointer: MarkdownPointer | null }>(coordinator, request.url, "state")) return runBootProbe(route.phase, env.WORDPRESS_STATE_BUCKET) @@ -100,60 +99,6 @@ export default { }, } -async function runCoordinatedEditorProbe(request: Request, env: Env, coordinator: DurableObjectStub, phase: EditorProbePhase): Promise { - const lease = await acquireLease(coordinator, request.url) - let finalized = false - try { - if (!lease.pointer) throw new Error("Canonical WordPress must be initialized before running an editor probe.") - const runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) - if (phase === "auto-draft-no-persist") patchAutoDraftPersistenceProbe(runtime.php) - patchEditorProbe(runtime.php, phase) - const response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) - await releaseLease(coordinator, request.url, lease) - finalized = true - await discardCachedRuntime() - return response - } catch (error) { - if (!finalized) await abortLease(coordinator, request.url, lease) - await discardCachedRuntime() - throw error - } -} - -function patchEditorProbe(php: PHP, phase: EditorProbePhase): void { - const path = "/wordpress/wp-admin/post-new.php" - const source = new TextDecoder().decode(php.readFileAsBuffer(path)) - const markers: Record = { - admin: "require_once __DIR__ . '/admin.php';", - "auto-draft": "$post_ID = $post->ID;", - "auto-draft-no-persist": "$post_ID = $post->ID;", - "block-editor": "require_once ABSPATH . 'wp-admin/admin-footer.php';", - } - const marker = markers[phase] - const index = source.indexOf(marker) - if (index === -1 || index !== source.lastIndexOf(marker)) throw new Error(`WordPress editor probe marker is not unique: ${phase}`) - const stop = `\necho wp_json_encode( array( 'schema' => 'wp-codebox/cloudflare-editor-probe/v1', 'phase' => '${phase}', 'memoryBytes' => memory_get_usage( true ), 'peakMemoryBytes' => memory_get_peak_usage( true ) ) );\nreturn;` - const insertion = phase === "block-editor" ? index : index + marker.length - php.writeFile(path, new TextEncoder().encode(`${source.slice(0, insertion)}${stop}${source.slice(insertion)}`)) -} - -function patchAutoDraftPersistenceProbe(php: PHP): void { - const path = "/wordpress/wp-content/plugins/markdown-database-integration/inc/class-wp-markdown-write-engine.php" - const source = new TextDecoder().decode(php.readFileAsBuffer(path)) - const needle = `if ( $id ) { - $this->mark_post_dirty( (int) $id ); - }` - const replacement = `if ( $id ) { - $rows = $this->driver->query( "SELECT post_status FROM \`{$this->prefix()}posts\` WHERE ID = " . (int) $id ); - if ( ! is_array( $rows ) || empty( $rows ) || 'auto-draft' !== ( $rows[0]->post_status ?? '' ) ) { - $this->mark_post_dirty( (int) $id ); - } - }` - const index = source.indexOf(needle) - if (index === -1 || index !== source.lastIndexOf(needle)) throw new Error("MDI auto-draft persistence probe marker is not unique.") - php.writeFile(path, new TextEncoder().encode(`${source.slice(0, index)}${replacement}${source.slice(index + needle.length)}`)) -} - interface MarkdownManifestFile { path: string objectKey: string diff --git a/scripts/build-cloudflare-canonical-mdi-seed.php b/scripts/build-cloudflare-canonical-mdi-seed.php index 30514783..a4b9f6e3 100644 --- a/scripts/build-cloudflare-canonical-mdi-seed.php +++ b/scripts/build-cloudflare-canonical-mdi-seed.php @@ -1,7 +1,7 @@ { - const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const probe = worker.slice(worker.indexOf("async function runCoordinatedEditorProbe"), worker.indexOf("async function resetCanonicalWordPress")) - - assert.match(probe, /await runtime\.requestHandler\.request/) - assert.match(probe, /await releaseLease/) - assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) - assert.match(probe, /\/wordpress\/wp-admin\/post-new\.php/) - assert.match(probe, /memory_get_peak_usage/) - assert.match(probe, /'auto-draft' !== \( \$rows\[0\]->post_status/) -}) - test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => { const config = JSON.parse((await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")).replace(/^\s*\/\/.*\n/, "")) as { rules?: Array<{ type?: string; globs?: string[] }> @@ -146,7 +130,7 @@ test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => assert.equal(markdownIndex.subarray(0, 16).toString(), "SQLite format 3\0") assert.equal(markdownRuntime.subarray(0, 4).toString("hex"), "504b0304") assert.equal(canonicalSeed.subarray(0, 4).toString("hex"), "504b0304") - assert.equal(canonicalManifest.markdownDatabaseIntegrationRevision, "6244f244f47f99af3261ba0948262cebe79e5a73") + assert.equal(canonicalManifest.markdownDatabaseIntegrationRevision, "7cf025f2d64aa933d937f1a18a129e278c231783") assert.equal(canonicalManifest.wordpressInstallSeedSha256, createHash("sha256").update(sqliteInput).digest("hex")) assert.equal(canonicalManifest.archiveSha256, createHash("sha256").update(canonicalSeed).digest("hex")) assert.ok(canonicalManifest.files.some((file) => file.path.endsWith(".md"))) @@ -158,15 +142,20 @@ test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => }) test("Cloudflare runtime pins and bundles the public constrained MDI runtime", async () => { - const revision = "6244f244f47f99af3261ba0948262cebe79e5a73" + const revision = "7cf025f2d64aa933d937f1a18a129e278c231783" const generator = await readFile(new URL("../scripts/build-cloudflare-mdi-runtime-bundle.mjs", import.meta.url), "utf8") const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const runtime = await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip", import.meta.url)) const names: string[] = [] - for await (const entry of decodeZip(new Blob([runtime]).stream())) names.push(entry.name) + let writeEngine = "" + for await (const entry of decodeZip(new Blob([runtime]).stream())) { + names.push(entry.name) + if (entry.name === "inc/class-wp-markdown-write-engine.php") writeEngine = await entry.text() + } assert.match(generator, new RegExp(`const revision = "${revision}"`)) assert.match(worker, new RegExp(`MARKDOWN_DATABASE_INTEGRATION_REVISION = "${revision}"`)) + assert.match(writeEngine, /! \$this->is_auto_draft\( \(int\) \$id \)/) assert.deepEqual(names.sort(), [ "db.php", "inc/class-wp-markdown-db.php", From d1a830fb7ec92251cc403b1ba2810557ef0be6da Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:13:59 -0400 Subject: [PATCH 35/87] Package constrained MDI runtime dependencies --- .../markdown-database-integration-runtime.zip | Bin 71211 -> 93041 bytes .../build-cloudflare-mdi-runtime-bundle.mjs | 20 ++++++++++++++++++ tests/cloudflare-runtime.test.ts | 10 +++++++-- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip index e30bef701e25d0abca3d31b7b894fd8bfd3330c1..f351e5ceb612ea392391fd125cac6a16888f6cb5 100644 GIT binary patch delta 21560 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 H^~3-Gf+u%6 delta 38 pcmex(jdk@JmWC~ifx_EUV;GMyiLkOU00Acu9%Eu)@E2wT@&M_62wngH diff --git a/scripts/build-cloudflare-mdi-runtime-bundle.mjs b/scripts/build-cloudflare-mdi-runtime-bundle.mjs index 3d5a9f14..a1e4546c 100644 --- a/scripts/build-cloudflare-mdi-runtime-bundle.mjs +++ b/scripts/build-cloudflare-mdi-runtime-bundle.mjs @@ -2,7 +2,9 @@ import { readFile, writeFile } from "node:fs/promises" import { decodeZip, encodeZip } from "@php-wasm/stream-compression" const revision = "7cf025f2d64aa933d937f1a18a129e278c231783" +const jsonMachineRevision = "8bf0b0ff6ff60ab480778eaa5ad7d505b442c2d4" const archiveUrl = `https://codeload.github.com/Automattic/markdown-database-integration/zip/${revision}` +const jsonMachineArchiveUrl = `https://codeload.github.com/halaxa/json-machine/zip/${jsonMachineRevision}` const sourceDirectory = process.env.MDI_RUNTIME_SOURCE const output = new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip", import.meta.url) @@ -34,6 +36,24 @@ if (sourceDirectory) { } if (runtimeFiles.length !== runtimePaths.size) throw new Error(`Expected ${runtimePaths.size} MDI runtime files, received ${runtimeFiles.length}.`) +const jsonMachineResponse = await fetch(jsonMachineArchiveUrl) +if (!jsonMachineResponse.ok || !jsonMachineResponse.body) throw new Error(`Unable to fetch JsonMachine: ${jsonMachineResponse.status}.`) +for await (const entry of decodeZip(jsonMachineResponse.body)) { + const separator = entry.name.indexOf("/") + const relative = separator === -1 ? "" : entry.name.slice(separator + 1) + if (!relative.startsWith("src/") || !relative.endsWith(".php")) continue + runtimeFiles.push(new File([await entry.arrayBuffer()], `vendor/halaxa/json-machine/${relative}`, { lastModified: 0 })) +} +runtimeFiles.push(new File([` left.name.localeCompare(right.name)) const archive = await new Response(encodeZip(runtimeFiles)).arrayBuffer() await writeFile(output, new Uint8Array(archive)) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 7b309936..0be9bf6b 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -143,6 +143,7 @@ test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => test("Cloudflare runtime pins and bundles the public constrained MDI runtime", async () => { const revision = "7cf025f2d64aa933d937f1a18a129e278c231783" + const jsonMachineRevision = "8bf0b0ff6ff60ab480778eaa5ad7d505b442c2d4" const generator = await readFile(new URL("../scripts/build-cloudflare-mdi-runtime-bundle.mjs", import.meta.url), "utf8") const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const runtime = await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip", import.meta.url)) @@ -154,9 +155,10 @@ test("Cloudflare runtime pins and bundles the public constrained MDI runtime", a } assert.match(generator, new RegExp(`const revision = "${revision}"`)) + assert.match(generator, new RegExp(`const jsonMachineRevision = "${jsonMachineRevision}"`)) assert.match(worker, new RegExp(`MARKDOWN_DATABASE_INTEGRATION_REVISION = "${revision}"`)) assert.match(writeEngine, /! \$this->is_auto_draft\( \(int\) \$id \)/) - assert.deepEqual(names.sort(), [ + for (const name of [ "db.php", "inc/class-wp-markdown-db.php", "inc/class-wp-markdown-driver.php", @@ -166,7 +168,11 @@ test("Cloudflare runtime pins and bundles the public constrained MDI runtime", a "inc/class-wp-markdown-search.php", "inc/class-wp-markdown-storage.php", "inc/class-wp-markdown-write-engine.php", - ]) + "vendor/autoload.php", + "vendor/halaxa/json-machine/src/Items.php", + "vendor/halaxa/json-machine/src/JsonDecoder/ExtJsonDecoder.php", + "vendor/halaxa/json-machine/src/functions.php", + ]) assert.ok(names.includes(name), `Runtime bundle includes ${name}`) }) test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusion groups", async () => { From 17127d29a64658179ce47efd874135477c6ac9b6 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:22:18 -0400 Subject: [PATCH 36/87] Instrument Cloudflare editor memory phases --- .../runtime-cloudflare/src/request-routing.ts | 9 +++++ packages/runtime-cloudflare/src/worker.ts | 39 ++++++++++++++++++- tests/cloudflare-runtime.test.ts | 9 +++++ 3 files changed, 56 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 51326da6..3eaa7dc9 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -5,8 +5,11 @@ export type WorkerRequestRoute = | { kind: "r2-mutate" } | { kind: "canonical-auth" } | { kind: "operator-reset" } + | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-hooks" | "block-editor" + export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") if (phase === null) return { kind: "wordpress" } @@ -15,5 +18,11 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "r2-mutate") return { kind: "r2-mutate" } if (phase === "canonical-auth") return { kind: "canonical-auth" } if (phase === "operator-reset") return { kind: "operator-reset" } + if (phase?.startsWith("editor-memory-")) { + const editorPhase = phase.slice("editor-memory-".length) + if (["admin", "before-insert", "after-insert", "after-hooks", "block-editor"].includes(editorPhase)) { + return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } + } + } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 382f302d..f740d5ed 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -7,7 +7,7 @@ import { dependenciesTotalSize, init } from "../../../node_modules/@php-wasm/web import phpWasmModule from "../../../node_modules/@php-wasm/web-8-5/asyncify/8_5_8/php_8_5.wasm" import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "./health-envelope.js" import { leaseRetryDelayMs } from "./lease-retry.js" -import { routeWorkerRequest } from "./request-routing.js" +import { routeWorkerRequest, type EditorMemoryProbePhase } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" @@ -86,6 +86,7 @@ export default { const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) + if (route.kind === "editor-memory-probe") return runEditorMemoryProbe(request, env, coordinator, route.phase) if (route.kind === "probe") { if (route.phase === "canonical-session") return canonicalSessionProbe(env.WORDPRESS_STATE_BUCKET, await coordinatorCall<{ pointer: MarkdownPointer | null }>(coordinator, request.url, "state")) return runBootProbe(route.phase, env.WORDPRESS_STATE_BUCKET) @@ -99,6 +100,42 @@ export default { }, } +async function runEditorMemoryProbe(request: Request, env: Env, coordinator: DurableObjectStub, phase: EditorMemoryProbePhase): Promise { + const lease = await acquireLease(coordinator, request.url) + let finalized = false + try { + if (!lease.pointer) throw new Error("Canonical WordPress must be initialized before running an editor memory probe.") + const runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) + patchEditorMemoryStop(runtime.php, phase) + const response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) + await releaseLease(coordinator, request.url, lease) + finalized = true + await discardCachedRuntime() + return response + } catch (error) { + if (!finalized) await abortLease(coordinator, request.url, lease) + await discardCachedRuntime() + throw error + } +} + +function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { + const stops: Record = { + admin: { path: "/wordpress/wp-admin/post-new.php", marker: "require_once __DIR__ . '/admin.php';", before: false }, + "before-insert": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( $create_in_db ) {", before: false }, + "after-insert": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( is_wp_error( $post_id ) ) {", before: true }, + "after-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "// Schedule auto-draft cleanup.", before: true }, + "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, + } + const stop = stops[phase] + const source = new TextDecoder().decode(php.readFileAsBuffer(stop.path)) + const index = source.indexOf(stop.marker) + if (index === -1 || index !== source.lastIndexOf(stop.marker)) throw new Error(`WordPress editor memory marker is not unique: ${phase}`) + const insertion = stop.before ? index : index + stop.marker.length + const output = `\necho wp_json_encode( array( 'schema' => 'wp-codebox/cloudflare-editor-memory/v1', 'phase' => '${phase}', 'memoryBytes' => memory_get_usage( true ), 'peakMemoryBytes' => memory_get_peak_usage( true ) ) ); exit;\n` + php.writeFile(stop.path, new TextEncoder().encode(`${source.slice(0, insertion)}${output}${source.slice(insertion)}`)) +} + interface MarkdownManifestFile { path: string objectKey: string diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 0be9bf6b..1d7891bc 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -43,6 +43,7 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-mutate")), { kind: "r2-mutate" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=canonical-auth")), { kind: "canonical-auth" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=operator-reset")), { kind: "operator-reset" }) + assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-memory-after-insert")), { kind: "editor-memory-probe", phase: "after-insert" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) @@ -114,6 +115,14 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui assert.equal(leaseRetryDelayMs(Number.NaN, 500), 500) }) +test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { + const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) + for (const phase of ["admin", "before-insert", "after-insert", "after-hooks", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) + assert.match(probe, /memory_get_peak_usage/) +}) + test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => { const config = JSON.parse((await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")).replace(/^\s*\/\/.*\n/, "")) as { rules?: Array<{ type?: string; globs?: string[] }> From bc245c31c165f5b63f0361592185837825ce4201 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:27:32 -0400 Subject: [PATCH 37/87] Tighten editor insertion memory marker --- packages/runtime-cloudflare/src/worker.ts | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index f740d5ed..14088a34 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -123,7 +123,15 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { const stops: Record = { admin: { path: "/wordpress/wp-admin/post-new.php", marker: "require_once __DIR__ . '/admin.php';", before: false }, "before-insert": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( $create_in_db ) {", before: false }, - "after-insert": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( is_wp_error( $post_id ) ) {", before: true }, + "after-insert": { path: "/wordpress/wp-admin/includes/post.php", marker: `$post_id = wp_insert_post( + array( + 'post_title' => post_type_supports( $post_type, 'title' ) ? __( 'Auto Draft' ) : '', + 'post_type' => $post_type, + 'post_status' => 'auto-draft', + ), + true, + false + );`, before: false }, "after-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "// Schedule auto-draft cleanup.", before: true }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, } From 0654274400e17d556654b3abf65dfd026e27a46e Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:32:34 -0400 Subject: [PATCH 38/87] Split editor post-insert memory phases --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 2 ++ tests/cloudflare-runtime.test.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 3eaa7dc9..51d71a6f 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-hooks" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-hooks", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 14088a34..77d1da8f 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -132,6 +132,8 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { true, false );`, before: false }, + "after-get-post": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( current_theme_supports( 'post-formats' )", before: true }, + "before-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "wp_after_insert_post( $post, false, null );", before: true }, "after-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "// Schedule auto-draft cleanup.", before: true }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 1d7891bc..70a1b294 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-hooks", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) }) From 47306bbd768edc62367c7039449dc638754a3b23 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:37:08 -0400 Subject: [PATCH 39/87] Instrument block editor REST preload memory --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 2 ++ tests/cloudflare-runtime.test.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 51d71a6f..c0f56b34 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-rest-preload" | "after-rest-preload" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-rest-preload", "after-rest-preload", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 77d1da8f..ba5d56ea 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -135,6 +135,8 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "after-get-post": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( current_theme_supports( 'post-formats' )", before: true }, "before-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "wp_after_insert_post( $post, false, null );", before: true }, "after-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "// Schedule auto-draft cleanup.", before: true }, + "before-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: true }, + "after-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: false }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, } const stop = stops[phase] diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 70a1b294..97a8f3a6 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-rest-preload", "after-rest-preload", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) }) From cedb6f33686a9c8419feaefbc1b9237389b6fe3f Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:42:04 -0400 Subject: [PATCH 40/87] Probe block editor global styles lookup --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 9 ++++++++- tests/cloudflare-runtime.test.ts | 3 ++- 3 files changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index c0f56b34..a8e75e6a 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-rest-preload" | "after-rest-preload" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-rest-preload", "after-rest-preload", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index ba5d56ea..c94e7ea6 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -135,12 +135,19 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "after-get-post": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( current_theme_supports( 'post-formats' )", before: true }, "before-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "wp_after_insert_post( $post, false, null );", before: true }, "after-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "// Schedule auto-draft cleanup.", before: true }, + "before-preload-paths": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$preload_paths = array(", before: true }, "before-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: true }, + "before-rest-preload-skip-global-styles": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: true }, "after-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: false }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, } const stop = stops[phase] - const source = new TextDecoder().decode(php.readFileAsBuffer(stop.path)) + let source = new TextDecoder().decode(php.readFileAsBuffer(stop.path)) + if (phase === "before-rest-preload-skip-global-styles") { + const lookup = "WP_Theme_JSON_Resolver::get_user_global_styles_post_id()" + if (source.split(lookup).length - 1 !== 2) throw new Error("WordPress global styles lookup count changed.") + source = source.replaceAll(lookup, "0") + } const index = source.indexOf(stop.marker) if (index === -1 || index !== source.lastIndexOf(stop.marker)) throw new Error(`WordPress editor memory marker is not unique: ${phase}`) const insertion = stop.before ? index : index + stop.marker.length diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 97a8f3a6..ba23f800 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,8 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-rest-preload", "after-rest-preload", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) }) From 4035a44e770739ac93659e55cf1a408b74de096b Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:53:43 -0400 Subject: [PATCH 41/87] Instrument block editor settings memory --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 3 +++ tests/cloudflare-runtime.test.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index a8e75e6a..beffab78 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "after-editor-settings" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "after-editor-settings", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index c94e7ea6..dff172fd 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -139,6 +139,9 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "before-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: true }, "before-rest-preload-skip-global-styles": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: true }, "after-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: false }, + "after-block-definitions": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "// Preload server-registered block bindings sources.", before: true }, + "before-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: true }, + "after-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: false }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, } const stop = stops[phase] diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index ba23f800..1ee52977 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) From 2b69b4941fc550b0949a58eacde7394c45913e5f Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 14:57:42 -0400 Subject: [PATCH 42/87] Instrument core editor settings phases --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 4 ++++ tests/cloudflare-runtime.test.ts | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index beffab78..ba2801c3 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "after-editor-settings" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-before-global" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "after-editor-settings", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-before-global", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index dff172fd..6e58436c 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -141,6 +141,10 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "after-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: false }, "after-block-definitions": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "// Preload server-registered block bindings sources.", before: true }, "before-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: true }, + "settings-before-styles": { path: "/wordpress/wp-includes/block-editor.php", marker: "$global_styles = array();", before: true }, + "settings-before-global": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__experimentalFeatures'] = wp_get_global_settings();", before: true }, + "settings-before-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableResolvedAssets']", before: true }, + "settings-after-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableIsBlockBasedTheme']", before: true }, "after-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: false }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 1ee52977..e32748d9 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-before-global", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) From 1c6a2e765a6c5a8060f742028795bd27203812fa Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 15:01:21 -0400 Subject: [PATCH 43/87] Split global stylesheet memory phases --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 2 ++ tests/cloudflare-runtime.test.ts | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index ba2801c3..9f1426d0 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-before-global" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-before-global", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 6e58436c..f7d97a4e 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -142,6 +142,8 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "after-block-definitions": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "// Preload server-registered block bindings sources.", before: true }, "before-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: true }, "settings-before-styles": { path: "/wordpress/wp-includes/block-editor.php", marker: "$global_styles = array();", before: true }, + "settings-after-presets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$block_classes = array(", before: true }, + "settings-after-block-classes": { path: "/wordpress/wp-includes/block-editor.php", marker: "// Get any additional css from the customizer", before: true }, "settings-before-global": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__experimentalFeatures'] = wp_get_global_settings();", before: true }, "settings-before-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableResolvedAssets']", before: true }, "settings-after-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableIsBlockBasedTheme']", before: true }, diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index e32748d9..d8171dfe 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-before-global", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) From 20a3869756d4070d9519252790222db44347efe4 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 15:44:25 -0400 Subject: [PATCH 44/87] Compare editor custom and theme styles --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 6 ++++++ tests/cloudflare-runtime.test.ts | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 9f1426d0..7ca4688b 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-global-skip-custom-css" | "settings-before-global-skip-theme-styles" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index f7d97a4e..7f9a1607 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -145,6 +145,8 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "settings-after-presets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$block_classes = array(", before: true }, "settings-after-block-classes": { path: "/wordpress/wp-includes/block-editor.php", marker: "// Get any additional css from the customizer", before: true }, "settings-before-global": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__experimentalFeatures'] = wp_get_global_settings();", before: true }, + "settings-before-global-skip-custom-css": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__experimentalFeatures'] = wp_get_global_settings();", before: true }, + "settings-before-global-skip-theme-styles": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__experimentalFeatures'] = wp_get_global_settings();", before: true }, "settings-before-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableResolvedAssets']", before: true }, "settings-after-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableIsBlockBasedTheme']", before: true }, "after-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: false }, @@ -157,6 +159,10 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { if (source.split(lookup).length - 1 !== 2) throw new Error("WordPress global styles lookup count changed.") source = source.replaceAll(lookup, "0") } + if (phase === "settings-before-global-skip-custom-css") { + source = source.replace("wp_get_custom_css()", "''").replace("wp_get_global_stylesheet( array( 'custom-css' ) )", "''") + } + if (phase === "settings-before-global-skip-theme-styles") source = source.replace("get_block_editor_theme_styles()", "array()") const index = source.indexOf(stop.marker) if (index === -1 || index !== source.lastIndexOf(stop.marker)) throw new Error(`WordPress editor memory marker is not unique: ${phase}`) const insertion = stop.before ? index : index + stop.marker.length diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index d8171dfe..076a3e27 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) From 5b11009002c952df3d0e02c8db0ebd74e580614a Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 15:53:22 -0400 Subject: [PATCH 45/87] Fast-path custom CSS theme JSON output --- packages/runtime-cloudflare/src/worker.ts | 19 ++++++++++++++++++- tests/cloudflare-runtime.test.ts | 3 +++ 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 7f9a1607..2da31e8c 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -1295,7 +1295,10 @@ async function bootWordPressRuntime( materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed) } - if (shouldPatchCanonicalRuntimePoliciesAtInit) patchCanonicalRuntimePoliciesAtInit(php) + if (shouldPatchCanonicalRuntimePoliciesAtInit) { + patchCanonicalRuntimePoliciesAtInit(php) + patchCanonicalThemeJsonCustomCss(php) + } } : undefined, beforeDatabaseSetup: databaseSeed ? (php: PHP) => { php.mkdir("/wordpress/wp-content/database") @@ -1344,6 +1347,20 @@ ${needle}` php.writeFile(settingsPath, new TextEncoder().encode(`${settings.slice(0, firstNeedle)}${replacement}${settings.slice(firstNeedle + needle.length)}`)) } +function patchCanonicalThemeJsonCustomCss(php: PHP): void { + const path = "/wordpress/wp-includes/class-wp-theme-json.php" + const needle = "\t\t$blocks_metadata = static::get_blocks_metadata();" + const source = new TextDecoder().decode(php.readFileAsBuffer(path)) + const index = source.indexOf(needle) + if (index === -1 || index !== source.lastIndexOf(needle)) throw new Error("WordPress theme JSON custom CSS patch needle was not uniquely found.") + const fastPath = `\t\tif ( array( 'custom-css' ) === $types ) { +\t\t\treturn (string) _wp_array_get( $this->theme_json, array( 'styles', 'css' ), '' ); +\t\t} + +` + php.writeFile(path, new TextEncoder().encode(`${source.slice(0, index)}${fastPath}${source.slice(index)}`)) +} + function collectRuntimeFiles(php: PHP, root: string, paths?: string[]): RuntimeFile[] { if (paths) { return paths.map((path) => { diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 076a3e27..d8532296 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -414,6 +414,9 @@ test("Cloudflare canonical runtime patches the unique init call with runtime per assert.match(patcher, /return \$old_value/) assert.equal((patcher.match(/do_action\( 'init' \);/g) ?? []).length, 1) assert.match(patcher, /php\.writeFile\(settingsPath/) + assert.match(patcher, /function patchCanonicalThemeJsonCustomCss/) + assert.match(patcher, /if \( array\( 'custom-css' \) === \$types \)/) + assert.match(patcher, /return \(string\) _wp_array_get\( \$this->theme_json, array\( 'styles', 'css' \), '' \)/) assert.doesNotMatch(patcher, /mu-plugins|add_action\(|WP_Site_Health|wp_schedule_site_health_cron|\$GLOBALS\['wp_filter'\]/) assert.doesNotMatch(worker, /materializeCanonicalCronAdapter|wp-codebox-canonical-cron-policy/) assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalRuntimePoliciesAtInit = false,/) From d5a266d1e9c44a4132a3ba80b6d7d3dd5482908e Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 15:56:29 -0400 Subject: [PATCH 46/87] Anchor custom CSS fast path uniquely --- packages/runtime-cloudflare/src/worker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 2da31e8c..d88f3b6e 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -1349,7 +1349,7 @@ ${needle}` function patchCanonicalThemeJsonCustomCss(php: PHP): void { const path = "/wordpress/wp-includes/class-wp-theme-json.php" - const needle = "\t\t$blocks_metadata = static::get_blocks_metadata();" + const needle = "\t\t$blocks_metadata = static::get_blocks_metadata();\n\t\t$style_nodes = static::get_style_nodes( $this->theme_json, $blocks_metadata, $options );\n\t\t$setting_nodes = static::get_setting_nodes( $this->theme_json, $blocks_metadata );" const source = new TextDecoder().decode(php.readFileAsBuffer(path)) const index = source.indexOf(needle) if (index === -1 || index !== source.lastIndexOf(needle)) throw new Error("WordPress theme JSON custom CSS patch needle was not uniquely found.") From 0ecd321fd650dee8fd33d6f08ec890d20ce1acf9 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 16:05:16 -0400 Subject: [PATCH 47/87] Seed WordPress custom CSS absence cache --- packages/runtime-cloudflare/src/worker.ts | 1 + tests/cloudflare-runtime.test.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index d88f3b6e..93f2357f 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -574,6 +574,7 @@ wp_set_password($password, $admin->ID); $password = null; update_option('siteurl', ${JSON.stringify(origin)}); update_option('home', ${JSON.stringify(origin)}); +if (false === get_theme_mod('custom_css_post_id', false)) set_theme_mod('custom_css_post_id', -1); $GLOBALS['wpdb']->flush_canonical_writes(); echo 'flushed';` } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index d8532296..fb960952 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -337,6 +337,7 @@ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed withou assert.match(probes, /phase === "canonical-wordpress" \|\| phase === "canonical-bootstrap-setup"/) assert.match(probes, /materializeWordPressServerFiles/) assert.match(probes, /canonicalBootstrapSetupCode\(passwordFile, "https:\/\/canonical-probe\.invalid"\)/) + assert.match(worker, /set_theme_mod\('custom_css_post_id', -1\)/) assert.match(probes, /canonicalChangedPathCounts\(canonicalSeed, collectRuntimeFiles/) assert.doesNotMatch(probes, /persistMarkdownRevision|commitLease|coordinatorCall/) assert.match(worker, /'widgetOptionCount' => \(int\) \$wpdb->get_var/) From 2e4948d1abedbbcd92f2bb0c0dc365e138679817 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 16:19:58 -0400 Subject: [PATCH 48/87] Log canonical bootstrap stages --- packages/runtime-cloudflare/src/worker.ts | 9 ++++++++- tests/cloudflare-runtime.test.ts | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 93f2357f..038a0586 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -375,14 +375,21 @@ async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: s async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { if (!env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") const origin = new URL(requestUrl).origin + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "boot-started" })) const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env), env.WORDPRESS_STATE_BUCKET, true) + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "boot-completed" })) try { const passwordFile = "/tmp/wordpress-admin-password" runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) const output = (await runtime.php.run({ code: canonicalBootstrapSetupCode(passwordFile, origin) })).text.trim() if (output !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") - const pointer = await persistMarkdownRevision(env.WORDPRESS_STATE_BUCKET, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT)) + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "setup-completed" })) + const files = collectRuntimeFiles(runtime.php, MARKDOWN_ROOT) + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "files-collected", files: files.length })) + const pointer = await persistMarkdownRevision(env.WORDPRESS_STATE_BUCKET, files) + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "revision-persisted", revision: pointer.revision })) await commitLease(coordinator, requestUrl, lease, pointer) + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "lease-committed", revision: pointer.revision })) return { ...runtime, pointer } } catch (error) { runtime.php.exit() diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index fb960952..3c21c1dc 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -424,6 +424,7 @@ test("Cloudflare canonical runtime patches the unique init call with runtime per assert.match(worker, /authConstants, bucket, true\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) + for (const stage of ["boot-started", "boot-completed", "setup-completed", "files-collected", "revision-persisted", "lease-committed"]) assert.match(worker, new RegExp(`stage: "${stage}"`)) assert.match(worker, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) assert.match(worker, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) assert.match(worker, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/) From b7cc8aea47edb6cdc2d590eb1bd1009e975d0214 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 16:33:02 -0400 Subject: [PATCH 49/87] Persist canonical bootstrap stage evidence --- packages/runtime-cloudflare/src/worker.ts | 16 ++++++++++------ tests/cloudflare-runtime.test.ts | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 038a0586..bc7c6288 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -375,21 +375,21 @@ async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: s async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { if (!env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") const origin = new URL(requestUrl).origin - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "boot-started" })) + await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "boot-started") const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env), env.WORDPRESS_STATE_BUCKET, true) - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "boot-completed" })) + await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "boot-completed") try { const passwordFile = "/tmp/wordpress-admin-password" runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) const output = (await runtime.php.run({ code: canonicalBootstrapSetupCode(passwordFile, origin) })).text.trim() if (output !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "setup-completed" })) + await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "setup-completed") const files = collectRuntimeFiles(runtime.php, MARKDOWN_ROOT) - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "files-collected", files: files.length })) + await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "files-collected", { files: files.length }) const pointer = await persistMarkdownRevision(env.WORDPRESS_STATE_BUCKET, files) - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "revision-persisted", revision: pointer.revision })) + await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "revision-persisted", { revision: pointer.revision }) await commitLease(coordinator, requestUrl, lease, pointer) - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "lease-committed", revision: pointer.revision })) + await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "lease-committed", { revision: pointer.revision }) return { ...runtime, pointer } } catch (error) { runtime.php.exit() @@ -397,6 +397,10 @@ async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStu } } +function recordBootstrapStage(bucket: R2Bucket, stage: string, details: Record = {}): Promise { + return bucket.put("diagnostics/bootstrap-stage.json", JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage, recordedAt: new Date().toISOString(), ...details })) +} + async function canonicalWordPressAuthConstants(env: Env): Promise> { return deriveWordPressAuthConstants(env.WORDPRESS_AUTH_SECRET ?? "", "default") } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 3c21c1dc..fbaefec8 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -424,7 +424,8 @@ test("Cloudflare canonical runtime patches the unique init call with runtime per assert.match(worker, /authConstants, bucket, true\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) - for (const stage of ["boot-started", "boot-completed", "setup-completed", "files-collected", "revision-persisted", "lease-committed"]) assert.match(worker, new RegExp(`stage: "${stage}"`)) + for (const stage of ["boot-started", "boot-completed", "setup-completed", "files-collected", "revision-persisted", "lease-committed"]) assert.match(worker, new RegExp(`recordBootstrapStage\\([^,]+, "${stage}"`)) + assert.match(worker, /diagnostics\/bootstrap-stage\.json/) assert.match(worker, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) assert.match(worker, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) assert.match(worker, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/) From a5a5cb4a9f08b6a002685824ea5d0be925af4acc Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 16:40:53 -0400 Subject: [PATCH 50/87] Seed custom CSS lookup cache canonically --- ...n-database-integration-canonical-seed.json | 7 ++++++- ...wn-database-integration-canonical-seed.zip | Bin 48499 -> 48818 bytes packages/runtime-cloudflare/src/worker.ts | 1 - .../build-cloudflare-canonical-mdi-seed.php | 3 +++ tests/cloudflare-runtime.test.ts | 5 ++++- 5 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json index a4d2b3fe..4d54fe7a 100644 --- a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json +++ b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json @@ -2,7 +2,7 @@ "schema": "wp-codebox/cloudflare-canonical-mdi-seed/v1", "markdownDatabaseIntegrationRevision": "7cf025f2d64aa933d937f1a18a129e278c231783", "wordpressInstallSeedSha256": "7212b89f1c01f98a23b89e9a2dd2b214306a85c10adb0355b9c7a6005b4fe822", - "archiveSha256": "d69b700c671ebee7f9eb01fa80d58c66072cf3966560d01cc188bd26b4818895", + "archiveSha256": "717b53395be2eb29ba7419f5a0b7eee4fe5a6527ec09246b24133aea82b3686c", "files": [ { "path": "_options/active_plugins.json", @@ -409,6 +409,11 @@ "sha256": "4bb5f2dcc89d2ca9281434c74b47e9049f65dadc53d94754487be6ea32430e1d", "size": 116 }, + { + "path": "_options/theme_mods_twentytwentyfive.json", + "sha256": "1d251370cb28c8d1aa06c7d78a0cb604e954caff117eb306321c7111d60035c6", + "size": 161 + }, { "path": "_options/thread_comments.json", "sha256": "10416951abb4fb35cafc0b90a178a67f669dafb8334bea3a23ee862eb9243e89", diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip index c744ae19dae12c95635e8314eb4b66642fee774b..f4ba64762a50b460de4e2ba88e9a9d5413b5ac5c 100644 GIT binary patch delta 820 zcmaJIK4rcyyAk%Sh+ z9wWM78I(b}Ih~uAi9{e21@)tT^t13^kRlZbMz52Ie$)%+J7EO(p z9M)X*Um2H3B-?U;iOJz~*&MxMfy-*KTTFIGmuPYgSbAM&ckWZRehe1NA`cZ7!xx*v z|Ho}TQHJHFXnCpfL{4X)=yKRiouX)RIz*Sr)^#GM+@{ROx-M^KykxmQ?yo{%_sG-! znP2)a1!fOV=S$%&Q9%EM!&7GFlrfWsvor=@JVL~Kw0l#KS+)noUV+Xj;VH|)@M8`Q zN-mal8a{!+{pIj@b@a9zo8Ed7E1;gzlB^Pq71@}a66jSWepaMoWsJivA5YdQWcjod zuR@Q{NbgkGtx_QOE{6?94pbvyx}K8N&{k)JLoQGAG^fV>={hpjU__mRs2PDGYf)0O z6V@@q)9YGytsHk45vPIrh=TcW_{NA28c6*OG^Ay(NqD14z$L#x1|6RH^|Y^nsWdos zvB;bi=*N!PFApYp{VrtypVeiRJ_{AIE6x zL3kKc81QkfjaChi85G#M$Dw_mr|WHaZV)grFObfN3vC%#W3bQy`9d9aTQJs@x?PTY zA(l)0ormebpwEg&3sv;liq!=*rP-jjW<$FukkN(_tDOC~9fZlncq;D3(xRGry4|5F z;(JgXQpZ0y!}ie529b=DOVRN|k|9Y) Lu0*1|wOz$8ZId!5 delta 603 zcmYjPT}V>_7`=WFm497IXx(K>WWrsw7Q%k6`?*u6+ui0|TRqiCHq@|Wti&kDic#T1 z(1Gej!=RTU^xplupWcG#p@Jgl!H1G4a7FZHNzK{AzNd2zhx2hhK4qPLt zuO_StYFwIQ5tccc5il(~N!N{6vP7S{p;G#2+KiCmq=ObbQBKk~3(D3ud{9`#Z7dp< zdb(qUH_=CjZP1CW)wM6g+O=vGt>)8zW*i&m;v7?xDP)9#}sTP==TWzj_$75cse?Yk%B}MqevDUDG zf=D;UU@cnc*jZdHip|+5!-OuJ86J#ID); $password = null; update_option('siteurl', ${JSON.stringify(origin)}); update_option('home', ${JSON.stringify(origin)}); -if (false === get_theme_mod('custom_css_post_id', false)) set_theme_mod('custom_css_post_id', -1); $GLOBALS['wpdb']->flush_canonical_writes(); echo 'flushed';` } diff --git a/scripts/build-cloudflare-canonical-mdi-seed.php b/scripts/build-cloudflare-canonical-mdi-seed.php index a4b9f6e3..4b660420 100644 --- a/scripts/build-cloudflare-canonical-mdi-seed.php +++ b/scripts/build-cloudflare-canonical-mdi-seed.php @@ -6,6 +6,7 @@ '_options/siteurl.json', '_options/home.json', '_tables/users.json', '_tables/usermeta.json', '_tables/terms.json', '_tables/term_taxonomy.json', '_tables/termmeta.json', '_tables/postmeta.json', '_tables/term_relationships.json', '_tables/comments.json', '_tables/commentmeta.json', '_tables/links.json', + '_options/theme_mods_twentytwentyfive.json', ); define( 'ABSPATH', __DIR__ . '/' ); @@ -72,6 +73,8 @@ function cloudflare_seed_files( string $root ): array { $pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); $runtime = WP_Markdown_Primary_Storage_Runtime::bootstrap_existing_cache( array( 'content_root' => $markdown_root, 'state_root' => $markdown_root ), new WP_SQLite_Connection( $pdo ), 'wordpress' ); $driver = $runtime->get_driver(); + $theme_mods = $pdo->prepare( "INSERT INTO wp_options (option_name, option_value, autoload) VALUES ('theme_mods_twentytwentyfive', ?, 'auto') ON CONFLICT(option_name) DO UPDATE SET option_value = excluded.option_value" ); + $theme_mods->execute( array( serialize( array( 'custom_css_post_id' => -1 ) ) ) ); foreach ( $pdo->query( 'SELECT ID FROM wp_posts ORDER BY ID' )->fetchAll( PDO::FETCH_COLUMN ) as $post_id ) { $driver->query( 'UPDATE wp_posts SET post_modified = post_modified WHERE ID = ' . (int) $post_id ); } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index fbaefec8..69a7f92b 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -330,6 +330,8 @@ echo json_encode(array('removed' => $removed_callbacks, 'calls' => $GLOBALS['wp_ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed without canonical persistence", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") + const generator = await readFile(new URL("../scripts/build-cloudflare-canonical-mdi-seed.php", import.meta.url), "utf8") + const canonicalManifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json", import.meta.url), "utf8")) as { files: Array<{ path: string }> } const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) assert.equal((probes.match(/initialMarkdownFiles/g) ?? []).length, 0) @@ -337,7 +339,8 @@ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed withou assert.match(probes, /phase === "canonical-wordpress" \|\| phase === "canonical-bootstrap-setup"/) assert.match(probes, /materializeWordPressServerFiles/) assert.match(probes, /canonicalBootstrapSetupCode\(passwordFile, "https:\/\/canonical-probe\.invalid"\)/) - assert.match(worker, /set_theme_mod\('custom_css_post_id', -1\)/) + assert.match(generator, /serialize\( array\( 'custom_css_post_id' => -1 \) \)/) + assert.ok(canonicalManifest.files.some((file) => file.path === "_options/theme_mods_twentytwentyfive.json")) assert.match(probes, /canonicalChangedPathCounts\(canonicalSeed, collectRuntimeFiles/) assert.doesNotMatch(probes, /persistMarkdownRevision|commitLease|coordinatorCall/) assert.match(worker, /'widgetOptionCount' => \(int\) \$wpdb->get_var/) From 1133786945a18007de442520d5179fa743274c70 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 16:45:08 -0400 Subject: [PATCH 51/87] Split canonical bootstrap setup phases --- packages/runtime-cloudflare/src/worker.ts | 37 +++++++++++++++++++++-- tests/cloudflare-runtime.test.ts | 9 ++++-- 2 files changed, 42 insertions(+), 4 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 3f47e42d..3de583d6 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -381,8 +381,14 @@ async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStu try { const passwordFile = "/tmp/wordpress-admin-password" runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) - const output = (await runtime.php.run({ code: canonicalBootstrapSetupCode(passwordFile, origin) })).text.trim() - if (output !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") + const passwordOutput = (await runtime.php.run({ code: canonicalBootstrapPasswordCode(passwordFile) })).text.trim() + if (passwordOutput !== "password-updated") throw new Error("Canonical bootstrap did not update the admin password.") + await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "password-updated") + const urlOutput = (await runtime.php.run({ code: canonicalBootstrapUrlCode(origin) })).text.trim() + if (urlOutput !== "urls-updated") throw new Error("Canonical bootstrap did not update the site URLs.") + await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "urls-updated") + const flushOutput = (await runtime.php.run({ code: canonicalBootstrapFlushCode() })).text.trim() + if (flushOutput !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "setup-completed") const files = collectRuntimeFiles(runtime.php, MARKDOWN_ROOT) await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "files-collected", { files: files.length }) @@ -397,6 +403,33 @@ async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStu } } +function canonicalBootstrapPasswordCode(passwordFile: string): string { + return `ID); +echo 'password-updated';` +} + +function canonicalBootstrapUrlCode(origin: string): string { + return `flush_canonical_writes(); +echo 'flushed';` +} + function recordBootstrapStage(bucket: R2Bucket, stage: string, details: Record = {}): Promise { return bucket.put("diagnostics/bootstrap-stage.json", JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage, recordedAt: new Date().toISOString(), ...details })) } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 69a7f92b..3562ef37 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -427,7 +427,10 @@ test("Cloudflare canonical runtime patches the unique init call with runtime per assert.match(worker, /authConstants, bucket, true\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) - for (const stage of ["boot-started", "boot-completed", "setup-completed", "files-collected", "revision-persisted", "lease-committed"]) assert.match(worker, new RegExp(`recordBootstrapStage\\([^,]+, "${stage}"`)) + for (const stage of ["boot-started", "boot-completed", "password-updated", "urls-updated", "setup-completed", "files-collected", "revision-persisted", "lease-committed"]) assert.match(worker, new RegExp(`recordBootstrapStage\\([^,]+, "${stage}"`)) + assert.match(worker, /canonicalBootstrapPasswordCode/) + assert.match(worker, /canonicalBootstrapUrlCode/) + assert.match(worker, /canonicalBootstrapFlushCode/) assert.match(worker, /diagnostics\/bootstrap-stage\.json/) assert.match(worker, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) assert.match(worker, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) @@ -451,7 +454,9 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /bootWordPressRuntime\("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision/) const noPointerBoot = worker.slice(worker.indexOf("async function bootstrapCanonicalRuntime"), worker.indexOf("async function persistRuntime")) assert.match(noPointerBoot, /packagedCanonicalMarkdownSeed\(\)/) - assert.match(noPointerBoot, /canonicalBootstrapSetupCode\(passwordFile, origin\)/) + assert.match(noPointerBoot, /canonicalBootstrapPasswordCode\(passwordFile\)/) + assert.match(noPointerBoot, /canonicalBootstrapUrlCode\(origin\)/) + assert.match(noPointerBoot, /canonicalBootstrapFlushCode\(\)/) assert.match(noPointerBoot, /await commitLease\(coordinator, requestUrl, lease, pointer\)/) assert.doesNotMatch(noPointerBoot, /wordpressInstallSeed|databaseSeed|bootstrap_existing_cache/) assert.doesNotMatch(coordinator, /@php-wasm|PHPRequestHandler|bootWordPressRuntime|new PHP\(/) From a88aba670dd57de56aed53b927010452c8d9a331 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 16:54:14 -0400 Subject: [PATCH 52/87] Instrument block editor output phases --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 3 +++ tests/cloudflare-runtime.test.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 7ca4688b..788c1a0e 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-global-skip-custom-css" | "settings-before-global-skip-theme-styles" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-global-skip-custom-css" | "settings-before-global-skip-theme-styles" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "after-init-script" | "before-admin-header" | "after-admin-header" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "after-init-script", "before-admin-header", "after-admin-header", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 3de583d6..83620add 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -150,6 +150,9 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "settings-before-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableResolvedAssets']", before: true }, "settings-after-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableIsBlockBasedTheme']", before: true }, "after-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: false }, + "after-init-script": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "if ( (int) get_option( 'page_for_posts' ) === $post->ID ) {", before: true }, + "before-admin-header": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "require_once ABSPATH . 'wp-admin/admin-header.php';", before: true }, + "after-admin-header": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "require_once ABSPATH . 'wp-admin/admin-header.php';", before: false }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, } const stop = stops[phase] diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 3562ef37..390cd533 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "after-init-script", "before-admin-header", "after-admin-header", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) From 34ca13a6f3abaa6cf630c39e03960625b464817e Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 16:58:10 -0400 Subject: [PATCH 53/87] Instrument editor footer scripts --- packages/runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 3 +++ tests/cloudflare-runtime.test.ts | 2 +- 3 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 788c1a0e..55f35133 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-global-skip-custom-css" | "settings-before-global-skip-theme-styles" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "after-init-script" | "before-admin-header" | "after-admin-header" | "block-editor" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-global-skip-custom-css" | "settings-before-global-skip-theme-styles" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "after-init-script" | "before-admin-header" | "after-admin-header" | "block-editor" | "footer-before-scripts" | "footer-after-admin-footer" | "footer-after-print-scripts" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "after-init-script", "before-admin-header", "after-admin-header", "block-editor"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "after-init-script", "before-admin-header", "after-admin-header", "block-editor", "footer-before-scripts", "footer-after-admin-footer", "footer-after-print-scripts"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 83620add..70d998ce 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -154,6 +154,9 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "before-admin-header": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "require_once ABSPATH . 'wp-admin/admin-header.php';", before: true }, "after-admin-header": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "require_once ABSPATH . 'wp-admin/admin-header.php';", before: false }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, + "footer-before-scripts": { path: "/wordpress/wp-admin/admin-footer.php", marker: "do_action( 'admin_footer', '' );", before: true }, + "footer-after-admin-footer": { path: "/wordpress/wp-admin/admin-footer.php", marker: "do_action( \"admin_print_footer_scripts-{$hook_suffix}\" );", before: true }, + "footer-after-print-scripts": { path: "/wordpress/wp-admin/admin-footer.php", marker: "do_action( \"admin_footer-{$hook_suffix}\" );", before: true }, } const stop = stops[phase] let source = new TextDecoder().decode(php.readFileAsBuffer(stop.path)) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 390cd533..dce0e455 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "after-init-script", "before-admin-header", "after-admin-header", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "after-init-script", "before-admin-header", "after-admin-header", "block-editor", "footer-before-scripts", "footer-after-admin-footer", "footer-after-print-scripts"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) From 7ffdeb726b63eed5677c20377c1cd4f35eab3619 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 17:04:38 -0400 Subject: [PATCH 54/87] Persist mutation stage evidence --- packages/runtime-cloudflare/src/worker.ts | 13 ++++++++++++- tests/cloudflare-runtime.test.ts | 2 ++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 70d998ce..04623693 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -262,13 +262,20 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi response = await canonicalAuthProbe(runtime, request) } else { if (isMutation(request, route)) runtime.php.writeFile(MARKDOWN_CHANGES_PATH, new TextEncoder().encode(JSON.stringify({ created: [], changed: [], deleted: [] }))) + if (isMutation(request, route)) await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "php-started") response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) - if (isMutation(request, route)) canonicalChanges = readCanonicalChanges(runtime.php) + if (isMutation(request, route)) { + await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "php-completed") + canonicalChanges = readCanonicalChanges(runtime.php) + await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "changes-collected", { changes: canonicalChanges }) + } } if (isMutation(request, route)) { if (!canonicalChanges) throw new Error("Canonical mutation completed without an MDI change set.") const next = await persistRuntime(env.WORDPRESS_STATE_BUCKET, runtime, canonicalChanges) + await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "revision-persisted", { revision: next.revision }) await commitLease(coordinator, request.url, lease, next) + await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "lease-committed", { revision: next.revision }) // The runtime now represents the promoted revision, including login session state. runtime.pointer = next cacheRuntime(next, runtime) @@ -283,6 +290,10 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi } } +function recordMutationStage(bucket: R2Bucket, request: Request, stage: string, details: Record = {}): Promise { + return bucket.put("diagnostics/mutation-stage.json", JSON.stringify({ schema: "wp-codebox/cloudflare-mutation-stage/v1", method: request.method, path: new URL(request.url).pathname, stage, recordedAt: new Date().toISOString(), ...details })) +} + function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate" | "canonical-auth"): boolean { return route === "r2-mutate" || !["GET", "HEAD", "OPTIONS"].includes(request.method) } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index dce0e455..e364e01f 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -632,6 +632,8 @@ test("serialized Cloudflare mutations use MDI flush paths and complete canonical assert.match(source, /packagedCanonicalMarkdownSeed/) assert.match(source, /update_option\('siteurl'/) assert.match(source, /WORDPRESS_ADMIN_PASSWORD is required/) + for (const stage of ["php-started", "php-completed", "changes-collected", "revision-persisted", "lease-committed"]) assert.match(source, new RegExp(`recordMutationStage\\([^,]+, request, "${stage}"`)) + assert.match(source, /diagnostics\/mutation-stage\.json/) }) test("canonical MDI seed generator is reproducible and validates its pinned inputs", async () => { From 29c502c99b1432a46b6f0e4dc36a381f020c7a1e Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 17:11:40 -0400 Subject: [PATCH 55/87] Recycle PHP at canonical request boundaries --- packages/runtime-cloudflare/src/worker.ts | 4 +--- tests/cloudflare-runtime.test.ts | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 04623693..f980bc9a 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -276,13 +276,11 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "revision-persisted", { revision: next.revision }) await commitLease(coordinator, request.url, lease, next) await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "lease-committed", { revision: next.revision }) - // The runtime now represents the promoted revision, including login session state. - runtime.pointer = next - cacheRuntime(next, runtime) } else { await releaseLease(coordinator, request.url, lease) } finalized = true + await discardRuntime(runtime) return response } catch (error) { if (!finalized) await abortLease(coordinator, request.url, lease) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index e364e01f..6066b19f 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -447,8 +447,8 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /let cachedRuntime/) assert.match(worker, /cachedRuntime\.baseRevision !== pointer\.revision/) assert.match(worker, /promise\.catch\(\(\) =>/) - assert.match(worker, /runtime\.pointer = next/) - assert.match(worker, /cacheRuntime\(next, runtime\)/) + assert.match(worker, /finalized = true\n await discardRuntime\(runtime\)/) + assert.doesNotMatch(worker, /runtime\.pointer = next/) assert.match(worker, /await abortLease\(coordinator, request\.url, lease\)/) assert.match(worker, /const LEASE_ACQUISITION_TIMEOUT_MS = 100_000/) assert.match(worker, /bootWordPressRuntime\("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision/) From aa1541178c8d33d5527a0abbe859c3eb342b4a37 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 17:28:58 -0400 Subject: [PATCH 56/87] Add MDI persistence isolation probes --- packages/runtime-cloudflare/src/worker.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index f980bc9a..00621244 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -730,12 +730,14 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (phase === "mdi-wordpress" || phase === "mdi-option" || phase === "mdi-insert") { + if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { + if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") + if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") const operation = phase === "mdi-option" ? "$updated = update_option('wp_codebox_mdi_probe', 1); $result = ['updated' => $updated];" - : phase === "mdi-insert" + : phase.startsWith("mdi-insert") ? "$post_id = wp_insert_post(['post_title' => 'MDI Probe', 'post_name' => 'mdi-probe', 'post_content' => 'MDI probe body.', 'post_status' => 'publish', 'post_type' => 'post'], true); if (is_wp_error($post_id)) { throw new Exception($post_id->get_error_message()); } $result = ['postId' => $post_id];" : "$result = [];" const evidence = (await runtime.php.run({ @@ -855,6 +857,14 @@ echo json_encode([ return new Response(`Unknown boot probe phase: ${phase}`, { status: 400 }) } +function patchMdiProbe(php: PHP, relativePath: string, needle: string, replacement: string): void { + const path = `/wordpress/wp-content/plugins/markdown-database-integration/${relativePath}` + const source = new TextDecoder().decode(php.readFileAsBuffer(path)) + const index = source.indexOf(needle) + if (index === -1 || index !== source.lastIndexOf(needle)) throw new Error(`MDI probe marker is not unique: ${relativePath}`) + php.writeFile(path, new TextEncoder().encode(`${source.slice(0, index)}${replacement}${source.slice(index + needle.length)}`)) +} + function wordpressProbeCode(phase: string): string { if (phase === "seeded-shortinit") { return " $wp_version, 'shortInit' => true]);" From a53ab9b4cb809ff336b6b338bf1459bb7f14bc45 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 17:42:40 -0400 Subject: [PATCH 57/87] Split MDI write lifecycle probes --- packages/runtime-cloudflare/src/worker.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 00621244..8bb00d61 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -730,11 +730,13 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild"].includes(phase)) { + if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild", "mdi-insert-no-shutdown-flush", "mdi-insert-no-writes"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") + if (phase === "mdi-insert-no-shutdown-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function ensure_shutdown_registered(): void {", "private function ensure_shutdown_registered(): void { return;") + if (phase === "mdi-insert-no-writes") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "public function persist_write( string $query, string $table, string $op_type ): void {", "public function persist_write( string $query, string $table, string $op_type ): void { return;") const operation = phase === "mdi-option" ? "$updated = update_option('wp_codebox_mdi_probe', 1); $result = ['updated' => $updated];" : phase.startsWith("mdi-insert") From b64a809c94bbea5b3d301f25ce89151e82ca4ae2 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 17:45:01 -0400 Subject: [PATCH 58/87] Report MDI dirty flush inventory --- packages/runtime-cloudflare/src/worker.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 8bb00d61..a55187c8 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -730,15 +730,17 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild", "mdi-insert-no-shutdown-flush", "mdi-insert-no-writes"].includes(phase)) { + if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild", "mdi-insert-no-shutdown-flush", "mdi-insert-no-writes", "mdi-insert-dirty-inventory"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") - if (phase === "mdi-insert-no-shutdown-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function ensure_shutdown_registered(): void {", "private function ensure_shutdown_registered(): void { return;") + if (phase === "mdi-insert-no-shutdown-flush" || phase === "mdi-insert-dirty-inventory") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function ensure_shutdown_registered(): void {", "private function ensure_shutdown_registered(): void { return;") if (phase === "mdi-insert-no-writes") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "public function persist_write( string $query, string $table, string $op_type ): void {", "public function persist_write( string $query, string $table, string $op_type ): void { return;") const operation = phase === "mdi-option" ? "$updated = update_option('wp_codebox_mdi_probe', 1); $result = ['updated' => $updated];" + : phase === "mdi-insert-dirty-inventory" + ? "$post_id = wp_insert_post(['post_title' => 'MDI Probe', 'post_name' => 'mdi-probe', 'post_content' => 'MDI probe body.', 'post_status' => 'publish', 'post_type' => 'post'], true); if (is_wp_error($post_id)) { throw new Exception($post_id->get_error_message()); } global $wpdb; $driver_ref = new ReflectionObject($wpdb->dbh); $engine_prop = $driver_ref->getProperty('write_engine'); $engine = $engine_prop->getValue($wpdb->dbh); $engine_ref = new ReflectionObject($engine); $dirty_prop = $engine_ref->getProperty('dirty'); $posts_prop = $engine_ref->getProperty('dirty_posts'); $options_prop = $engine_ref->getProperty('dirty_option_names'); $result = ['postId' => $post_id, 'dirty' => array_keys($dirty_prop->getValue($engine)), 'dirtyPosts' => array_keys($posts_prop->getValue($engine)), 'dirtyOptions' => array_keys($options_prop->getValue($engine))];" : phase.startsWith("mdi-insert") ? "$post_id = wp_insert_post(['post_title' => 'MDI Probe', 'post_name' => 'mdi-probe', 'post_content' => 'MDI probe body.', 'post_status' => 'publish', 'post_type' => 'post'], true); if (is_wp_error($post_id)) { throw new Exception($post_id->get_error_message()); } $result = ['postId' => $post_id];" : "$result = [];" From 34e1452a5a0dc7cc89a952b56a9128aec7adf779 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 17:47:00 -0400 Subject: [PATCH 59/87] Isolate MDI table flush probes --- packages/runtime-cloudflare/src/worker.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index a55187c8..ccdd10d0 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -730,9 +730,19 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild", "mdi-insert-no-shutdown-flush", "mdi-insert-no-writes", "mdi-insert-dirty-inventory"].includes(phase)) { + if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild", "mdi-insert-no-shutdown-flush", "mdi-insert-no-writes", "mdi-insert-dirty-inventory"].includes(phase) || phase.startsWith("mdi-insert-flush-")) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { + const flushTarget = { + "mdi-insert-flush-options": "options", + "mdi-insert-flush-taxonomy": "term_taxonomy", + "mdi-insert-flush-relationships": "term_relationships_non_markdown", + "mdi-insert-flush-postmeta": "postmeta_non_markdown", + }[phase] + if (flushTarget) { + patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") + patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "foreach ( array_keys( $this->dirty ) as $table_suffix ) {", `foreach ( array_keys( $this->dirty ) as $table_suffix ) { if ( $table_suffix !== '${flushTarget}' ) continue;`) + } if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") if (phase === "mdi-insert-no-shutdown-flush" || phase === "mdi-insert-dirty-inventory") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function ensure_shutdown_registered(): void {", "private function ensure_shutdown_registered(): void { return;") From cc5a6b2cf24b93259d729a524deba19e7881662b Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 17:51:59 -0400 Subject: [PATCH 60/87] Isolate MDI options index maintenance --- packages/runtime-cloudflare/src/worker.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index ccdd10d0..108de5bb 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -735,6 +735,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise try { const flushTarget = { "mdi-insert-flush-options": "options", + "mdi-insert-flush-options-no-index": "options", "mdi-insert-flush-taxonomy": "term_taxonomy", "mdi-insert-flush-relationships": "term_relationships_non_markdown", "mdi-insert-flush-postmeta": "postmeta_non_markdown", @@ -743,6 +744,10 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "foreach ( array_keys( $this->dirty ) as $table_suffix ) {", `foreach ( array_keys( $this->dirty ) as $table_suffix ) { if ( $table_suffix !== '${flushTarget}' ) continue;`) } + if (phase === "mdi-insert-flush-options-no-index") { + patchMdiProbe(runtime.php, "inc/class-wp-markdown-driver.php", "public function upsert_options_index( array $rows ): void {", "public function upsert_options_index( array $rows ): void { return;") + patchMdiProbe(runtime.php, "inc/class-wp-markdown-driver.php", "public function remove_from_options_index( array $option_names ): void {", "public function remove_from_options_index( array $option_names ): void { return;") + } if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") if (phase === "mdi-insert-no-shutdown-flush" || phase === "mdi-insert-dirty-inventory") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function ensure_shutdown_registered(): void {", "private function ensure_shutdown_registered(): void { return;") From 3771dc9ca2944fa60406a8d1325546a451f05f6f Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 17:58:16 -0400 Subject: [PATCH 61/87] Isolate MDI options fetch --- packages/runtime-cloudflare/src/worker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 108de5bb..85fafbf7 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -736,6 +736,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise const flushTarget = { "mdi-insert-flush-options": "options", "mdi-insert-flush-options-no-index": "options", + "mdi-insert-flush-options-fetch-only": "options", "mdi-insert-flush-taxonomy": "term_taxonomy", "mdi-insert-flush-relationships": "term_relationships_non_markdown", "mdi-insert-flush-postmeta": "postmeta_non_markdown", @@ -748,6 +749,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise patchMdiProbe(runtime.php, "inc/class-wp-markdown-driver.php", "public function upsert_options_index( array $rows ): void {", "public function upsert_options_index( array $rows ): void { return;") patchMdiProbe(runtime.php, "inc/class-wp-markdown-driver.php", "public function remove_from_options_index( array $option_names ): void {", "public function remove_from_options_index( array $option_names ): void { return;") } + if (phase === "mdi-insert-flush-options-fetch-only") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "$index_deletes = array();\n\n\t\tforeach ( $names as $name ) {", "$index_deletes = array();\n\n\t\tforeach ( array() as $name ) {") if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") if (phase === "mdi-insert-no-shutdown-flush" || phase === "mdi-insert-dirty-inventory") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function ensure_shutdown_registered(): void {", "private function ensure_shutdown_registered(): void { return;") From 2a621c0013abf011d5b0815a53cb82fdac04f594 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 18:02:17 -0400 Subject: [PATCH 62/87] Isolate MDI dirty option writes --- packages/runtime-cloudflare/src/worker.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 85fafbf7..16350fd8 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -733,7 +733,12 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild", "mdi-insert-no-shutdown-flush", "mdi-insert-no-writes", "mdi-insert-dirty-inventory"].includes(phase) || phase.startsWith("mdi-insert-flush-")) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { - const flushTarget = { + const optionTarget = { + "mdi-insert-flush-option-rewrite-rules": "rewrite_rules", + "mdi-insert-flush-option-calendar": "wp_calendar_block_has_published_posts", + "mdi-insert-flush-option-fresh-site": "fresh_site", + }[phase] + const flushTarget = optionTarget ? "options" : { "mdi-insert-flush-options": "options", "mdi-insert-flush-options-no-index": "options", "mdi-insert-flush-options-fetch-only": "options", @@ -750,6 +755,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise patchMdiProbe(runtime.php, "inc/class-wp-markdown-driver.php", "public function remove_from_options_index( array $option_names ): void {", "public function remove_from_options_index( array $option_names ): void { return;") } if (phase === "mdi-insert-flush-options-fetch-only") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "$index_deletes = array();\n\n\t\tforeach ( $names as $name ) {", "$index_deletes = array();\n\n\t\tforeach ( array() as $name ) {") + if (optionTarget) patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", ": array_keys( $this->dirty_option_names );\n\n\t\tif ( empty( $names ) ) {", `: array_keys( $this->dirty_option_names );\n\n\t\t$names = array('${optionTarget}');\n\n\t\tif ( empty( $names ) ) {`) if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") if (phase === "mdi-insert-no-shutdown-flush" || phase === "mdi-insert-dirty-inventory") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function ensure_shutdown_registered(): void {", "private function ensure_shutdown_registered(): void { return;") From 2ab517a864aaf5bf69f37d579259c2af75565f57 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 18:10:19 -0400 Subject: [PATCH 63/87] Isolate MDI option mutation tracking --- packages/runtime-cloudflare/src/worker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 16350fd8..c5db4dfb 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -742,6 +742,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise "mdi-insert-flush-options": "options", "mdi-insert-flush-options-no-index": "options", "mdi-insert-flush-options-fetch-only": "options", + "mdi-insert-flush-options-no-tracking": "options", "mdi-insert-flush-taxonomy": "term_taxonomy", "mdi-insert-flush-relationships": "term_relationships_non_markdown", "mdi-insert-flush-postmeta": "postmeta_non_markdown", @@ -755,6 +756,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise patchMdiProbe(runtime.php, "inc/class-wp-markdown-driver.php", "public function remove_from_options_index( array $option_names ): void {", "public function remove_from_options_index( array $option_names ): void { return;") } if (phase === "mdi-insert-flush-options-fetch-only") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "$index_deletes = array();\n\n\t\tforeach ( $names as $name ) {", "$index_deletes = array();\n\n\t\tforeach ( array() as $name ) {") + if (phase === "mdi-insert-flush-options-no-tracking") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "public function track_canonical_mutation( string $absolute_path ): void {", "public function track_canonical_mutation( string $absolute_path ): void { return;") if (optionTarget) patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", ": array_keys( $this->dirty_option_names );\n\n\t\tif ( empty( $names ) ) {", `: array_keys( $this->dirty_option_names );\n\n\t\t$names = array('${optionTarget}');\n\n\t\tif ( empty( $names ) ) {`) if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") From d42ba01fab2dc471534b8853bcd9f6a431321bcd Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 18:15:15 -0400 Subject: [PATCH 64/87] Probe shared MDI temp paths --- packages/runtime-cloudflare/src/worker.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index c5db4dfb..7ec2bf47 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -743,6 +743,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise "mdi-insert-flush-options-no-index": "options", "mdi-insert-flush-options-fetch-only": "options", "mdi-insert-flush-options-no-tracking": "options", + "mdi-insert-flush-options-shared-temp-path": "options", "mdi-insert-flush-taxonomy": "term_taxonomy", "mdi-insert-flush-relationships": "term_relationships_non_markdown", "mdi-insert-flush-postmeta": "postmeta_non_markdown", @@ -757,6 +758,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } if (phase === "mdi-insert-flush-options-fetch-only") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "$index_deletes = array();\n\n\t\tforeach ( $names as $name ) {", "$index_deletes = array();\n\n\t\tforeach ( array() as $name ) {") if (phase === "mdi-insert-flush-options-no-tracking") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "public function track_canonical_mutation( string $absolute_path ): void {", "public function track_canonical_mutation( string $absolute_path ): void { return;") + if (phase === "mdi-insert-flush-options-shared-temp-path") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "$tmp = $abs . '.tmp.' . getmypid() . '.' . substr( md5( uniqid( '', true ) ), 0, 8 );", "$tmp = $this->json_tmp_path( $abs );") if (optionTarget) patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", ": array_keys( $this->dirty_option_names );\n\n\t\tif ( empty( $names ) ) {", `: array_keys( $this->dirty_option_names );\n\n\t\t$names = array('${optionTarget}');\n\n\t\tif ( empty( $names ) ) {`) if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") From 6abd2b38937d2e3a6f655acd14be97fd2567f270 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 18:49:26 -0400 Subject: [PATCH 65/87] Pin bounded MDI option persistence --- ...n-database-integration-canonical-seed.json | 2 +- .../markdown-database-integration-runtime.zip | Bin 93041 -> 93034 bytes packages/runtime-cloudflare/src/worker.ts | 2 +- .../build-cloudflare-canonical-mdi-seed.php | 2 +- .../build-cloudflare-mdi-runtime-bundle.mjs | 2 +- tests/cloudflare-runtime.test.ts | 4 ++-- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json index 4d54fe7a..dee9df9d 100644 --- a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json +++ b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json @@ -1,6 +1,6 @@ { "schema": "wp-codebox/cloudflare-canonical-mdi-seed/v1", - "markdownDatabaseIntegrationRevision": "7cf025f2d64aa933d937f1a18a129e278c231783", + "markdownDatabaseIntegrationRevision": "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c", "wordpressInstallSeedSha256": "7212b89f1c01f98a23b89e9a2dd2b214306a85c10adb0355b9c7a6005b4fe822", "archiveSha256": "717b53395be2eb29ba7419f5a0b7eee4fe5a6527ec09246b24133aea82b3686c", "files": [ diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-runtime.zip index f351e5ceb612ea392391fd125cac6a16888f6cb5..2dc3966a82ee3805885cb341e1434f347258cee5 100644 GIT binary patch delta 12021 zcmY+qWlWvj^EJ%D-QA(MyA`+MR^092?tY59%PC&m-Q69EI}~@<;=KLkPX2k`{o&eK zvnFeHW|A$r=4J`zbP1-GWgpd6$CzrL1p-341_FWx0t_$$(1AAn*IkIdrEPOQf z0%RacFRhiQXB5X8;al36BROPS+QDkUk>${;1L8RPI#P(t^rpEqrbe`nSdSAg@;tw( z9o+$9tC!jMVFQ#E^-6z!^Lt-Dzgkud*?3;f78u;fbkZlOMap`ln#;YtgWkz*ba4b` zi*RLmbjyK;m7t;3zpTGznYkA8Cx_Hm zukjGMxRfqi5LiKyJg5Ki7{0lg-SsVgaGAIM$2&-=p3%8mg)&JehdNQ=;u!k8BO+N` z>R)9>*y6G^5;sQGBEwa4QW+RaoYnlK$UQwF@^IyPV>UcZbMPo98Zd$w%PSSq;!eE&o_;JNA6BTV0Bka#L)j!Jeu`Bcc3_YD3aUZq>l+r+>xBl0>Ajt&T`-rrnqx zQw@@0l3zU!vGVi@AzSn%R}q8zA!({n>V0d7Eq8Ph%RsD-UltX*ARZ-COy8`;VZcBD zB__|Fyv)FpS>|d7LzQf9ns!7jt~2J6H*_J6Bd0Gc*_o{d2KSnjD^#eBs!4hmPvl3* z^}VJPmbI!u7b2sFeq)lqF@vc@6~>R|ECL0E3cpx@$V9^O6bb|<)SDho||Clzt)!EFz66g(zl{=f~Ey&fZU_yR%I_(FB?I zcxe%lvh@7y{BXE+aq)RF^|^g{d)i;Te>mA+-xE>(Js6lEMX~(8nVtQok?UQ_SHkZX z;DS(j6KW-|^VH_Dhveyuqi#YpOV`{5LBXgCtV*9&AA{?z*0FVONYY(7g_4#iO>iXZ z!OaaqBMcc>Z%W{mn@kxWz!Ehsyo`6FAe{p zeKHTbLnGDJ9E=QmOQ^kmgE6C3o!Uywx+gD8;0zH88JXt_U2~_AD%V@FO31%lI_fCZ z6a_51N6+2PlQtber-dz9C{>|Xi!r2P43T6WHl-{GZ`xui)2_NT^DV!)?tY6YeM3{Sd%0DfX<`Z;k!+n@MFkaBtI+NQ?QYU=^jtr7 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
+ +

WordPress at the edge, with durable Markdown state

+ + + +

This bounded Cloudflare runtime runs WordPress and PHP as WebAssembly at the edge while keeping durable site state outside the disposable query database.

+ + + +

Architecture flow

+ + + +
+
+
+

Edge request

+ +

WordPress/PHP WebAssembly boots for the request.

+
+
+ + + +
+

+ +
+

Disposable SQLite

+ +

SQLite is reconstructed as query state, not the durable source of truth.

+
+
+ + + +
+

+ +
+

Canonical state

+ +

Markdown Database Integration exports canonical Markdown and JSON into immutable, content-addressed R2 objects and revision manifests.

+
+
+ + + +
+

+ +
+

Serialized writes

+ +

A Durable Object serializes writes and atomically advances the current revision. A cold runtime hydrates that manifest and reconstructs SQLite.

+
+
+
+ + + +

Verified capabilities

+ + + +
    +
  • Log in to WordPress.
  • + +
  • Edit content with the block editor and publish it.
  • + +
  • Render the published page publicly.
  • + +
  • Recover the site after a cold start from the current revision manifest.
  • +
+ + + +

Bounded by design

+ + + +
    +
  • One site namespace is currently configured.
  • + +
  • Requests for a site are serialized while a write lease is held.
  • + +
  • Each dynamic request currently pays PHP boot cost.
  • + +
  • Cold recovery hydrates the full revision manifest.
  • +
+ +
+ +HTML; try { mkdir( $root, 0755, true ); @@ -71,6 +168,13 @@ function cloudflare_seed_files( string $root ): array { } $pdo = new PDO( 'sqlite:' . $database ); $pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION ); + $front_page = $pdo->prepare( 'UPDATE wp_posts SET post_title = ?, post_name = ?, post_excerpt = ?, post_content = ?, post_status = ? WHERE ID = 2 AND post_type = ?' ); + $front_page->execute( array( 'Cloudflare WordPress Runtime', 'cloudflare-wordpress-runtime', 'A bounded WordPress WebAssembly runtime with canonical Markdown state in R2.', $front_page_content, 'publish', 'page' ) ); + if ( 1 !== $front_page->rowCount() ) { throw new RuntimeException( 'Unable to configure canonical front page ID 2.' ); } + $options = $pdo->prepare( "INSERT INTO wp_options (option_name, option_value, autoload) VALUES (?, ?, 'on') ON CONFLICT(option_name) DO UPDATE SET option_value = excluded.option_value" ); + foreach ( array( 'blogname' => 'Cloudflare WordPress Runtime', 'blogdescription' => 'WordPress WebAssembly with canonical Markdown state', 'show_on_front' => 'page', 'page_on_front' => '2' ) as $name => $value ) { + $options->execute( array( $name, $value ) ); + } $runtime = WP_Markdown_Primary_Storage_Runtime::bootstrap_existing_cache( array( 'content_root' => $markdown_root, 'state_root' => $markdown_root ), new WP_SQLite_Connection( $pdo ), 'wordpress' ); $driver = $runtime->get_driver(); $theme_mods = $pdo->prepare( "INSERT INTO wp_options (option_name, option_value, autoload) VALUES ('theme_mods_twentytwentyfive', ?, 'auto') ON CONFLICT(option_name) DO UPDATE SET option_value = excluded.option_value" ); diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 9ece2769..ce7b972e 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -405,3 +405,37 @@ test("canonical MDI seed generator is reproducible and validates its pinned inpu assert.match(source, /SELECT ID FROM wp_posts ORDER BY ID/) assert.match(source, /MDI_REVISION/) }) + +test("canonical MDI seed owns the Cloudflare front page and its architecture explanation", async () => { + const generator = await readFile(new URL("../scripts/build-cloudflare-canonical-mdi-seed.php", import.meta.url), "utf8") + const archive = await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip", import.meta.url)) + const files = new Map() + for await (const entry of decodeZip(new Blob([archive]).stream())) files.set(entry.name, await entry.text()) + const frontPage = files.get("page/cloudflare-wordpress-runtime.md") ?? "" + + assert.match(generator, /UPDATE wp_posts SET post_title = \?, post_name = \?, post_excerpt = \?, post_content = \?, post_status = \? WHERE ID = 2 AND post_type = \?/) + assert.match(generator, /'show_on_front' => 'page', 'page_on_front' => '2'/) + assert.match(generator, /'blogname' => 'Cloudflare WordPress Runtime'/) + assert.ok(files.has("_options/show_on_front.json")) + assert.ok(files.has("_options/page_on_front.json")) + assert.match(files.get("_options/show_on_front.json") ?? "", /"page"/) + assert.match(files.get("_options/page_on_front.json") ?? "", /"2"/) + assert.match(frontPage, /title: Cloudflare WordPress Runtime/) + for (const claim of [ + "WordPress/PHP WebAssembly", + "SQLite is reconstructed as query state", + "canonical Markdown and JSON", + "content-addressed R2 objects and revision manifests", + "Durable Object serializes writes and atomically advances the current revision", + "cold runtime hydrates that manifest and reconstructs SQLite", + "Log in to WordPress", + "block editor", + "Render the published page publicly", + "One site namespace", + "Requests for a site are serialized", + "PHP boot cost", + "full revision manifest", + ]) assert.match(frontPage, new RegExp(claim)) + assert.match(frontPage, //) + assert.match(frontPage, //) +}) From f1ec68bb658368697593380eaa45bf2120a5365e Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 11:09:42 -0400 Subject: [PATCH 77/87] Repair Cloudflare WordPress corpus dev rig --- package-lock.json | 452 +++++++++-- package.json | 4 +- .../@php-wasm+stream-compression+3.1.45.patch | 740 ++++++++++++++++++ patches/README.md | 10 + scripts/cloudflare-local-gate.mjs | 3 +- ...ate-cloudflare-wordpress-runtime-corpus.ts | 27 +- tests/cloudflare-runtime.test.ts | 4 +- ...loudflare-wordpress-archive-corpus.test.ts | 17 +- 8 files changed, 1182 insertions(+), 75 deletions(-) create mode 100644 patches/@php-wasm+stream-compression+3.1.45.patch create mode 100644 patches/README.md diff --git a/package-lock.json b/package-lock.json index 3de95ea1..c013a9db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,13 @@ { "name": "wp-codebox-workspace", - "version": "0.12.27", + "version": "0.12.28", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "wp-codebox-workspace", - "version": "0.12.27", + "version": "0.12.28", + "hasInstallScript": true, "license": "ISC", "workspaces": [ "packages/*" @@ -205,6 +206,7 @@ "devDependencies": { "@cloudflare/workers-types": "^5.20260718.1", "@types/node": "^24.0.0", + "patch-package": "^8.0.1", "tsx": "^4.20.0", "wrangler": "^4.112.0" } @@ -1942,15 +1944,6 @@ "npm": ">=10.2.3" } }, - "node_modules/@php-wasm/node-8-4/node_modules/@php-wasm/stream-compression": { - "version": "3.1.45", - "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.45.tgz", - "integrity": "sha512-zLU1AGNEg1tuwm93LHrL8OcV24LyNglTg/CXV5CVZiMPpg1wfIDHJy11qqjZM8iCIJk87qYQhkKOjTnGinAVDg==", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/util": "3.1.45" - } - }, "node_modules/@php-wasm/node-8-4/node_modules/@php-wasm/universal": { "version": "3.1.45", "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-3.1.45.tgz", @@ -2051,12 +2044,21 @@ } }, "node_modules/@php-wasm/stream-compression": { - "version": "3.1.35", - "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.35.tgz", - "integrity": "sha512-0YQSWF5Y1YIBLEVnY1dcrpGveVDUeTs1hx+uFwA4OrN9ZAMExqmghPCJuIkIRw9b8GNsHwojg/LD/5NXgc8fZQ==", + "version": "3.1.45", + "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.45.tgz", + "integrity": "sha512-zLU1AGNEg1tuwm93LHrL8OcV24LyNglTg/CXV5CVZiMPpg1wfIDHJy11qqjZM8iCIJk87qYQhkKOjTnGinAVDg==", "license": "GPL-2.0-or-later", "dependencies": { - "@php-wasm/util": "3.1.35" + "@php-wasm/util": "3.1.45" + } + }, + "node_modules/@php-wasm/stream-compression/node_modules/@php-wasm/util": { + "version": "3.1.45", + "resolved": "https://registry.npmjs.org/@php-wasm/util/-/util-3.1.45.tgz", + "integrity": "sha512-czXqXd2NJWkapV/zTr3WPSPdcgyI5KN7xSUusYpgkfljSovfvHOKKrTMQtbfPXRa9+MD8LmPQt+XStKPRp76Tg==", + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" } }, "node_modules/@php-wasm/universal": { @@ -2131,15 +2133,6 @@ "npm": ">=10.2.3" } }, - "node_modules/@php-wasm/web-8-5/node_modules/@php-wasm/stream-compression": { - "version": "3.1.45", - "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.45.tgz", - "integrity": "sha512-zLU1AGNEg1tuwm93LHrL8OcV24LyNglTg/CXV5CVZiMPpg1wfIDHJy11qqjZM8iCIJk87qYQhkKOjTnGinAVDg==", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/util": "3.1.45" - } - }, "node_modules/@php-wasm/web-8-5/node_modules/@php-wasm/universal": { "version": "3.1.45", "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-3.1.45.tgz", @@ -2532,6 +2525,13 @@ "npm": ">=8.11.0" } }, + "node_modules/@yarnpkg/lockfile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz", + "integrity": "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/@zip.js/zip.js": { "version": "2.7.57", "resolved": "https://registry.npmjs.org/@zip.js/zip.js/-/zip.js-2.7.57.tgz", @@ -2720,6 +2720,19 @@ "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "license": "MIT" }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/btoa-lite": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", @@ -2812,6 +2825,52 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/clean-git-ref": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", @@ -2910,6 +2969,21 @@ "node": ">=0.8" } }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "2.6.9", "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", @@ -3277,6 +3351,19 @@ "fxparser": "src/cli/cli.js" } }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/finalhandler": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", @@ -3295,6 +3382,16 @@ "node": ">= 0.8" } }, + "node_modules/find-yarn-workspace-root": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz", + "integrity": "sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "micromatch": "^4.0.2" + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3442,6 +3539,16 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/has-property-descriptors": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", @@ -3599,6 +3706,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-docker": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", + "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", @@ -3608,6 +3731,16 @@ "node": ">=8" } }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, "node_modules/is-typed-array": { "version": "1.1.15", "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", @@ -3623,12 +3756,32 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-wsl": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", + "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/isarray": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "license": "MIT" }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, "node_modules/isomorphic-git": { "version": "1.38.3", "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.38.3.tgz", @@ -3660,6 +3813,26 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/json-stable-stringify": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.3.0.tgz", + "integrity": "sha512-qtYiSSFlwot9XHtF9bD9c7rwKjr+RecWT//ZnPvSmEjpV5mmPOCN4j8UjY5hbjNkOwZ/jQv3J6R1/pL7RwgMsg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "isarray": "^2.0.5", + "jsonify": "^0.0.1", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsonc-parser": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", @@ -3678,6 +3851,16 @@ "graceful-fs": "^4.1.6" } }, + "node_modules/jsonify": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.1.tgz", + "integrity": "sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==", + "dev": true, + "license": "Public Domain", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", @@ -3721,6 +3904,16 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/klaw-sync": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz", + "integrity": "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.11" + } + }, "node_modules/kleur": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", @@ -3818,6 +4011,20 @@ "node": ">= 0.6" } }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", @@ -3944,6 +4151,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/octokit": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.2.2.tgz", @@ -3987,6 +4204,23 @@ "wrappy": "1" } }, + "node_modules/open": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz", + "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -4002,6 +4236,51 @@ "node": ">= 0.8" } }, + "node_modules/patch-package": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz", + "integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@yarnpkg/lockfile": "^1.1.0", + "chalk": "^4.1.2", + "ci-info": "^3.7.0", + "cross-spawn": "^7.0.3", + "find-yarn-workspace-root": "^2.0.0", + "fs-extra": "^10.0.0", + "json-stable-stringify": "^1.0.2", + "klaw-sync": "^6.0.0", + "minimist": "^1.2.6", + "open": "^7.4.2", + "semver": "^7.5.3", + "slash": "^2.0.0", + "tmp": "^0.2.4", + "yaml": "^2.2.2" + }, + "bin": { + "patch-package": "index.js" + }, + "engines": { + "node": ">=14", + "npm": ">5" + } + }, + "node_modules/patch-package/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/path-expression-matcher": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", @@ -4017,6 +4296,16 @@ "node": ">=14.0.0" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/path-to-regexp": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", @@ -4030,6 +4319,19 @@ "dev": true, "license": "MIT" }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/pify": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", @@ -4377,6 +4679,29 @@ "@img/sharp-win32-x64": "0.34.5" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", @@ -4494,6 +4819,16 @@ "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", @@ -4595,6 +4930,19 @@ "node": ">= 0.4" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -4779,6 +5127,22 @@ "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", "license": "Apache-2.0" }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/which-typed-array": { "version": "1.1.21", "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.21.tgz", @@ -4973,6 +5337,22 @@ "node": ">=10" } }, + "node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", @@ -5047,7 +5427,7 @@ }, "packages/cli": { "name": "@automattic/wp-codebox-cli", - "version": "0.12.27", + "version": "0.12.28", "dependencies": { "@automattic/wp-codebox-core": "file:../runtime-core", "@automattic/wp-codebox-playground": "file:../runtime-playground" @@ -5090,15 +5470,6 @@ "npm": ">=10.2.3" } }, - "packages/runtime-cloudflare/node_modules/@php-wasm/stream-compression": { - "version": "3.1.45", - "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.45.tgz", - "integrity": "sha512-zLU1AGNEg1tuwm93LHrL8OcV24LyNglTg/CXV5CVZiMPpg1wfIDHJy11qqjZM8iCIJk87qYQhkKOjTnGinAVDg==", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/util": "3.1.45" - } - }, "packages/runtime-cloudflare/node_modules/@php-wasm/universal": { "version": "3.1.45", "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-3.1.45.tgz", @@ -5167,14 +5538,14 @@ }, "packages/runtime-core": { "name": "@automattic/wp-codebox-core", - "version": "0.12.27", + "version": "0.12.28", "dependencies": { "ajv": "^8.20.0" } }, "packages/runtime-playground": { "name": "@automattic/wp-codebox-playground", - "version": "0.12.27", + "version": "0.12.28", "dependencies": { "@automattic/wp-codebox-core": "file:../runtime-core", "@php-wasm/node": "^3.1.45", @@ -5353,15 +5724,6 @@ "npm": ">=10.2.3" } }, - "packages/runtime-playground/node_modules/@php-wasm/stream-compression": { - "version": "3.1.45", - "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.45.tgz", - "integrity": "sha512-zLU1AGNEg1tuwm93LHrL8OcV24LyNglTg/CXV5CVZiMPpg1wfIDHJy11qqjZM8iCIJk87qYQhkKOjTnGinAVDg==", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/util": "3.1.45" - } - }, "packages/runtime-playground/node_modules/@php-wasm/universal": { "version": "3.1.45", "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-3.1.45.tgz", diff --git a/package.json b/package.json index 8118527a..ae9c9e85 100644 --- a/package.json +++ b/package.json @@ -82,6 +82,7 @@ "build": "node ./node_modules/typescript/bin/tsc -b packages/runtime-core packages/runtime-playground packages/cli && node scripts/ensure-cli-bin-executable.mjs", "cloudflare:dry-run": "npm exec -- wrangler deploy --dry-run --config packages/runtime-cloudflare/wrangler.jsonc", "cloudflare:local-gate": "node scripts/cloudflare-local-gate.mjs", + "postinstall": "patch-package", "generate:cloudflare-wordpress-runtime-corpus": "tsx scripts/generate-cloudflare-wordpress-runtime-corpus.ts", "provision:cloudflare-wordpress-runtime-corpus": "node scripts/provision-cloudflare-wordpress-runtime-corpus.mjs", "prepare": "npm run build", @@ -191,7 +192,6 @@ "test:php-artifact-import-idempotency": "php scripts/php-artifact-import-idempotency-smoke.php", "test:php-browser-runtime-local-package": "php scripts/php-browser-runtime-local-package-smoke.php", "test:php-browser-runtime-agent-substrate": "php scripts/php-browser-runtime-agent-substrate-smoke.php", - "test:php-browser-preview-only-session": "php -d zend.assertions=1 -d assert.exception=1 scripts/php-browser-preview-only-session-smoke.php", "test:mcp-client-configs": "tsx tests/mcp-client-configs.test.ts", "test:runtime-tool-policy": "tsx tests/runtime-tool-policy.test.ts", "test:primitive-contract-parity": "tsx tests/primitive-contract-parity.test.ts", @@ -256,6 +256,7 @@ "devDependencies": { "@cloudflare/workers-types": "^5.20260718.1", "@types/node": "^24.0.0", + "patch-package": "^8.0.1", "tsx": "^4.20.0", "wrangler": "^4.112.0" }, @@ -455,6 +456,7 @@ "yargs-parser": "^21.1.1" }, "overrides": { + "@php-wasm/stream-compression": "3.1.45", "ajv": "^8.20.0", "express": "^4.22.2", "qs": "^6.15.2", diff --git a/patches/@php-wasm+stream-compression+3.1.45.patch b/patches/@php-wasm+stream-compression+3.1.45.patch new file mode 100644 index 00000000..dfc38050 --- /dev/null +++ b/patches/@php-wasm+stream-compression+3.1.45.patch @@ -0,0 +1,740 @@ +diff --git a/node_modules/@php-wasm/stream-compression/index.cjs b/node_modules/@php-wasm/stream-compression/index.cjs +index f5fe882..487b0bb 100644 +--- a/node_modules/@php-wasm/stream-compression/index.cjs ++++ b/node_modules/@php-wasm/stream-compression/index.cjs +@@ -1,2 +1,2 @@ +-"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const F=require("@php-wasm/util");function U(...e){const r=new Uint8Array(e.reduce((n,a)=>n+a.length,0));let t=0;for(const n of e)r.set(n,t),t+=n.length;return r}function A(e){if(e===void 0){let r=new Uint8Array;return new TransformStream({transform(t){r=U(r,t)},flush(t){t.enqueue(r)}})}else{const r=new ArrayBuffer(e||0);let t=0;return new TransformStream({transform(n){new Uint8Array(r).set(n,t),t+=n.byteLength},flush(n){n.enqueue(new Uint8Array(r))}})}}function E(e,r){if(r===0)return new ReadableStream({start(a){a.close()}});const t=e.getReader({mode:"byob"});let n=0;return new ReadableStream({async pull(a){const{value:i,done:s}=await t.read(new Uint8Array(r-n));if(s){t.releaseLock(),a.close();return}n+=i.length,a.enqueue(i),n>=r&&(t.releaseLock(),a.close())},cancel(){t.cancel()}})}async function c(e,r){return r!==void 0&&(e=E(e,r)),await e.pipeThrough(A(r)).getReader().read().then(({value:t})=>t)}async function _(e,r){return new File([await c(r)],e)}function T(e){if(e instanceof ReadableStream)return e;let r;return Symbol.asyncIterator in e?r=e[Symbol.asyncIterator]():Symbol.iterator in e?r=e[Symbol.iterator]():r=e,new ReadableStream({async pull(t){const{done:n,value:a}=await r.next();if(n){t.close();return}t.enqueue(a)}})}class D extends File{static fromArrayBuffer(r,t,n){return new D(new ReadableStream({start(a){a.enqueue(new Uint8Array(r)),a.close()}}),t,n)}constructor(r,t,n){super([],t,{type:n==null?void 0:n.type}),this.readableStream=r,this.filesize=n==null?void 0:n.filesize}slice(){throw new Error("slice() is not possible on a StreamedFile")}stream(){return this.readableStream}async text(){return new TextDecoder().decode(await this.arrayBuffer())}async arrayBuffer(){return await c(this.stream())}}ReadableStream.prototype[Symbol.asyncIterator]||(ReadableStream.prototype[Symbol.asyncIterator]=async function*(){const e=this.getReader();try{for(;;){const{done:r,value:t}=await e.read();if(r)return;yield t}}finally{e.releaseLock()}},ReadableStream.prototype.iterate=ReadableStream.prototype[Symbol.asyncIterator]);const N=32,h=67324752,y=33639248,g=101010256,M=0,R=8;function b(e){return new TransformStream({transform(r,t){e(r)&&t.enqueue(r)}})}function k(e){let r=!1;return new TransformStream({async transform(t,n){r||(r=!0,n.enqueue(e)),n.enqueue(t)}})}function x(e){return new TransformStream({async transform(r,t){t.enqueue(r)},async flush(r){r.enqueue(e)}})}function w(e,r){return O(e,r).pipeThrough(new TransformStream({async transform(t,n){const a=new File([t.bytes],new TextDecoder().decode(t.path),{type:t.isDirectory?"directory":void 0});n.enqueue(a)}}))}const v=()=>!0;function O(e,r=v){return new ReadableStream({async pull(n){const a=await P(e);if(!a){n.close();return}n.enqueue(a)}}).pipeThrough(b(({signature:n})=>n===h)).pipeThrough(b(r))}async function P(e){const t=new DataView((await c(e,4)).buffer).getUint32(0,!0);return t===h?await L(e,!0):t===y?await C(e,!0):t===g?await I(e,!0):null}async function L(e,r=!1){if(!r&&new DataView((await c(e,4)).buffer).getUint32(0,!0)!==h)return null;const t=new DataView((await c(e,26)).buffer),n=t.getUint16(22,!0),a=t.getUint16(24,!0),i={signature:h,version:t.getUint32(0,!0),generalPurpose:t.getUint16(2,!0),compressionMethod:t.getUint16(4,!0),lastModifiedTime:t.getUint16(6,!0),lastModifiedDate:t.getUint16(8,!0),crc:t.getUint32(10,!0),compressedSize:t.getUint32(14,!0),uncompressedSize:t.getUint32(18,!0)};i.path=await c(e,n),i.isDirectory=B(i.path),i.extra=await c(e,a);let s=E(e,i.compressedSize);if(i.compressionMethod===R){const o=new Uint8Array(10);o.set([31,139,8]);const f=new Uint8Array(8),u=new DataView(f.buffer);u.setUint32(0,i.crc,!0),u.setUint32(4,i.uncompressedSize%2**32,!0),s=s.pipeThrough(k(o)).pipeThrough(x(f)).pipeThrough(new DecompressionStream("gzip"))}return i.bytes=await s.pipeThrough(A(i.uncompressedSize)).getReader().read().then(({value:o})=>o),i}async function C(e,r=!1){if(!r&&new DataView((await c(e,4)).buffer).getUint32(0,!0)!==y)return null;const t=new DataView((await c(e,42)).buffer),n=t.getUint16(24,!0),a=t.getUint16(26,!0),i=t.getUint16(28,!0),s={signature:y,versionCreated:t.getUint16(0,!0),versionNeeded:t.getUint16(2,!0),generalPurpose:t.getUint16(4,!0),compressionMethod:t.getUint16(6,!0),lastModifiedTime:t.getUint16(8,!0),lastModifiedDate:t.getUint16(10,!0),crc:t.getUint32(12,!0),compressedSize:t.getUint32(16,!0),uncompressedSize:t.getUint32(20,!0),diskNumber:t.getUint16(30,!0),internalAttributes:t.getUint16(32,!0),externalAttributes:t.getUint32(34,!0),firstByteAt:t.getUint32(38,!0)};return s.lastByteAt=s.firstByteAt+N+n+i+a+s.compressedSize-1,s.path=await c(e,n),s.isDirectory=B(s.path),s.extra=await c(e,a),s.fileComment=await c(e,i),s}function B(e){return e[e.byteLength-1]==47}async function I(e,r=!1){if(!r&&new DataView((await c(e,4)).buffer).getUint32(0,!0)!==g)return null;const t=new DataView((await c(e,18)).buffer),n={signature:g,numberOfDisks:t.getUint16(0,!0),centralDirectoryStartDisk:t.getUint16(2,!0),numberCentralDirectoryRecordsOnThisDisk:t.getUint16(4,!0),numberCentralDirectoryRecords:t.getUint16(6,!0),centralDirectorySize:t.getUint32(8,!0),centralDirectoryOffset:t.getUint32(12,!0)},a=t.getUint16(16,!0);return n.comment=await c(e,a),n}const z=110*1024,V=10*1024,H=1024*1024*1,Z=new F.Semaphore({concurrency:10}),S=()=>!0;async function G(e,r=S){if(r===S){const d=await fetch(e);return w(d.body)}const t=await q(e);if(t<=H){const d=await fetch(e);return w(d.body)}const n=await fetch(e,{headers:{Range:"bytes=0-0","Accept-Encoding":"none"}}),[a,i]=n.body.tee(),s=a.getReader(),{value:o}=await s.read(),{done:f}=await s.read();if(s.releaseLock(),a.cancel(),!((o==null?void 0:o.length)===1&&f))return w(i);i.cancel();const l=await J(e,t);return W(l).pipeThrough(b(r)).pipeThrough($()).pipeThrough(j(l))}function W(e){let r;return new ReadableStream({async start(){r=await Y(e)},async pull(t){const n=await C(r);if(!n){t.close();return}t.enqueue(n)}})}async function Y(e){const r=z;let t=new Uint8Array,n=e.length;do{n=Math.max(0,n-r);const a=Math.min(n+r-1,e.length-1),i=await c(await e.streamBytes(n,a));t=U(i,t);const s=new DataView(i.buffer);for(let o=s.byteLength-4;o>=0;o--){if(s.getUint32(o,!0)!==g)continue;const u=o+12+4;if(t.byteLengthn&&(t=t.slice(l-n));return new Blob([t]).stream()}}while(n>=0);throw new Error("Central directory not found")}function $(){let e=0,r=[];return new TransformStream({transform(t,n){t.firstByteAt>e+V&&(n.enqueue(r),r=[]),e=t.lastByteAt,r.push(t)},flush(t){t.enqueue(r)}})}function j(e){let r=!1,t=0,n;const a=[],i=new WritableStream({write(o,f){o.length&&(++t,K(e,o).then(u=>{a.push([o,u])}).catch(u=>{f.error(u)}).finally(()=>{--t}))},abort(){r=!0,n.close()},async close(){r=!0}});return{readable:new ReadableStream({start(o){n=o},async pull(o){for(;;){if(r&&!a.length&&t===0){o.close();return}if(!a.length){await new Promise(p=>setTimeout(p,50));continue}const[l,d]=a[0],m=await L(d);if(!m){a.shift();continue}if(l.find(p=>p.path===m.path)){o.enqueue(m);break}}}}),writable:i}}async function K(e,r){const t=await Z.acquire();try{const n=r[r.length-1];return await e.streamBytes(r[0].firstByteAt,n.lastByteAt)}finally{t()}}async function q(e){return await fetch(e,{method:"HEAD"}).then(r=>r.headers.get("Content-Length")).then(r=>{if(!r)throw new Error("Content-Length header is missing");const t=parseInt(r,10);if(isNaN(t)||t<0)throw new Error("Content-Length header is invalid");return t})}async function J(e,r){return r===void 0&&(r=await q(e)),{length:r,streamBytes:async(t,n)=>await fetch(e,{headers:{Range:`bytes=${t}-${n-1}`,"Accept-Encoding":"none"}}).then(a=>a.body)}}function Q(e){return T(e).pipeThrough(X())}function X(){const e=new Map;let r=0;return new TransformStream({async transform(t,n){const a=new Uint8Array(await t.arrayBuffer());let i=await c(new Blob([a]).stream().pipeThrough(new CompressionStream("gzip")));const s=new DataView(i.buffer).getUint32(i.byteLength-8,!0);i=i.slice(10,i.byteLength-8);const o=new TextEncoder().encode(t.name),f={signature:h,version:2,generalPurpose:0,compressionMethod:t.type==="directory"||i.byteLength===0?M:R,lastModifiedTime:0,lastModifiedDate:0,crc:s,compressedSize:i.byteLength,uncompressedSize:a.byteLength,path:o,extra:new Uint8Array(0)};e.set(r,f);const u=ee(f);n.enqueue(u),r+=u.byteLength,n.enqueue(i),r+=i.byteLength},flush(t){const n=r;let a=0;for(const[o,f]of e.entries()){const u={...f,signature:y,fileComment:new Uint8Array(0),diskNumber:0,internalAttributes:0,externalAttributes:0},l=te(u,o);t.enqueue(l),a+=l.byteLength}const i={signature:g,numberOfDisks:0,centralDirectoryOffset:n,centralDirectorySize:a,centralDirectoryStartDisk:0,numberCentralDirectoryRecordsOnThisDisk:e.size,numberCentralDirectoryRecords:e.size,comment:new Uint8Array(0)},s=ne(i);t.enqueue(s),e.clear()}})}function ee(e){const r=new ArrayBuffer(30+e.path.byteLength+e.extra.byteLength),t=new DataView(r);t.setUint32(0,e.signature,!0),t.setUint16(4,e.version,!0),t.setUint16(6,e.generalPurpose,!0),t.setUint16(8,e.compressionMethod,!0),t.setUint16(10,e.lastModifiedDate,!0),t.setUint16(12,e.lastModifiedTime,!0),t.setUint32(14,e.crc,!0),t.setUint32(18,e.compressedSize,!0),t.setUint32(22,e.uncompressedSize,!0),t.setUint16(26,e.path.byteLength,!0),t.setUint16(28,e.extra.byteLength,!0);const n=new Uint8Array(r);return n.set(e.path,30),n.set(e.extra,30+e.path.byteLength),n}function te(e,r){const t=new ArrayBuffer(46+e.path.byteLength+e.extra.byteLength),n=new DataView(t);n.setUint32(0,e.signature,!0),n.setUint16(4,e.versionCreated,!0),n.setUint16(6,e.versionNeeded,!0),n.setUint16(8,e.generalPurpose,!0),n.setUint16(10,e.compressionMethod,!0),n.setUint16(12,e.lastModifiedDate,!0),n.setUint16(14,e.lastModifiedTime,!0),n.setUint32(16,e.crc,!0),n.setUint32(20,e.compressedSize,!0),n.setUint32(24,e.uncompressedSize,!0),n.setUint16(28,e.path.byteLength,!0),n.setUint16(30,e.extra.byteLength,!0),n.setUint16(32,e.fileComment.byteLength,!0),n.setUint16(34,e.diskNumber,!0),n.setUint16(36,e.internalAttributes,!0),n.setUint32(38,e.externalAttributes,!0),n.setUint32(42,r,!0);const a=new Uint8Array(t);return a.set(e.path,46),a.set(e.extra,46+e.path.byteLength),a}function ne(e){const r=new ArrayBuffer(22+e.comment.byteLength),t=new DataView(r);t.setUint32(0,e.signature,!0),t.setUint16(4,e.numberOfDisks,!0),t.setUint16(6,e.centralDirectoryStartDisk,!0),t.setUint16(8,e.numberCentralDirectoryRecordsOnThisDisk,!0),t.setUint16(10,e.numberCentralDirectoryRecords,!0),t.setUint32(12,e.centralDirectorySize,!0),t.setUint32(16,e.centralDirectoryOffset,!0),t.setUint16(20,e.comment.byteLength,!0);const n=new Uint8Array(r);return n.set(e.comment,22),n}exports.StreamedFile=D;exports.collectBytes=c;exports.collectFile=_;exports.decodeRemoteZip=G;exports.decodeZip=w;exports.encodeZip=Q;exports.iteratorToStream=T; ++"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function m(...e){const n=new Uint8Array(e.reduce((r,a)=>r+a.length,0));let t=0;for(const r of e)n.set(r,t),t+=r.length;return n}function b(e){if(e===void 0){let n=new Uint8Array;return new TransformStream({transform(t){n=m(n,t)},flush(t){t.enqueue(n)}})}else{const n=new ArrayBuffer(e||0);let t=0;return new TransformStream({transform(r){new Uint8Array(n).set(r,t),t+=r.byteLength},flush(r){r.enqueue(new Uint8Array(n))}})}}function S(e,n){if(n===0)return new ReadableStream({start(a){a.close()}});const t=e.getReader({mode:"byob"});let r=0;return new ReadableStream({async pull(a){const{value:i,done:s}=await t.read(new Uint8Array(n-r));if(s){t.releaseLock(),a.close();return}r+=i.length,a.enqueue(i),r>=n&&(t.releaseLock(),a.close())},cancel(){t.cancel()}})}async function c(e,n){return n!==void 0&&(e=S(e,n)),await e.pipeThrough(b(n)).getReader().read().then(({value:t})=>t)}async function B(e,n){return new File([await c(n)],e)}function A(e){if(e instanceof ReadableStream)return e;let n;return Symbol.asyncIterator in e?n=e[Symbol.asyncIterator]():Symbol.iterator in e?n=e[Symbol.iterator]():n=e,new ReadableStream({async pull(t){const{done:r,value:a}=await n.next();if(r){t.close();return}t.enqueue(a)}})}class U extends File{static fromArrayBuffer(n,t,r){return new U(new ReadableStream({start(a){a.enqueue(new Uint8Array(n)),a.close()}}),t,r)}constructor(n,t,r){super([],t,{type:r==null?void 0:r.type}),this.readableStream=n,this.filesize=r==null?void 0:r.filesize}slice(){throw new Error("slice() is not possible on a StreamedFile")}stream(){return this.readableStream}async text(){return new TextDecoder().decode(await this.arrayBuffer())}async arrayBuffer(){return await c(this.stream())}}ReadableStream.prototype[Symbol.asyncIterator]||(ReadableStream.prototype[Symbol.asyncIterator]=async function*(){const e=this.getReader();try{for(;;){const{done:n,value:t}=await e.read();if(n)return;yield t}}finally{e.releaseLock()}},ReadableStream.prototype.iterate=ReadableStream.prototype[Symbol.asyncIterator]);const _=30,h=67324752,y=33639248,g=101010256,N=0,E=8;function p(e){return new TransformStream({transform(n,t){e(n)&&t.enqueue(n)}})}function M(e){let n=!1;return new TransformStream({async transform(t,r){n||(n=!0,r.enqueue(e)),r.enqueue(t)}})}function v(e){return new TransformStream({async transform(n,t){t.enqueue(n)},async flush(n){n.enqueue(e)}})}function w(e,n){return F(e,n).pipeThrough(new TransformStream({async transform(t,r){const a=new File([t.bytes],new TextDecoder().decode(t.path),{type:t.isDirectory?"directory":void 0});r.enqueue(a)}}))}const x=()=>!0;function F(e,n=x){return new ReadableStream({async pull(r){const a=await q(e);if(!a){r.close();return}r.enqueue(a)}}).pipeThrough(p(({signature:r})=>r===h)).pipeThrough(p(n))}async function q(e){const t=new DataView((await c(e,4)).buffer).getUint32(0,!0);return t===h?await T(e,!0):t===y?await L(e,!0):t===g?await k(e,!0):null}async function T(e,n=!1){if(!n&&new DataView((await c(e,4)).buffer).getUint32(0,!0)!==h)return null;const t=new DataView((await c(e,26)).buffer),r=t.getUint16(22,!0),a=t.getUint16(24,!0),i={signature:h,version:t.getUint32(0,!0),generalPurpose:t.getUint16(2,!0),compressionMethod:t.getUint16(4,!0),lastModifiedTime:t.getUint16(6,!0),lastModifiedDate:t.getUint16(8,!0),crc:t.getUint32(10,!0),compressedSize:t.getUint32(14,!0),uncompressedSize:t.getUint32(18,!0)};i.path=await c(e,r),i.isDirectory=R(i.path),i.extra=await c(e,a);let s=S(e,i.compressedSize);if(i.compressionMethod===E){const o=new Uint8Array(10);o.set([31,139,8]);const u=new Uint8Array(8),f=new DataView(u.buffer);f.setUint32(0,i.crc,!0),f.setUint32(4,i.uncompressedSize%2**32,!0),s=s.pipeThrough(M(o)).pipeThrough(v(u)).pipeThrough(new DecompressionStream("gzip"))}return i.bytes=await s.pipeThrough(b(i.uncompressedSize)).getReader().read().then(({value:o})=>o),i}async function L(e,n=!1){if(!n&&new DataView((await c(e,4)).buffer).getUint32(0,!0)!==y)return null;const t=new DataView((await c(e,42)).buffer),r=t.getUint16(24,!0),a=t.getUint16(26,!0),i=t.getUint16(28,!0),s={signature:y,versionCreated:t.getUint16(0,!0),versionNeeded:t.getUint16(2,!0),generalPurpose:t.getUint16(4,!0),compressionMethod:t.getUint16(6,!0),lastModifiedTime:t.getUint16(8,!0),lastModifiedDate:t.getUint16(10,!0),crc:t.getUint32(12,!0),compressedSize:t.getUint32(16,!0),uncompressedSize:t.getUint32(20,!0),diskNumber:t.getUint16(30,!0),internalAttributes:t.getUint16(32,!0),externalAttributes:t.getUint32(34,!0),firstByteAt:t.getUint32(38,!0)};return s.lastByteAt=s.firstByteAt+_+r+a+s.compressedSize,s.path=await c(e,r),s.isDirectory=R(s.path),s.extra=await c(e,a),s.fileComment=await c(e,i),s}function R(e){return e[e.byteLength-1]==47}async function k(e,n=!1){if(!n&&new DataView((await c(e,4)).buffer).getUint32(0,!0)!==g)return null;const t=new DataView((await c(e,18)).buffer),r={signature:g,numberOfDisks:t.getUint16(0,!0),centralDirectoryStartDisk:t.getUint16(2,!0),numberCentralDirectoryRecordsOnThisDisk:t.getUint16(4,!0),numberCentralDirectoryRecords:t.getUint16(6,!0),centralDirectorySize:t.getUint32(8,!0),centralDirectoryOffset:t.getUint32(12,!0)},a=t.getUint16(16,!0);return r.comment=await c(e,a),r}const O=110*1024,z=10*1024,I=48,P=1024*1024*1,D=()=>!0;async function V(e,n=D){if(n===D){const l=await fetch(e);return w(l.body)}const t=await C(e);if(t<=P){const l=await fetch(e);return w(l.body)}const r=await fetch(e,{headers:{Range:"bytes=0-0","Accept-Encoding":"none"}}),[a,i]=r.body.tee(),s=a.getReader(),{value:o}=await s.read(),{done:u}=await s.read();if(s.releaseLock(),a.cancel(),!((o==null?void 0:o.length)===1&&u))return w(i);i.cancel();const d=await j(e,t);return H(d).pipeThrough(p(n)).pipeThrough(G(d.length)).pipeThrough($(d))}function H(e){const n=Z(e);return new ReadableStream({async pull(t){const r=await L(await n);if(!r){t.close();return}t.enqueue(r)}})}async function Z(e){const n=O;let t=new Uint8Array,r=e.length;do{r=Math.max(0,r-n);const a=Math.min(r+n,e.length),i=await c(await e.streamBytes(r,a));t=m(i,t);const s=new DataView(i.buffer);for(let o=s.byteLength-4;o>=0;o--){if(s.getUint32(o,!0)!==g)continue;const f=o+12+4;if(t.byteLengthr&&(t=t.slice(d-r));return new Blob([t]).stream()}}while(r>=0);throw new Error("Central directory not found")}function G(e){let n=0,t=[];const r=Math.max(z,Math.ceil(e/I));return new TransformStream({transform(a,i){a.firstByteAt>n+r&&(i.enqueue(t),t=[]),n=a.lastByteAt,t.push(a)},flush(a){a.enqueue(t)}})}function $(e){return new TransformStream({async transform(n,t){if(!n.length)return;const r=await W(e,n);for(;;){const a=await T(r);if(!a)return;n.some(i=>Y(i.path,a.path))&&t.enqueue(a)}}})}function Y(e,n){return e.length===n.length&&e.every((t,r)=>t===n[r])}async function W(e,n){const t=n[n.length-1];return await e.streamBytes(n[0].firstByteAt,t.lastByteAt)}async function C(e){return await fetch(e,{method:"HEAD"}).then(n=>n.headers.get("Content-Length")).then(n=>{if(!n)throw new Error("Content-Length header is missing");const t=parseInt(n,10);if(isNaN(t)||t<0)throw new Error("Content-Length header is invalid");return t})}async function j(e,n){return n===void 0&&(n=await C(e)),{length:n,streamBytes:async(t,r)=>{const a=await fetch(e,{headers:{Range:`bytes=${t}-${r-1}`,"Accept-Encoding":"none"}});if(a.status!==206||!a.body)throw new Error(`Expected a partial zip range response, received ${a.status} ${a.statusText}`);return a.body}}}function K(e){return A(e).pipeThrough(Q())}function Q(){const e=new Map;let n=0;return new TransformStream({async transform(t,r){const a=new Uint8Array(await t.arrayBuffer());let i=await c(new Blob([a]).stream().pipeThrough(new CompressionStream("gzip")));const s=new DataView(i.buffer).getUint32(i.byteLength-8,!0);i=i.slice(10,i.byteLength-8);const o=new TextEncoder().encode(t.name),u={signature:h,version:2,generalPurpose:0,compressionMethod:t.type==="directory"||i.byteLength===0?N:E,lastModifiedTime:0,lastModifiedDate:0,crc:s,compressedSize:i.byteLength,uncompressedSize:a.byteLength,path:o,extra:new Uint8Array(0)};e.set(n,u);const f=X(u);r.enqueue(f),n+=f.byteLength,r.enqueue(i),n+=i.byteLength},flush(t){const r=n;let a=0;for(const[o,u]of e.entries()){const f={...u,signature:y,fileComment:new Uint8Array(0),diskNumber:0,internalAttributes:0,externalAttributes:0},d=J(f,o);t.enqueue(d),a+=d.byteLength}const i={signature:g,numberOfDisks:0,centralDirectoryOffset:r,centralDirectorySize:a,centralDirectoryStartDisk:0,numberCentralDirectoryRecordsOnThisDisk:e.size,numberCentralDirectoryRecords:e.size,comment:new Uint8Array(0)},s=ee(i);t.enqueue(s),e.clear()}})}function X(e){const n=new ArrayBuffer(30+e.path.byteLength+e.extra.byteLength),t=new DataView(n);t.setUint32(0,e.signature,!0),t.setUint16(4,e.version,!0),t.setUint16(6,e.generalPurpose,!0),t.setUint16(8,e.compressionMethod,!0),t.setUint16(10,e.lastModifiedDate,!0),t.setUint16(12,e.lastModifiedTime,!0),t.setUint32(14,e.crc,!0),t.setUint32(18,e.compressedSize,!0),t.setUint32(22,e.uncompressedSize,!0),t.setUint16(26,e.path.byteLength,!0),t.setUint16(28,e.extra.byteLength,!0);const r=new Uint8Array(n);return r.set(e.path,30),r.set(e.extra,30+e.path.byteLength),r}function J(e,n){const t=new ArrayBuffer(46+e.path.byteLength+e.extra.byteLength),r=new DataView(t);r.setUint32(0,e.signature,!0),r.setUint16(4,e.versionCreated,!0),r.setUint16(6,e.versionNeeded,!0),r.setUint16(8,e.generalPurpose,!0),r.setUint16(10,e.compressionMethod,!0),r.setUint16(12,e.lastModifiedDate,!0),r.setUint16(14,e.lastModifiedTime,!0),r.setUint32(16,e.crc,!0),r.setUint32(20,e.compressedSize,!0),r.setUint32(24,e.uncompressedSize,!0),r.setUint16(28,e.path.byteLength,!0),r.setUint16(30,e.extra.byteLength,!0),r.setUint16(32,e.fileComment.byteLength,!0),r.setUint16(34,e.diskNumber,!0),r.setUint16(36,e.internalAttributes,!0),r.setUint32(38,e.externalAttributes,!0),r.setUint32(42,n,!0);const a=new Uint8Array(t);return a.set(e.path,46),a.set(e.extra,46+e.path.byteLength),a}function ee(e){const n=new ArrayBuffer(22+e.comment.byteLength),t=new DataView(n);t.setUint32(0,e.signature,!0),t.setUint16(4,e.numberOfDisks,!0),t.setUint16(6,e.centralDirectoryStartDisk,!0),t.setUint16(8,e.numberCentralDirectoryRecordsOnThisDisk,!0),t.setUint16(10,e.numberCentralDirectoryRecords,!0),t.setUint32(12,e.centralDirectorySize,!0),t.setUint32(16,e.centralDirectoryOffset,!0),t.setUint16(20,e.comment.byteLength,!0);const r=new Uint8Array(n);return r.set(e.comment,22),r}exports.StreamedFile=U;exports.collectBytes=c;exports.collectFile=B;exports.decodeRemoteZip=V;exports.decodeZip=w;exports.encodeZip=K;exports.iteratorToStream=A; + //# sourceMappingURL=index.cjs.map +diff --git a/node_modules/@php-wasm/stream-compression/index.js b/node_modules/@php-wasm/stream-compression/index.js +index 0cc9cb5..d13408a 100644 +--- a/node_modules/@php-wasm/stream-compression/index.js ++++ b/node_modules/@php-wasm/stream-compression/index.js +@@ -1,76 +1,75 @@ +-import { Semaphore as _ } from "@php-wasm/util"; +-function U(...e) { +- const r = new Uint8Array( +- e.reduce((n, a) => n + a.length, 0) ++function m(...e) { ++ const n = new Uint8Array( ++ e.reduce((r, a) => r + a.length, 0) + ); + let t = 0; +- for (const n of e) +- r.set(n, t), t += n.length; +- return r; ++ for (const r of e) ++ n.set(r, t), t += r.length; ++ return n; + } +-function S(e) { ++function D(e) { + if (e === void 0) { +- let r = new Uint8Array(); ++ let n = new Uint8Array(); + return new TransformStream({ + transform(t) { +- r = U(r, t); ++ n = m(n, t); + }, + flush(t) { +- t.enqueue(r); ++ t.enqueue(n); + } + }); + } else { +- const r = new ArrayBuffer(e || 0); ++ const n = new ArrayBuffer(e || 0); + let t = 0; + return new TransformStream({ +- transform(n) { +- new Uint8Array(r).set(n, t), t += n.byteLength; ++ transform(r) { ++ new Uint8Array(n).set(r, t), t += r.byteLength; + }, +- flush(n) { +- n.enqueue(new Uint8Array(r)); ++ flush(r) { ++ r.enqueue(new Uint8Array(n)); + } + }); + } + } +-function A(e, r) { +- if (r === 0) ++function b(e, n) { ++ if (n === 0) + return new ReadableStream({ + start(a) { + a.close(); + } + }); + const t = e.getReader({ mode: "byob" }); +- let n = 0; ++ let r = 0; + return new ReadableStream({ + async pull(a) { + const { value: i, done: s } = await t.read( +- new Uint8Array(r - n) ++ new Uint8Array(n - r) + ); + if (s) { + t.releaseLock(), a.close(); + return; + } +- n += i.length, a.enqueue(i), n >= r && (t.releaseLock(), a.close()); ++ r += i.length, a.enqueue(i), r >= n && (t.releaseLock(), a.close()); + }, + cancel() { + t.cancel(); + } + }); + } +-async function c(e, r) { +- return r !== void 0 && (e = A(e, r)), await e.pipeThrough(S(r)).getReader().read().then(({ value: t }) => t); ++async function u(e, n) { ++ return n !== void 0 && (e = b(e, n)), await e.pipeThrough(D(n)).getReader().read().then(({ value: t }) => t); + } +-async function re(e, r) { +- return new File([await c(r)], e); ++async function j(e, n) { ++ return new File([await u(n)], e); + } +-function q(e) { ++function C(e) { + if (e instanceof ReadableStream) + return e; +- let r; +- return Symbol.asyncIterator in e ? r = e[Symbol.asyncIterator]() : Symbol.iterator in e ? r = e[Symbol.iterator]() : r = e, new ReadableStream({ ++ let n; ++ return Symbol.asyncIterator in e ? n = e[Symbol.asyncIterator]() : Symbol.iterator in e ? n = e[Symbol.iterator]() : n = e, new ReadableStream({ + async pull(t) { +- const { done: n, value: a } = await r.next(); +- if (n) { ++ const { done: r, value: a } = await n.next(); ++ if (r) { + t.close(); + return; + } +@@ -78,16 +77,16 @@ function q(e) { + } + }); + } +-class E extends File { +- static fromArrayBuffer(r, t, n) { +- return new E( ++class S extends File { ++ static fromArrayBuffer(n, t, r) { ++ return new S( + new ReadableStream({ + start(a) { +- a.enqueue(new Uint8Array(r)), a.close(); ++ a.enqueue(new Uint8Array(n)), a.close(); + } + }), + t, +- n ++ r + ); + } + /** +@@ -97,8 +96,8 @@ class E extends File { + * @param name The name of the file. + * @param options An object containing options such as the MIME type and file size. + */ +- constructor(r, t, n) { +- super([], t, { type: n == null ? void 0 : n.type }), this.readableStream = r, this.filesize = n == null ? void 0 : n.filesize; ++ constructor(n, t, r) { ++ super([], t, { type: r == null ? void 0 : r.type }), this.readableStream = n, this.filesize = r == null ? void 0 : r.filesize; + } + /** + * Overrides the slice() method of the File class. +@@ -130,15 +129,15 @@ class E extends File { + * @returns File data as an ArrayBuffer. + */ + async arrayBuffer() { +- return await c(this.stream()); ++ return await u(this.stream()); + } + } + ReadableStream.prototype[Symbol.asyncIterator] || (ReadableStream.prototype[Symbol.asyncIterator] = async function* () { + const e = this.getReader(); + try { + for (; ; ) { +- const { done: r, value: t } = await e.read(); +- if (r) ++ const { done: n, value: t } = await e.read(); ++ if (n) + return; + yield t; + } +@@ -147,36 +146,36 @@ ReadableStream.prototype[Symbol.asyncIterator] || (ReadableStream.prototype[Symb + } + }, ReadableStream.prototype.iterate = // @ts-ignore + ReadableStream.prototype[Symbol.asyncIterator]); +-const N = 32, h = 67324752, w = 33639248, g = 101010256, x = 0, L = 8; +-function b(e) { ++const B = 30, h = 67324752, w = 33639248, g = 101010256, _ = 0, A = 8; ++function p(e) { + return new TransformStream({ +- transform(r, t) { +- e(r) && t.enqueue(r); ++ transform(n, t) { ++ e(n) && t.enqueue(n); + } + }); + } +-function F(e) { +- let r = !1; ++function N(e) { ++ let n = !1; + return new TransformStream({ +- async transform(t, n) { +- r || (r = !0, n.enqueue(e)), n.enqueue(t); ++ async transform(t, r) { ++ n || (n = !0, r.enqueue(e)), r.enqueue(t); + } + }); + } +-function k(e) { ++function x(e) { + return new TransformStream({ +- async transform(r, t) { +- t.enqueue(r); ++ async transform(n, t) { ++ t.enqueue(n); + }, +- async flush(r) { +- r.enqueue(e); ++ async flush(n) { ++ n.enqueue(e); + } + }); + } +-function p(e, r) { +- return v(e, r).pipeThrough( ++function y(e, n) { ++ return v(e, n).pipeThrough( + new TransformStream({ +- async transform(t, n) { ++ async transform(t, r) { + const a = new File( + [t.bytes], + new TextDecoder().decode(t.path), +@@ -184,36 +183,36 @@ function p(e, r) { + type: t.isDirectory ? "directory" : void 0 + } + ); +- n.enqueue(a); ++ r.enqueue(a); + } + }) + ); + } + const M = () => !0; +-function v(e, r = M) { ++function v(e, n = M) { + return new ReadableStream({ +- async pull(n) { +- const a = await O(e); ++ async pull(r) { ++ const a = await F(e); + if (!a) { +- n.close(); ++ r.close(); + return; + } +- n.enqueue(a); ++ r.enqueue(a); + } + }).pipeThrough( +- b(({ signature: n }) => n === h) ++ p(({ signature: r }) => r === h) + ).pipeThrough( +- b(r) ++ p(n) + ); + } +-async function O(e) { +- const t = new DataView((await c(e, 4)).buffer).getUint32(0, !0); +- return t === h ? await R(e, !0) : t === w ? await T(e, !0) : t === g ? await P(e, !0) : null; ++async function F(e) { ++ const t = new DataView((await u(e, 4)).buffer).getUint32(0, !0); ++ return t === h ? await E(e, !0) : t === w ? await T(e, !0) : t === g ? await q(e, !0) : null; + } +-async function R(e, r = !1) { +- if (!r && new DataView((await c(e, 4)).buffer).getUint32(0, !0) !== h) ++async function E(e, n = !1) { ++ if (!n && new DataView((await u(e, 4)).buffer).getUint32(0, !0) !== h) + return null; +- const t = new DataView((await c(e, 26)).buffer), n = t.getUint16(22, !0), a = t.getUint16(24, !0), i = { ++ const t = new DataView((await u(e, 26)).buffer), r = t.getUint16(22, !0), a = t.getUint16(24, !0), i = { + signature: h, + version: t.getUint32(0, !0), + generalPurpose: t.getUint16(2, !0), +@@ -224,20 +223,20 @@ async function R(e, r = !1) { + compressedSize: t.getUint32(14, !0), + uncompressedSize: t.getUint32(18, !0) + }; +- i.path = await c(e, n), i.isDirectory = C(i.path), i.extra = await c(e, a); +- let s = A(e, i.compressedSize); +- if (i.compressionMethod === L) { ++ i.path = await u(e, r), i.isDirectory = L(i.path), i.extra = await u(e, a); ++ let s = b(e, i.compressedSize); ++ if (i.compressionMethod === A) { + const o = new Uint8Array(10); + o.set([31, 139, 8]); +- const f = new Uint8Array(8), u = new DataView(f.buffer); +- u.setUint32(0, i.crc, !0), u.setUint32(4, i.uncompressedSize % 2 ** 32, !0), s = s.pipeThrough(F(o)).pipeThrough(k(f)).pipeThrough(new DecompressionStream("gzip")); ++ const c = new Uint8Array(8), f = new DataView(c.buffer); ++ f.setUint32(0, i.crc, !0), f.setUint32(4, i.uncompressedSize % 2 ** 32, !0), s = s.pipeThrough(N(o)).pipeThrough(x(c)).pipeThrough(new DecompressionStream("gzip")); + } +- return i.bytes = await s.pipeThrough(S(i.uncompressedSize)).getReader().read().then(({ value: o }) => o), i; ++ return i.bytes = await s.pipeThrough(D(i.uncompressedSize)).getReader().read().then(({ value: o }) => o), i; + } +-async function T(e, r = !1) { +- if (!r && new DataView((await c(e, 4)).buffer).getUint32(0, !0) !== w) ++async function T(e, n = !1) { ++ if (!n && new DataView((await u(e, 4)).buffer).getUint32(0, !0) !== w) + return null; +- const t = new DataView((await c(e, 42)).buffer), n = t.getUint16(24, !0), a = t.getUint16(26, !0), i = t.getUint16(28, !0), s = { ++ const t = new DataView((await u(e, 42)).buffer), r = t.getUint16(24, !0), a = t.getUint16(26, !0), i = t.getUint16(28, !0), s = { + signature: w, + versionCreated: t.getUint16(0, !0), + versionNeeded: t.getUint16(2, !0), +@@ -253,18 +252,18 @@ async function T(e, r = !1) { + externalAttributes: t.getUint32(34, !0), + firstByteAt: t.getUint32(38, !0) + }; +- return s.lastByteAt = s.firstByteAt + N + n + i + a + s.compressedSize - 1, s.path = await c(e, n), s.isDirectory = C(s.path), s.extra = await c(e, a), s.fileComment = await c( ++ return s.lastByteAt = s.firstByteAt + B + r + a + s.compressedSize, s.path = await u(e, r), s.isDirectory = L(s.path), s.extra = await u(e, a), s.fileComment = await u( + e, + i + ), s; + } +-function C(e) { ++function L(e) { + return e[e.byteLength - 1] == 47; + } +-async function P(e, r = !1) { +- if (!r && new DataView((await c(e, 4)).buffer).getUint32(0, !0) !== g) ++async function q(e, n = !1) { ++ if (!n && new DataView((await u(e, 4)).buffer).getUint32(0, !0) !== g) + return null; +- const t = new DataView((await c(e, 18)).buffer), n = { ++ const t = new DataView((await u(e, 18)).buffer), r = { + signature: g, + numberOfDisks: t.getUint16(0, !0), + centralDirectoryStartDisk: t.getUint16(2, !0), +@@ -273,20 +272,20 @@ async function P(e, r = !1) { + centralDirectorySize: t.getUint32(8, !0), + centralDirectoryOffset: t.getUint32(12, !0) + }, a = t.getUint16(16, !0); +- return n.comment = await c(e, a), n; ++ return r.comment = await u(e, a), r; + } +-const I = 110 * 1024, z = 10 * 1024, V = 1024 * 1024 * 1, H = new _({ concurrency: 10 }), D = () => !0; +-async function ae(e, r = D) { +- if (r === D) { +- const d = await fetch(e); +- return p(d.body); ++const k = 110 * 1024, z = 10 * 1024, I = 48, O = 1024 * 1024 * 1, U = () => !0; ++async function J(e, n = U) { ++ if (n === U) { ++ const l = await fetch(e); ++ return y(l.body); + } +- const t = await B(e); +- if (t <= V) { +- const d = await fetch(e); +- return p(d.body); ++ const t = await R(e); ++ if (t <= O) { ++ const l = await fetch(e); ++ return y(l.body); + } +- const n = await fetch(e, { ++ const r = await fetch(e, { + headers: { + // 0-0 looks weird, doesn't it? + // The Range header is inclusive so it's actually +@@ -294,175 +293,144 @@ async function ae(e, r = D) { + Range: "bytes=0-0", + "Accept-Encoding": "none" + } +- }), [a, i] = n.body.tee(), s = a.getReader(), { value: o } = await s.read(), { done: f } = await s.read(); +- if (s.releaseLock(), a.cancel(), !((o == null ? void 0 : o.length) === 1 && f)) +- return p(i); ++ }), [a, i] = r.body.tee(), s = a.getReader(), { value: o } = await s.read(), { done: c } = await s.read(); ++ if (s.releaseLock(), a.cancel(), !((o == null ? void 0 : o.length) === 1 && c)) ++ return y(i); + i.cancel(); +- const l = await K(e, t); +- return Z(l).pipeThrough(b(r)).pipeThrough(W()).pipeThrough( +- Y(l) ++ const d = await Y(e, t); ++ return V(d).pipeThrough(p(n)).pipeThrough(H(d.length)).pipeThrough( ++ Z(d) + ); + } +-function Z(e) { +- let r; ++function V(e) { ++ const n = P(e); + return new ReadableStream({ +- async start() { +- r = await G(e); +- }, + async pull(t) { +- const n = await T( +- r ++ const r = await T( ++ await n + ); +- if (!n) { ++ if (!r) { + t.close(); + return; + } +- t.enqueue(n); ++ t.enqueue(r); + } + }); + } +-async function G(e) { +- const r = I; +- let t = new Uint8Array(), n = e.length; ++async function P(e) { ++ const n = k; ++ let t = new Uint8Array(), r = e.length; + do { +- n = Math.max(0, n - r); +- const a = Math.min( +- n + r - 1, +- e.length - 1 +- ), i = await c( +- await e.streamBytes(n, a) ++ r = Math.max(0, r - n); ++ const a = Math.min(r + n, e.length), i = await u( ++ await e.streamBytes(r, a) + ); +- t = U(i, t); ++ t = m(i, t); + const s = new DataView(i.buffer); + for (let o = s.byteLength - 4; o >= 0; o--) { + if (s.getUint32(o, !0) !== g) + continue; +- const u = o + 12 + 4; +- if (t.byteLength < u + 4) ++ const f = o + 12 + 4; ++ if (t.byteLength < f + 4) + throw new Error("Central directory not found"); +- const l = s.getUint32(u, !0); +- if (l < n) { +- const d = await c( +- await e.streamBytes(l, n - 1) ++ const d = s.getUint32(f, !0); ++ if (d < r) { ++ const l = await u( ++ await e.streamBytes(d, r) + ); +- t = U( +- d, ++ t = m( ++ l, + t + ); +- } else l > n && (t = t.slice( +- l - n ++ } else d > r && (t = t.slice( ++ d - r + )); + return new Blob([t]).stream(); + } +- } while (n >= 0); ++ } while (r >= 0); + throw new Error("Central directory not found"); + } +-function W() { +- let e = 0, r = []; ++function H(e) { ++ let n = 0, t = []; ++ const r = Math.max( ++ z, ++ Math.ceil(e / I) ++ ); + return new TransformStream({ +- transform(t, n) { +- t.firstByteAt > e + z && (n.enqueue(r), r = []), e = t.lastByteAt, r.push(t); ++ transform(a, i) { ++ a.firstByteAt > n + r && (i.enqueue(t), t = []), n = a.lastByteAt, t.push(a); + }, +- flush(t) { +- t.enqueue(r); ++ flush(a) { ++ a.enqueue(t); + } + }); + } +-function Y(e) { +- let r = !1, t = 0, n; +- const a = [], i = new WritableStream({ +- write(o, f) { +- o.length && (++t, $(e, o).then((u) => { +- a.push([o, u]); +- }).catch((u) => { +- f.error(u); +- }).finally(() => { +- --t; +- })); +- }, +- abort() { +- r = !0, n.close(); +- }, +- async close() { +- r = !0; ++function Z(e) { ++ return new TransformStream({ ++ async transform(n, t) { ++ if (!n.length) ++ return; ++ const r = await $(e, n); ++ for (; ; ) { ++ const a = await E(r); ++ if (!a) ++ return; ++ n.some( ++ (i) => G(i.path, a.path) ++ ) && t.enqueue(a); ++ } + } + }); +- return { +- readable: new ReadableStream({ +- start(o) { +- n = o; +- }, +- async pull(o) { +- for (; ; ) { +- if (r && !a.length && t === 0) { +- o.close(); +- return; +- } +- if (!a.length) { +- await new Promise((m) => setTimeout(m, 50)); +- continue; +- } +- const [l, d] = a[0], y = await R(d); +- if (!y) { +- a.shift(); +- continue; +- } +- if (l.find( +- (m) => m.path === y.path +- )) { +- o.enqueue(y); +- break; +- } +- } +- } +- }), +- writable: i +- }; + } +-async function $(e, r) { +- const t = await H.acquire(); +- try { +- const n = r[r.length - 1]; +- return await e.streamBytes( +- r[0].firstByteAt, +- n.lastByteAt +- ); +- } finally { +- t(); +- } ++function G(e, n) { ++ return e.length === n.length && e.every((t, r) => t === n[r]); ++} ++async function $(e, n) { ++ const t = n[n.length - 1]; ++ return await e.streamBytes( ++ n[0].firstByteAt, ++ t.lastByteAt ++ ); + } +-async function B(e) { +- return await fetch(e, { method: "HEAD" }).then((r) => r.headers.get("Content-Length")).then((r) => { +- if (!r) ++async function R(e) { ++ return await fetch(e, { method: "HEAD" }).then((n) => n.headers.get("Content-Length")).then((n) => { ++ if (!n) + throw new Error("Content-Length header is missing"); +- const t = parseInt(r, 10); ++ const t = parseInt(n, 10); + if (isNaN(t) || t < 0) + throw new Error("Content-Length header is invalid"); + return t; + }); + } +-async function K(e, r) { +- return r === void 0 && (r = await B(e)), { +- length: r, +- streamBytes: async (t, n) => await fetch(e, { +- headers: { +- // The Range header is inclusive, so we need to subtract 1 +- Range: `bytes=${t}-${n - 1}`, +- "Accept-Encoding": "none" +- } +- }).then((a) => a.body) ++async function Y(e, n) { ++ return n === void 0 && (n = await R(e)), { ++ length: n, ++ streamBytes: async (t, r) => { ++ const a = await fetch(e, { ++ headers: { ++ // The Range header is inclusive, so we need to subtract 1 ++ Range: `bytes=${t}-${r - 1}`, ++ "Accept-Encoding": "none" ++ } ++ }); ++ if (a.status !== 206 || !a.body) ++ throw new Error( ++ `Expected a partial zip range response, received ${a.status} ${a.statusText}` ++ ); ++ return a.body; ++ } + }; + } +-function ie(e) { +- return q(e).pipeThrough(j()); ++function ee(e) { ++ return C(e).pipeThrough(W()); + } +-function j() { ++function W() { + const e = /* @__PURE__ */ new Map(); +- let r = 0; ++ let n = 0; + return new TransformStream({ +- async transform(t, n) { ++ async transform(t, r) { + const a = new Uint8Array(await t.arrayBuffer()); +- let i = await c( ++ let i = await u( + new Blob([a]).stream().pipeThrough(new CompressionStream("gzip")) + ); + const s = new DataView(i.buffer).getUint32( +@@ -470,11 +438,11 @@ function j() { + !0 + ); + i = i.slice(10, i.byteLength - 8); +- const o = new TextEncoder().encode(t.name), f = { ++ const o = new TextEncoder().encode(t.name), c = { + signature: h, + version: 2, + generalPurpose: 0, +- compressionMethod: t.type === "directory" || i.byteLength === 0 ? x : L, ++ compressionMethod: t.type === "directory" || i.byteLength === 0 ? _ : A, + lastModifiedTime: 0, + lastModifiedDate: 0, + crc: s, +@@ -483,34 +451,34 @@ function j() { + path: o, + extra: new Uint8Array(0) + }; +- e.set(r, f); +- const u = J(f); +- n.enqueue(u), r += u.byteLength, n.enqueue(i), r += i.byteLength; ++ e.set(n, c); ++ const f = K(c); ++ r.enqueue(f), n += f.byteLength, r.enqueue(i), n += i.byteLength; + }, + flush(t) { +- const n = r; ++ const r = n; + let a = 0; + for (const [ + o, +- f ++ c + ] of e.entries()) { +- const u = { +- ...f, ++ const f = { ++ ...c, + signature: w, + fileComment: new Uint8Array(0), + diskNumber: 0, + internalAttributes: 0, + externalAttributes: 0 +- }, l = Q( +- u, ++ }, d = Q( ++ f, + o + ); +- t.enqueue(l), a += l.byteLength; ++ t.enqueue(d), a += d.byteLength; + } + const i = { + signature: g, + numberOfDisks: 0, +- centralDirectoryOffset: n, ++ centralDirectoryOffset: r, + centralDirectorySize: a, + centralDirectoryStartDisk: 0, + numberCentralDirectoryRecordsOnThisDisk: e.size, +@@ -521,35 +489,35 @@ function j() { + } + }); + } +-function J(e) { +- const r = new ArrayBuffer( ++function K(e) { ++ const n = new ArrayBuffer( + 30 + e.path.byteLength + e.extra.byteLength +- ), t = new DataView(r); ++ ), t = new DataView(n); + t.setUint32(0, e.signature, !0), t.setUint16(4, e.version, !0), t.setUint16(6, e.generalPurpose, !0), t.setUint16(8, e.compressionMethod, !0), t.setUint16(10, e.lastModifiedDate, !0), t.setUint16(12, e.lastModifiedTime, !0), t.setUint32(14, e.crc, !0), t.setUint32(18, e.compressedSize, !0), t.setUint32(22, e.uncompressedSize, !0), t.setUint16(26, e.path.byteLength, !0), t.setUint16(28, e.extra.byteLength, !0); +- const n = new Uint8Array(r); +- return n.set(e.path, 30), n.set(e.extra, 30 + e.path.byteLength), n; ++ const r = new Uint8Array(n); ++ return r.set(e.path, 30), r.set(e.extra, 30 + e.path.byteLength), r; + } +-function Q(e, r) { ++function Q(e, n) { + const t = new ArrayBuffer( + 46 + e.path.byteLength + e.extra.byteLength +- ), n = new DataView(t); +- n.setUint32(0, e.signature, !0), n.setUint16(4, e.versionCreated, !0), n.setUint16(6, e.versionNeeded, !0), n.setUint16(8, e.generalPurpose, !0), n.setUint16(10, e.compressionMethod, !0), n.setUint16(12, e.lastModifiedDate, !0), n.setUint16(14, e.lastModifiedTime, !0), n.setUint32(16, e.crc, !0), n.setUint32(20, e.compressedSize, !0), n.setUint32(24, e.uncompressedSize, !0), n.setUint16(28, e.path.byteLength, !0), n.setUint16(30, e.extra.byteLength, !0), n.setUint16(32, e.fileComment.byteLength, !0), n.setUint16(34, e.diskNumber, !0), n.setUint16(36, e.internalAttributes, !0), n.setUint32(38, e.externalAttributes, !0), n.setUint32(42, r, !0); ++ ), r = new DataView(t); ++ r.setUint32(0, e.signature, !0), r.setUint16(4, e.versionCreated, !0), r.setUint16(6, e.versionNeeded, !0), r.setUint16(8, e.generalPurpose, !0), r.setUint16(10, e.compressionMethod, !0), r.setUint16(12, e.lastModifiedDate, !0), r.setUint16(14, e.lastModifiedTime, !0), r.setUint32(16, e.crc, !0), r.setUint32(20, e.compressedSize, !0), r.setUint32(24, e.uncompressedSize, !0), r.setUint16(28, e.path.byteLength, !0), r.setUint16(30, e.extra.byteLength, !0), r.setUint16(32, e.fileComment.byteLength, !0), r.setUint16(34, e.diskNumber, !0), r.setUint16(36, e.internalAttributes, !0), r.setUint32(38, e.externalAttributes, !0), r.setUint32(42, n, !0); + const a = new Uint8Array(t); + return a.set(e.path, 46), a.set(e.extra, 46 + e.path.byteLength), a; + } + function X(e) { +- const r = new ArrayBuffer(22 + e.comment.byteLength), t = new DataView(r); ++ const n = new ArrayBuffer(22 + e.comment.byteLength), t = new DataView(n); + t.setUint32(0, e.signature, !0), t.setUint16(4, e.numberOfDisks, !0), t.setUint16(6, e.centralDirectoryStartDisk, !0), t.setUint16(8, e.numberCentralDirectoryRecordsOnThisDisk, !0), t.setUint16(10, e.numberCentralDirectoryRecords, !0), t.setUint32(12, e.centralDirectorySize, !0), t.setUint32(16, e.centralDirectoryOffset, !0), t.setUint16(20, e.comment.byteLength, !0); +- const n = new Uint8Array(r); +- return n.set(e.comment, 22), n; ++ const r = new Uint8Array(n); ++ return r.set(e.comment, 22), r; + } + export { +- E as StreamedFile, +- c as collectBytes, +- re as collectFile, +- ae as decodeRemoteZip, +- p as decodeZip, +- ie as encodeZip, +- q as iteratorToStream ++ S as StreamedFile, ++ u as collectBytes, ++ j as collectFile, ++ J as decodeRemoteZip, ++ y as decodeZip, ++ ee as encodeZip, ++ C as iteratorToStream + }; + //# sourceMappingURL=index.js.map diff --git a/patches/README.md b/patches/README.md new file mode 100644 index 00000000..0a32038c --- /dev/null +++ b/patches/README.md @@ -0,0 +1,10 @@ +# Temporary Playground Overlay + +`@php-wasm+stream-compression+3.1.45.patch` is the built output of +WordPress Playground PR [#4110](https://github.com/WordPress/wordpress-playground/pull/4110) +at commit `afea9c042999c65f5b43bc049ca4b2bea9d8b361`. + +WP Codebox needs the repaired bounded Range decoder for the Cloudflare dev rig +and browser asset path before that upstream change can be merged and released. +Remove this patch and the package override after a published Playground package +contains the same change. diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 22f90744..3ddd469d 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -2,6 +2,7 @@ import { spawn } from "node:child_process" import { mkdtemp, rm } from "node:fs/promises" import { tmpdir } from "node:os" import { join } from "node:path" +import { stripVTControlCharacters } from "node:util" const port = 8792 const origin = `http://127.0.0.1:${port}` @@ -73,7 +74,7 @@ async function stopWorker() { async function waitForServer() { const deadline = Date.now() + 30_000 while (Date.now() < deadline) { - if (/Ready on http:\/\/(?:localhost|127\.0\.0\.1):8792/.test(output)) return + if (/Ready on http:\/\/(?:localhost|127\.0\.0\.1):8792/.test(stripVTControlCharacters(output))) return if (child.exitCode !== null) throw new Error(`workerd exited before starting:\n${output}`) await new Promise((resolve) => setTimeout(resolve, 100)) } diff --git a/scripts/generate-cloudflare-wordpress-runtime-corpus.ts b/scripts/generate-cloudflare-wordpress-runtime-corpus.ts index 45a8f897..88bfed4e 100644 --- a/scripts/generate-cloudflare-wordpress-runtime-corpus.ts +++ b/scripts/generate-cloudflare-wordpress-runtime-corpus.ts @@ -1,7 +1,7 @@ import { createHash } from "node:crypto" import { mkdir, writeFile } from "node:fs/promises" import { dirname, resolve } from "node:path" -import { decodeRemoteZip, encodeZip } from "@php-wasm/stream-compression" +import { decodeZip, encodeZip } from "@php-wasm/stream-compression" import { isWordPressRuntimeFile } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" import { WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, wordpressRuntimeArtifactKey, type WordPressRuntimeArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" @@ -9,24 +9,15 @@ const sourceUrl = process.env.WORDPRESS_RUNTIME_ARCHIVE_URL ?? "https://download const sourceVersion = process.env.WORDPRESS_RUNTIME_VERSION ?? "7.0.2" const output = resolve(process.env.WORDPRESS_RUNTIME_ARTIFACT_OUTPUT ?? "artifacts/cloudflare-wordpress-runtime-corpus.zip") const manifestOutput = resolve("packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json") -const decoder = new TextDecoder() -const archivePaths = new Set() -const selected: File[] = [] - -const response = await fetch(sourceUrl, { method: "HEAD" }) -if (!response.ok) throw new Error(`Unable to inspect WordPress archive: ${response.status}.`) +const response = await fetch(sourceUrl) +if (!response.ok || !response.body) throw new Error(`Unable to download WordPress archive: ${response.status}.`) const identity = response.headers.get("etag") ?? response.headers.get("last-modified") ?? undefined -const stream = await decodeRemoteZip(sourceUrl, (entry) => { - const path = decoder.decode(entry.path) - archivePaths.add(path) - return isWordPressRuntimeFile(path, archivePaths) -}) -for await (const entry of stream) { - const path = entry instanceof File ? entry.name : decoder.decode(entry.path) - if (!isWordPressRuntimeFile(path, archivePaths)) continue - const bytes = entry instanceof File ? new Uint8Array(await entry.arrayBuffer()) : entry.bytes - selected.push(new File([bytes], path, { lastModified: 0 })) -} +const files: File[] = [] +for await (const file of decodeZip(response.body)) files.push(file) +const archivePaths = new Set(files.map((file) => file.name)) +const selected = files + .filter((file) => isWordPressRuntimeFile(file.name, archivePaths)) + .map((file) => new File([file], file.name, { lastModified: 0 })) selected.sort((left, right) => left.name.localeCompare(right.name)) if (!selected.length) throw new Error("WordPress archive did not yield a runtime corpus.") const archive = new Uint8Array(await new Response(encodeZip(selected)).arrayBuffer()) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index ce7b972e..1b833baf 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -328,7 +328,9 @@ test("WordPress runtime corpus generator keeps the ZIP outside the Worker bundle const generator = await readFile(new URL("../scripts/generate-cloudflare-wordpress-runtime-corpus.ts", import.meta.url), "utf8") const artifact = await readFile(new URL("../packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts", import.meta.url), "utf8") const manifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json", import.meta.url), "utf8")) as WordPressRuntimeArtifactManifest - assert.match(generator, /decodeRemoteZip\(sourceUrl/) + assert.match(generator, /const response = await fetch\(sourceUrl\)/) + assert.match(generator, /decodeZip\(response\.body\)/) + assert.doesNotMatch(generator, /decodeRemoteZip/) assert.match(generator, /encodeZip\(selected\)/) assert.match(generator, /lastModified: 0/) assert.match(generator, /artifacts\/cloudflare-wordpress-runtime-corpus\.zip/) diff --git a/tests/cloudflare-wordpress-archive-corpus.test.ts b/tests/cloudflare-wordpress-archive-corpus.test.ts index 33c378ed..15dac11c 100644 --- a/tests/cloudflare-wordpress-archive-corpus.test.ts +++ b/tests/cloudflare-wordpress-archive-corpus.test.ts @@ -1,21 +1,18 @@ import assert from "node:assert/strict" import test from "node:test" -import { decodeRemoteZip, type CentralDirectoryEntry } from "@php-wasm/stream-compression" +import { decodeZip } from "@php-wasm/stream-compression" import { isWordPressRuntimeFile, summarizeWordPressRuntimeCorpus, type WordPressArchiveEntry } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" const WORDPRESS_ARCHIVE_URL = "https://wordpress.org/latest.zip" const MAX_RUNTIME_CORPUS_BYTES = 32 * 1024 * 1024 test("WordPress production runtime corpus stays within the Worker materialization budget", async () => { - const decoder = new TextDecoder() const entries: WordPressArchiveEntry[] = [] - const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry) => { - const directoryEntry = entry as CentralDirectoryEntry - entries.push({ path: decoder.decode(directoryEntry.path), uncompressedSize: directoryEntry.uncompressedSize, isDirectory: directoryEntry.isDirectory }) - return false - }) - for await (const _ of stream) { - // The predicate records central-directory metadata and selects no file ranges. + const response = await fetch(WORDPRESS_ARCHIVE_URL) + assert.equal(response.ok, true, `WordPress archive returned HTTP ${response.status}.`) + assert.ok(response.body, "WordPress archive response has no body.") + for await (const file of decodeZip(response.body)) { + entries.push({ path: file.name, uncompressedSize: file.size, isDirectory: file.name.endsWith("/") }) } const corpus = summarizeWordPressRuntimeCorpus(entries) @@ -39,5 +36,7 @@ test("WordPress production runtime corpus stays within the Worker materializatio legacyBytes: legacy.reduce((total, entry) => total + entry.uncompressedSize, 0), largestExcludedGroups: byGroup(excluded), })) + assert.ok(entries.some((entry) => entry.path === "wordpress/wp-includes/version.php"), "Archive is missing wp-includes/version.php.") + assert.ok(corpus.selectedFiles > 0, "WordPress archive did not yield a runtime corpus.") assert.ok(corpus.selectedBytes <= MAX_RUNTIME_CORPUS_BYTES, `Selected ${corpus.selectedBytes} bytes exceeds the ${MAX_RUNTIME_CORPUS_BYTES}-byte runtime corpus budget.`) }) From e9ab6bb539412c3a26adda134269a311b6deecae Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 13:08:12 -0400 Subject: [PATCH 78/87] Fix Cloudflare site styles and warm reads --- package-lock.json | 939 +++++++---- package.json | 1 + ...n-database-integration-canonical-seed.json | 6 +- ...wn-database-integration-canonical-seed.zip | Bin 51807 -> 55902 bytes .../assets/wordpress-runtime-artifact.json | 1496 ++++++++++++++++- packages/runtime-cloudflare/package.json | 8 +- .../src/wordpress-runtime-artifact.ts | 2 +- .../src/wordpress-runtime-corpus.ts | 7 +- packages/runtime-cloudflare/src/worker.ts | 13 +- .../build-cloudflare-canonical-mdi-seed.php | 126 +- scripts/cloudflare-local-gate.mjs | 42 +- tests/cloudflare-runtime.test.ts | 26 +- ...loudflare-wordpress-archive-corpus.test.ts | 5 +- 13 files changed, 2231 insertions(+), 440 deletions(-) diff --git a/package-lock.json b/package-lock.json index aecafd87..34319d21 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1329,7 +1329,9 @@ } }, "node_modules/@nodable/entities": { - "version": "2.1.1", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-3.0.0.tgz", + "integrity": "sha512-8L9xFeTYKhm49xfIypoe2W5wV1m/3Z58kT+7kR9A8OyFxcPduI4VmxaUMQyKYrRjUoLLSXv6EKKID5Tvj9cUVw==", "funding": [ { "type": "github", @@ -1340,6 +1342,8 @@ }, "node_modules/@octokit/app": { "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.1.0.tgz", + "integrity": "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw==", "license": "MIT", "dependencies": { "@octokit/auth-app": "^6.0.0", @@ -1356,10 +1360,14 @@ }, "node_modules/@octokit/app/node_modules/@octokit/openapi-types": { "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", "license": "MIT" }, "node_modules/@octokit/app/node_modules/@octokit/plugin-paginate-rest": { "version": "9.2.2", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.2.2.tgz", + "integrity": "sha512-u3KYkGF7GcZnSD/3UP0S7K5XUFT2FkOQdcfXZGZQPGv3lm4F2Xbf71lvjldr8c1H3nNbF+33cLEkWYbokGWqiQ==", "license": "MIT", "dependencies": { "@octokit/types": "^12.6.0" @@ -1373,6 +1381,8 @@ }, "node_modules/@octokit/app/node_modules/@octokit/types": { "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^20.0.0" @@ -1380,6 +1390,8 @@ }, "node_modules/@octokit/auth-app": { "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-6.1.4.tgz", + "integrity": "sha512-QkXkSOHZK4dA5oUqY5Dk3S+5pN2s1igPjEASNQV8/vgJgW034fQWR16u7VsNOK/EljA00eyjYF5mWNxWKWhHRQ==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-app": "^7.1.0", @@ -1398,6 +1410,8 @@ }, "node_modules/@octokit/auth-oauth-app": { "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-7.1.0.tgz", + "integrity": "sha512-w+SyJN/b0l/HEb4EOPRudo7uUOSW51jcK1jwLa+4r7PA8FPFpoxEnHBHMITqCsc/3Vo2qqFjgQfz/xUUvsSQnA==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-device": "^6.1.0", @@ -1414,6 +1428,8 @@ }, "node_modules/@octokit/auth-oauth-device": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-6.1.0.tgz", + "integrity": "sha512-FNQ7cb8kASufd6Ej4gnJ3f1QB5vJitkoV1O0/g6e6lUsQ7+VsSNRHRmFScN2tV4IgKA12frrr/cegUs0t+0/Lw==", "license": "MIT", "dependencies": { "@octokit/oauth-methods": "^4.1.0", @@ -1427,6 +1443,8 @@ }, "node_modules/@octokit/auth-oauth-user": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-4.1.0.tgz", + "integrity": "sha512-FrEp8mtFuS/BrJyjpur+4GARteUCrPeR/tZJzD8YourzoVhRics7u7we/aDcKv+yywRNwNi/P4fRi631rG/OyQ==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-device": "^6.1.0", @@ -1442,6 +1460,8 @@ }, "node_modules/@octokit/auth-token": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", "license": "MIT", "engines": { "node": ">= 18" @@ -1449,6 +1469,8 @@ }, "node_modules/@octokit/auth-unauthenticated": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-5.0.1.tgz", + "integrity": "sha512-oxeWzmBFxWd+XolxKTc4zr+h3mt+yofn4r7OfoIkR/Cj/o70eEGmPsFbueyJE2iBAGpjgTnEOKM3pnuEGVmiqg==", "license": "MIT", "dependencies": { "@octokit/request-error": "^5.0.0", @@ -1460,10 +1482,14 @@ }, "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/openapi-types": { "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", "license": "MIT" }, "node_modules/@octokit/auth-unauthenticated/node_modules/@octokit/types": { "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^20.0.0" @@ -1471,6 +1497,8 @@ }, "node_modules/@octokit/core": { "version": "5.2.2", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.2.2.tgz", + "integrity": "sha512-/g2d4sW9nUDJOMz3mabVQvOGhVa4e/BN/Um7yca9Bb2XTzPPnfTWHWQg+IsEYO7M3Vx+EXvaM/I2pJWIMun1bg==", "license": "MIT", "dependencies": { "@octokit/auth-token": "^4.0.0", @@ -1487,6 +1515,8 @@ }, "node_modules/@octokit/endpoint": { "version": "9.0.6", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.6.tgz", + "integrity": "sha512-H1fNTMA57HbkFESSt3Y9+FBICv+0jFceJFPWDePYlR/iMGrwM5ph+Dd4XRQs+8X+PUFURLQgX9ChPfhJ/1uNQw==", "license": "MIT", "dependencies": { "@octokit/types": "^13.1.0", @@ -1498,6 +1528,8 @@ }, "node_modules/@octokit/graphql": { "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.1.1.tgz", + "integrity": "sha512-3mkDltSfcDUoa176nlGoA32RGjeWjl3K7F/BwHwRMJUW/IteSa4bnSV8p2ThNkcIcZU2umkZWxwETSSCJf2Q7g==", "license": "MIT", "dependencies": { "@octokit/request": "^8.4.1", @@ -1510,6 +1542,8 @@ }, "node_modules/@octokit/oauth-app": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-6.1.0.tgz", + "integrity": "sha512-nIn/8eUJ/BKUVzxUXd5vpzl1rwaVxMyYbQkNZjHrF7Vk/yu98/YDF/N2KeWO7uZ0g3b5EyiFXFkZI8rJ+DH1/g==", "license": "MIT", "dependencies": { "@octokit/auth-oauth-app": "^7.0.0", @@ -1527,6 +1561,8 @@ }, "node_modules/@octokit/oauth-authorization-url": { "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-6.0.2.tgz", + "integrity": "sha512-CdoJukjXXxqLNK4y/VOiVzQVjibqoj/xHgInekviUJV73y/BSIcwvJ/4aNHPBPKcPWFnd4/lO9uqRV65jXhcLA==", "license": "MIT", "engines": { "node": ">= 18" @@ -1534,6 +1570,8 @@ }, "node_modules/@octokit/oauth-methods": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-4.1.0.tgz", + "integrity": "sha512-4tuKnCRecJ6CG6gr0XcEXdZtkTDbfbnD5oaHBmLERTjTMZNi2CbfEHZxPU41xXLDG4DfKf+sonu00zvKI9NSbw==", "license": "MIT", "dependencies": { "@octokit/oauth-authorization-url": "^6.0.2", @@ -1548,10 +1586,14 @@ }, "node_modules/@octokit/openapi-types": { "version": "24.2.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-24.2.0.tgz", + "integrity": "sha512-9sIH3nSUttelJSXUrmGzl7QUBFul0/mB8HRYl3fOlgHbIWG+WnYDXU3v/2zMtAvuzZ/ed00Ei6on975FhBfzrg==", "license": "MIT" }, "node_modules/@octokit/plugin-paginate-graphql": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-4.0.1.tgz", + "integrity": "sha512-R8ZQNmrIKKpHWC6V2gum4x9LG2qF1RxRjo27gjQcG3j+vf2tLsEfE7I/wRWEPzYMaenr1M+qDAtNcwZve1ce1A==", "license": "MIT", "engines": { "node": ">= 18" @@ -1588,6 +1630,8 @@ }, "node_modules/@octokit/plugin-retry": { "version": "6.1.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-6.1.0.tgz", + "integrity": "sha512-WrO3bvq4E1Xh1r2mT9w6SDFg01gFmP81nIG77+p/MqW1JeXXgL++6umim3t6x0Zj5pZm3rXAN+0HEjmmdhIRig==", "license": "MIT", "dependencies": { "@octokit/request-error": "^5.0.0", @@ -1603,6 +1647,8 @@ }, "node_modules/@octokit/plugin-throttling": { "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-8.2.0.tgz", + "integrity": "sha512-nOpWtLayKFpgqmgD0y3GqXafMFuKcA4tRPZIfu7BArd2lEZeb1988nhWhwx4aZWmjDmUfdgVf7W+Tt4AmvRmMQ==", "license": "MIT", "dependencies": { "@octokit/types": "^12.2.0", @@ -1617,10 +1663,14 @@ }, "node_modules/@octokit/plugin-throttling/node_modules/@octokit/openapi-types": { "version": "20.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-20.0.0.tgz", + "integrity": "sha512-EtqRBEjp1dL/15V7WiX5LJMIxxkdiGJnabzYx5Apx4FkQIFgAfKumXeYAqqJCj1s+BMX4cPFIFC4OLCR6stlnA==", "license": "MIT" }, "node_modules/@octokit/plugin-throttling/node_modules/@octokit/types": { "version": "12.6.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.6.0.tgz", + "integrity": "sha512-1rhSOfRa6H9w4YwK0yrf5faDaDTb+yLyBUKOCV4xtCDB5VmIPqd/v9yr9o6SAzOAlRxMiRiCic6JVM1/kunVkw==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^20.0.0" @@ -1628,6 +1678,8 @@ }, "node_modules/@octokit/request": { "version": "8.4.1", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.4.1.tgz", + "integrity": "sha512-qnB2+SY3hkCmBxZsR/MPCybNmbJe4KAlfWErXq+rBKkQJlbjdJeS85VI9r8UqeLYLvnAenU8Q1okM/0MBsAGXw==", "license": "MIT", "dependencies": { "@octokit/endpoint": "^9.0.6", @@ -1641,6 +1693,8 @@ }, "node_modules/@octokit/request-error": { "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.1.1.tgz", + "integrity": "sha512-v9iyEQJH6ZntoENr9/yXxjuezh4My67CBSu9r6Ve/05Iu5gNgnisNWOsoJHTP6k0Rr0+HQIpnH+kyammu90q/g==", "license": "MIT", "dependencies": { "@octokit/types": "^13.1.0", @@ -1653,6 +1707,8 @@ }, "node_modules/@octokit/types": { "version": "13.10.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.10.0.tgz", + "integrity": "sha512-ifLaO34EbbPj0Xgro4G5lP5asESjwHracYJvVaPIyXMuiuXLlhic3S47cBdTb+jfODkTE5YtGCLt3Ay3+J97sA==", "license": "MIT", "dependencies": { "@octokit/openapi-types": "^24.2.0" @@ -1660,6 +1716,8 @@ }, "node_modules/@octokit/webhooks": { "version": "12.3.2", + "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-12.3.2.tgz", + "integrity": "sha512-exj1MzVXoP7xnAcAB3jZ97pTvVPkQF9y6GA/dvYC47HV7vLv+24XRS6b/v/XnyikpEuvMhugEXdGtAlU086WkQ==", "license": "MIT", "dependencies": { "@octokit/request-error": "^5.0.0", @@ -1673,6 +1731,8 @@ }, "node_modules/@octokit/webhooks-methods": { "version": "4.1.0", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-4.1.0.tgz", + "integrity": "sha512-zoQyKw8h9STNPqtm28UGOYFE7O6D4Il8VJwhAtMHFt2C4L0VQT1qGKLeefUOqHNs1mNRYSadVv7x0z8U2yyeWQ==", "license": "MIT", "engines": { "node": ">= 18" @@ -1680,6 +1740,8 @@ }, "node_modules/@octokit/webhooks-types": { "version": "7.6.1", + "resolved": "https://registry.npmjs.org/@octokit/webhooks-types/-/webhooks-types-7.6.1.tgz", + "integrity": "sha512-S8u2cJzklBC0FgTwWVLaM8tMrDuDMVE4xiTK4EYXM9GntyvrdbSoxqDQa+Fh57CCNApyIpyeqPhhFEmHPfrXgw==", "license": "MIT" }, "node_modules/@php-wasm/cli-util": { @@ -1884,18 +1946,13 @@ }, "node_modules/@php-wasm/stream-compression": { "version": "3.1.45", + "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.45.tgz", + "integrity": "sha512-zLU1AGNEg1tuwm93LHrL8OcV24LyNglTg/CXV5CVZiMPpg1wfIDHJy11qqjZM8iCIJk87qYQhkKOjTnGinAVDg==", "license": "GPL-2.0-or-later", "dependencies": { "@php-wasm/util": "3.1.45" } }, - "node_modules/@php-wasm/stream-compression/node_modules/@php-wasm/util": { - "version": "3.1.45", - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, "node_modules/@php-wasm/universal": { "version": "3.1.46", "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-3.1.46.tgz", @@ -1915,6 +1972,8 @@ }, "node_modules/@php-wasm/universal/node_modules/ini": { "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -1930,10 +1989,12 @@ } }, "node_modules/@php-wasm/web-8-5": { - "version": "3.1.45", + "version": "3.1.46", + "resolved": "https://registry.npmjs.org/@php-wasm/web-8-5/-/web-8-5-3.1.46.tgz", + "integrity": "sha512-j9wwlY6aNXuKBr4JPJzqE+vkM3Onk+byJsxLqaJs4U0gYDUBLr8CuKuVr+p13apSTUosConePYTAs0214aNxYA==", "license": "GPL-2.0-or-later", "dependencies": { - "@php-wasm/universal": "3.1.45", + "@php-wasm/universal": "3.1.46", "wasm-feature-detect": "1.8.0" }, "engines": { @@ -1941,54 +2002,6 @@ "npm": ">=10.2.3" } }, - "node_modules/@php-wasm/web-8-5/node_modules/@php-wasm/logger": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "node_modules/@php-wasm/web-8-5/node_modules/@php-wasm/progress": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/logger": "3.1.45" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "node_modules/@php-wasm/web-8-5/node_modules/@php-wasm/universal": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/logger": "3.1.45", - "@php-wasm/progress": "3.1.45", - "@php-wasm/stream-compression": "3.1.45", - "@php-wasm/util": "3.1.45", - "ini": "4.1.2" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "node_modules/@php-wasm/web-8-5/node_modules/@php-wasm/util": { - "version": "3.1.45", - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "node_modules/@php-wasm/web-8-5/node_modules/ini": { - "version": "4.1.2", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "node_modules/@php-wasm/web-service-worker": { "version": "3.1.46", "resolved": "https://registry.npmjs.org/@php-wasm/web-service-worker/-/web-service-worker-3.1.46.tgz", @@ -2023,6 +2036,24 @@ "npm": ">=10.2.3" } }, + "node_modules/@php-wasm/xdebug-bridge/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@poppinss/colors": { "version": "4.1.6", "dev": true, @@ -2063,15 +2094,21 @@ "license": "CC0-1.0" }, "node_modules/@types/aws-lambda": { - "version": "8.10.161", + "version": "8.10.162", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.162.tgz", + "integrity": "sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==", "license": "MIT" }, "node_modules/@types/btoa-lite": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@types/btoa-lite/-/btoa-lite-1.0.2.tgz", + "integrity": "sha512-ZYbcE2x7yrvNFJiU7xJGrpF/ihpkM7zKgw8bha3LNJSesvTtUNxbpzaT7WXBIryf6jovisrxTBvymxMeLLj1Mg==", "license": "MIT" }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", + "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", + "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", "dependencies": { "@types/ms": "*", @@ -2080,21 +2117,29 @@ }, "node_modules/@types/ms": { "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT" }, "node_modules/@types/node": { - "version": "24.12.4", + "version": "24.13.3", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.13.3.tgz", + "integrity": "sha512-Dh8vAsV36ig5wa9OX4pXvMc9D3Veibfw2wix0CUwYODLD8nkj9UsLjASr49nPg+2eKzxhBV+v7L8pXvT4e639Q==", "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~7.18.0" } }, "node_modules/@types/node/node_modules/undici-types": { - "version": "7.16.0", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "license": "MIT" }, "node_modules/@types/pngjs": { "version": "6.0.5", + "resolved": "https://registry.npmjs.org/@types/pngjs/-/pngjs-6.0.5.tgz", + "integrity": "sha512-0k5eKfrA83JOZPppLtS2C7OUtyNAl2wKNxfyYl9Q5g9lPkgBl/9hNyAu6HuEH2J4XmIv2znEpkDd0SaZVxW6iQ==", "license": "MIT", "dependencies": { "@types/node": "*" @@ -2151,6 +2196,8 @@ }, "node_modules/@wp-playground/cli/node_modules/fs-extra": { "version": "11.1.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.1.1.tgz", + "integrity": "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==", "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -2161,6 +2208,24 @@ "node": ">=14.14" } }, + "node_modules/@wp-playground/cli/node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/@wp-playground/common": { "version": "3.1.46", "resolved": "https://registry.npmjs.org/@wp-playground/common/-/common-3.1.46.tgz", @@ -2314,6 +2379,8 @@ }, "node_modules/@wp-playground/wordpress-builds": { "version": "0.9.19", + "resolved": "https://registry.npmjs.org/@wp-playground/wordpress-builds/-/wordpress-builds-0.9.19.tgz", + "integrity": "sha512-dlqvh/MJDpE5W68vgPPKUjpip6zMuBh59L3D18ZX+pdwurC4q4ydrTww/3sTMV69BBcoH6kF93YtAtujNC5Mpw==", "license": "GPL-2.0-or-later", "engines": { "node": ">=18.18.0", @@ -2338,6 +2405,8 @@ }, "node_modules/abort-controller": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", "dependencies": { "event-target-shim": "^5.0.0" @@ -2348,6 +2417,8 @@ }, "node_modules/accepts": { "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "license": "MIT", "dependencies": { "mime-types": "~2.1.34", @@ -2359,6 +2430,8 @@ }, "node_modules/accepts/node_modules/negotiator": { "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2366,6 +2439,8 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", @@ -2377,6 +2452,8 @@ }, "node_modules/ajv": { "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", @@ -2391,6 +2468,8 @@ }, "node_modules/ansi-regex": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "engines": { "node": ">=8" @@ -2398,6 +2477,8 @@ }, "node_modules/ansi-styles": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -2409,16 +2490,34 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/anynum": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.1.tgz", + "integrity": "sha512-N6//FLET/tXYNM/F6ABca1oH6fWB+KlTt909Le28WMDBk8oaT4vY17DCrwg2MvmuqUKt3Ni4N5dGJ/EoBgcO6A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/array-flatten": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, "node_modules/async-lock": { "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", "license": "MIT" }, "node_modules/available-typed-arrays": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", "license": "MIT", "dependencies": { "possible-typed-array-names": "^1.0.0" @@ -2432,6 +2531,8 @@ }, "node_modules/base64-js": { "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { "type": "github", @@ -2450,6 +2551,8 @@ }, "node_modules/before-after-hook": { "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==", "license": "Apache-2.0" }, "node_modules/blake3-wasm": { @@ -2458,7 +2561,9 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "1.20.5", + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -2481,6 +2586,8 @@ }, "node_modules/bottleneck": { "version": "2.19.5", + "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", + "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "license": "MIT" }, "node_modules/braces": { @@ -2496,10 +2603,14 @@ }, "node_modules/btoa-lite": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/btoa-lite/-/btoa-lite-1.0.0.tgz", + "integrity": "sha512-gvW7InbIyF8AicrqWoptdW08pUxuhq8BEgowNajy9RhiE86fmGAGl+bLKo6oB8QP0CkqHLowfN0oJdKC/J6LbA==", "license": "MIT" }, "node_modules/buffer": { "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", "funding": [ { "type": "github", @@ -2522,10 +2633,14 @@ }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, "node_modules/bytes": { "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2533,6 +2648,8 @@ }, "node_modules/call-bind": { "version": "1.0.9", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", + "integrity": "sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2549,6 +2666,8 @@ }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2560,6 +2679,8 @@ }, "node_modules/call-bound": { "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -2614,10 +2735,14 @@ }, "node_modules/clean-git-ref": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/clean-git-ref/-/clean-git-ref-2.0.1.tgz", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", "license": "Apache-2.0" }, "node_modules/clean-stack": { "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", "license": "MIT", "engines": { "node": ">=6" @@ -2625,6 +2750,8 @@ }, "node_modules/cliui": { "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -2637,6 +2764,8 @@ }, "node_modules/color-convert": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -2647,10 +2776,14 @@ }, "node_modules/color-name": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/content-disposition": { "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" @@ -2661,6 +2794,8 @@ }, "node_modules/content-type": { "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2668,6 +2803,8 @@ }, "node_modules/cookie": { "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2682,6 +2819,8 @@ }, "node_modules/crc-32": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "license": "Apache-2.0", "bin": { "crc32": "bin/crc32.njs" @@ -2705,6 +2844,8 @@ }, "node_modules/debug": { "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", "dependencies": { "ms": "2.0.0" @@ -2712,10 +2853,14 @@ }, "node_modules/debug/node_modules/ms": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, "node_modules/decompress-response": { "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" @@ -2729,6 +2874,8 @@ }, "node_modules/define-data-property": { "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", "license": "MIT", "dependencies": { "es-define-property": "^1.0.0", @@ -2744,6 +2891,8 @@ }, "node_modules/depd": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2751,10 +2900,14 @@ }, "node_modules/deprecation": { "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", "license": "ISC" }, "node_modules/destroy": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "license": "MIT", "engines": { "node": ">= 0.8", @@ -2771,10 +2924,14 @@ }, "node_modules/diff3": { "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", "license": "MIT" }, "node_modules/dunder-proto": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2787,6 +2944,8 @@ }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" @@ -2794,14 +2953,20 @@ }, "node_modules/ee-first": { "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, "node_modules/emoji-regex": { "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT" }, "node_modules/encodeurl": { "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -2817,6 +2982,8 @@ }, "node_modules/es-define-property": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2824,6 +2991,8 @@ }, "node_modules/es-errors": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -2831,6 +3000,8 @@ }, "node_modules/es-object-atoms": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2880,6 +3051,8 @@ }, "node_modules/escalade": { "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "engines": { "node": ">=6" @@ -2887,10 +3060,14 @@ }, "node_modules/escape-html": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, "node_modules/etag": { "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -2898,6 +3075,8 @@ }, "node_modules/event-target-shim": { "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", "engines": { "node": ">=6" @@ -2905,6 +3084,8 @@ }, "node_modules/events": { "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", "engines": { "node": ">=0.8.x" @@ -2912,6 +3093,8 @@ }, "node_modules/express": { "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", "license": "MIT", "dependencies": { "accepts": "~1.3.8", @@ -2956,14 +3139,29 @@ }, "node_modules/express/node_modules/cookie-signature": { "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", "license": "MIT" }, + "node_modules/express/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/fast-deep-equal": { "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, "node_modules/fast-uri": { - "version": "3.1.2", + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", + "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", "funding": [ { "type": "github", @@ -2977,7 +3175,9 @@ "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { - "version": "1.2.0", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.3.0.tgz", + "integrity": "sha512-F74cZEdCvuw9P41GAC3rod4X04jjWGM1JPEv/GWSqFTWLsdyMSBMBMlm9Hk3GLBgLBbdBNY8yee0pQh2RBVESQ==", "funding": [ { "type": "github", @@ -2986,12 +3186,29 @@ ], "license": "MIT", "dependencies": { - "path-expression-matcher": "^1.5.0", - "xml-naming": "^0.1.0" + "path-expression-matcher": "^1.6.2", + "xml-naming": "^0.3.0" + } + }, + "node_modules/fast-xml-builder/node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" } }, "node_modules/fast-xml-parser": { - "version": "5.8.0", + "version": "5.10.1", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.10.1.tgz", + "integrity": "sha512-IEMIf7298kXuZSRFoGfMYrl7is8LpavODgbNz1cwIudv7KwVFnuU+UsMporfq6PD6aXSlawZlARiA3UywCTfMw==", "funding": [ { "type": "github", @@ -3000,16 +3217,32 @@ ], "license": "MIT", "dependencies": { - "@nodable/entities": "^2.1.0", + "@nodable/entities": "^3.0.0", "fast-xml-builder": "^1.2.0", - "path-expression-matcher": "^1.5.0", - "strnum": "^2.3.0", - "xml-naming": "^0.1.0" + "is-unsafe": "^2.0.0", + "path-expression-matcher": "^1.6.2", + "strnum": "^2.4.1", + "xml-naming": "^0.3.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, + "node_modules/fast-xml-parser/node_modules/xml-naming": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.3.0.tgz", + "integrity": "sha512-ghig2TBE/H11aOVgmahA3MhimvkBr6JIYknH/Dhdk10nXwdbIqBJsbfMxpvFPG8bAw77gN29aQWvKpmVoPlvPQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "dev": true, @@ -3023,6 +3256,8 @@ }, "node_modules/finalhandler": { "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -3047,6 +3282,8 @@ }, "node_modules/for-each": { "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", "license": "MIT", "dependencies": { "is-callable": "^1.2.7" @@ -3060,6 +3297,8 @@ }, "node_modules/forwarded": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3067,6 +3306,8 @@ }, "node_modules/fresh": { "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3097,6 +3338,9 @@ }, "node_modules/fsevents": { "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ @@ -3108,6 +3352,8 @@ }, "node_modules/function-bind": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3115,6 +3361,8 @@ }, "node_modules/get-caller-file": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" @@ -3122,6 +3370,8 @@ }, "node_modules/get-intrinsic": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3144,6 +3394,8 @@ }, "node_modules/get-proto": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3155,6 +3407,8 @@ }, "node_modules/gopd": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -3165,6 +3419,8 @@ }, "node_modules/graceful-fs": { "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, "node_modules/has-flag": { @@ -3177,6 +3433,8 @@ }, "node_modules/has-property-descriptors": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", "license": "MIT", "dependencies": { "es-define-property": "^1.0.0" @@ -3187,6 +3445,8 @@ }, "node_modules/has-symbols": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -3197,6 +3457,8 @@ }, "node_modules/has-tostringtag": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -3210,6 +3472,8 @@ }, "node_modules/hasown": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3220,6 +3484,8 @@ }, "node_modules/http-errors": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { "depd": "~2.0.0", @@ -3238,6 +3504,8 @@ }, "node_modules/iconv-lite": { "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -3248,6 +3516,8 @@ }, "node_modules/ieee754": { "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { "type": "github", @@ -3266,6 +3536,8 @@ }, "node_modules/ignore": { "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "license": "MIT", "engines": { "node": ">= 4" @@ -3273,6 +3545,8 @@ }, "node_modules/indent-string": { "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", "license": "MIT", "engines": { "node": ">=8" @@ -3280,6 +3554,8 @@ }, "node_modules/inherits": { "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, "node_modules/ini": { @@ -3291,6 +3567,8 @@ }, "node_modules/ipaddr.js": { "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "engines": { "node": ">= 0.10" @@ -3298,6 +3576,8 @@ }, "node_modules/is-callable": { "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -3322,6 +3602,8 @@ }, "node_modules/is-fullwidth-code-point": { "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "engines": { "node": ">=8" @@ -3337,6 +3619,8 @@ }, "node_modules/is-typed-array": { "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", "license": "MIT", "dependencies": { "which-typed-array": "^1.1.16" @@ -3348,6 +3632,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-unsafe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-unsafe/-/is-unsafe-2.0.0.tgz", + "integrity": "sha512-2LdV822R+wmI86unXA93WCFpL6g+av8ynWk0nrHyJqGop5VoocYsSLFgN8jrfalT6iGeLNM4KXuVSsULP53kEA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, "node_modules/is-wsl": { "version": "2.2.0", "dev": true, @@ -3361,6 +3657,8 @@ }, "node_modules/isarray": { "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", "license": "MIT" }, "node_modules/isexe": { @@ -3393,6 +3691,8 @@ }, "node_modules/json-schema-traverse": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, "node_modules/json-stable-stringify": { @@ -3415,10 +3715,14 @@ }, "node_modules/jsonc-parser": { "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", "license": "MIT" }, "node_modules/jsonfile": { "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -3437,6 +3741,8 @@ }, "node_modules/jsonwebtoken": { "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", "dependencies": { "jws": "^4.0.1", @@ -3457,6 +3763,8 @@ }, "node_modules/jwa": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "dependencies": { "buffer-equal-constant-time": "^1.0.1", @@ -3466,6 +3774,8 @@ }, "node_modules/jws": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "dependencies": { "jwa": "^2.0.1", @@ -3490,34 +3800,50 @@ }, "node_modules/lodash.includes": { "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT" }, "node_modules/lodash.isboolean": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "license": "MIT" }, "node_modules/lodash.isinteger": { "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT" }, "node_modules/lodash.isnumber": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "license": "MIT" }, "node_modules/lodash.isstring": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT" }, "node_modules/lru-cache": { - "version": "11.5.1", + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", "license": "BlueOak-1.0.0", "engines": { "node": "20 || >=22" @@ -3525,6 +3851,8 @@ }, "node_modules/math-intrinsics": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -3532,6 +3860,8 @@ }, "node_modules/media-typer": { "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3539,6 +3869,8 @@ }, "node_modules/merge-descriptors": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3546,6 +3878,8 @@ }, "node_modules/methods": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3565,6 +3899,8 @@ }, "node_modules/mime": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "license": "MIT", "bin": { "mime": "cli.js" @@ -3582,6 +3918,8 @@ }, "node_modules/mime-types": { "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "dependencies": { "mime-db": "1.52.0" @@ -3592,6 +3930,8 @@ }, "node_modules/mime-types/node_modules/mime-db": { "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "engines": { "node": ">= 0.6" @@ -3599,6 +3939,8 @@ }, "node_modules/mimic-response": { "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", "license": "MIT", "engines": { "node": ">=10" @@ -3628,6 +3970,8 @@ }, "node_modules/minimist": { "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3635,6 +3979,8 @@ }, "node_modules/minimisted": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", "license": "MIT", "dependencies": { "minimist": "^1.2.5" @@ -3642,10 +3988,14 @@ }, "node_modules/ms": { "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/nan": { - "version": "2.27.0", + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.28.0.tgz", + "integrity": "sha512-fTsDz99OTq2sVePhGdp4qQhggZFtKr64ZNVyVajRKtMOkJxYekplBh577PiJB12v/D3s2E5cGtOI45LWp6rnLQ==", "license": "MIT" }, "node_modules/negotiator": { @@ -3657,6 +4007,8 @@ }, "node_modules/object-inspect": { "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -3695,6 +4047,8 @@ }, "node_modules/on-finished": { "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "dependencies": { "ee-first": "1.1.1" @@ -3705,6 +4059,8 @@ }, "node_modules/once": { "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "dependencies": { "wrappy": "1" @@ -3727,10 +4083,14 @@ }, "node_modules/pako": { "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)" }, "node_modules/parseurl": { "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -3778,7 +4138,9 @@ } }, "node_modules/path-expression-matcher": { - "version": "1.5.0", + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.6.2.tgz", + "integrity": "sha512-enSlaiat05iasnzmgNxRj8reFdj3puY2QpNgP1aPIaVfT6nn9ICuPoFlKHk8EN22HcwewshO+mN2DGbkCEOtqQ==", "funding": [ { "type": "github", @@ -3800,6 +4162,8 @@ }, "node_modules/path-to-regexp": { "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", "license": "MIT" }, "node_modules/pathe": { @@ -3820,6 +4184,8 @@ }, "node_modules/pify": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", "license": "MIT", "engines": { "node": ">=6" @@ -3827,6 +4193,8 @@ }, "node_modules/pixelmatch": { "version": "7.2.0", + "resolved": "https://registry.npmjs.org/pixelmatch/-/pixelmatch-7.2.0.tgz", + "integrity": "sha512-xhcb4yHu9sM/G7foGzoLtXYcC0zHEaOXXjRKhGup0fw78Nf2Tkiapv4EQyMzrbcmQPsllAI7DbFY2UT7PlI9Pg==", "license": "ISC", "dependencies": { "pngjs": "^7.0.0" @@ -3836,10 +4204,12 @@ } }, "node_modules/playwright": { - "version": "1.60.0", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.61.1.tgz", + "integrity": "sha512-DWnY5o3YbLWK4GovuAVwpqL+1VwGNdUGrRr++8j8PtQQzvAVZUIMjKQ90fY689sEJZJBbZVw1rXaOKSTitkzPQ==", "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.60.0" + "playwright-core": "1.61.1" }, "bin": { "playwright": "cli.js" @@ -3852,7 +4222,9 @@ } }, "node_modules/playwright-core": { - "version": "1.60.0", + "version": "1.61.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.61.1.tgz", + "integrity": "sha512-h7Qlt6m4REp25qvIdvbDtVmD4LqVXfpRxhORv9L0jzETM05p4fuPJ3dKyuSXQxDSbXnmS79HAgi9589lGSpLkg==", "license": "Apache-2.0", "bin": { "playwright-core": "cli.js" @@ -3863,6 +4235,8 @@ }, "node_modules/pngjs": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-7.0.0.tgz", + "integrity": "sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==", "license": "MIT", "engines": { "node": ">=14.19.0" @@ -3870,6 +4244,8 @@ }, "node_modules/possible-typed-array-names": { "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", "license": "MIT", "engines": { "node": ">= 0.4" @@ -3877,6 +4253,8 @@ }, "node_modules/process": { "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", "engines": { "node": ">= 0.6.0" @@ -3884,6 +4262,8 @@ }, "node_modules/proxy-addr": { "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -3901,10 +4281,13 @@ } }, "node_modules/qs": { - "version": "6.15.2", + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -3922,6 +4305,8 @@ }, "node_modules/raw-body": { "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", "license": "MIT", "dependencies": { "bytes": "~3.1.2", @@ -3935,6 +4320,8 @@ }, "node_modules/readable-stream": { "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", "dependencies": { "abort-controller": "^3.0.0", @@ -3949,6 +4336,8 @@ }, "node_modules/require-directory": { "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3956,6 +4345,8 @@ }, "node_modules/require-from-string": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3963,6 +4354,8 @@ }, "node_modules/safe-buffer": { "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", @@ -3981,17 +4374,23 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, "node_modules/sax": { "version": "1.6.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz", + "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==", "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" } }, "node_modules/semver": { - "version": "7.8.1", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -4002,6 +4401,8 @@ }, "node_modules/send": { "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", "license": "MIT", "dependencies": { "debug": "2.6.9", @@ -4022,8 +4423,19 @@ "node": ">= 0.8.0" } }, + "node_modules/send/node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/serve-static": { "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", "license": "MIT", "dependencies": { "encodeurl": "~2.0.0", @@ -4037,6 +4449,8 @@ }, "node_modules/set-function-length": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", "license": "MIT", "dependencies": { "define-data-property": "^1.1.4", @@ -4052,10 +4466,14 @@ }, "node_modules/setprototypeof": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC" }, "node_modules/sha.js": { "version": "2.4.12", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.12.tgz", + "integrity": "sha512-8LzC5+bvI45BjpfXU8V5fdU2mfeKiQe1D1gIMn7XUlF3OTUrpdJpPPH4EMAnF0DsHHdSZqCdSss5qCmJKuiO3w==", "license": "(MIT AND BSD-3-Clause)", "dependencies": { "inherits": "^2.0.4", @@ -4135,12 +4553,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -4153,6 +4573,8 @@ }, "node_modules/side-channel-list": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -4167,6 +4589,8 @@ }, "node_modules/side-channel-map": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -4183,6 +4607,8 @@ }, "node_modules/side-channel-weakmap": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "dependencies": { "call-bound": "^1.0.2", @@ -4200,6 +4626,8 @@ }, "node_modules/simple-concat": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", "funding": [ { "type": "github", @@ -4218,6 +4646,8 @@ }, "node_modules/simple-get": { "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", "funding": [ { "type": "github", @@ -4249,6 +4679,8 @@ }, "node_modules/statuses": { "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4256,6 +4688,8 @@ }, "node_modules/string_decoder": { "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -4263,6 +4697,8 @@ }, "node_modules/string-width": { "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -4275,6 +4711,8 @@ }, "node_modules/strip-ansi": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -4284,14 +4722,19 @@ } }, "node_modules/strnum": { - "version": "2.3.0", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.1.tgz", + "integrity": "sha512-M9eUSMT2dCB2cTNPG7UYj6KuK7RJR2SN2+yCV/fTW3xzTCS6EaGZ5pSMgDIjB7r8zSfTGk+dvvn9rTjpVS9Mwg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], - "license": "MIT" + "license": "MIT", + "dependencies": { + "anynum": "^1.0.1" + } }, "node_modules/supports-color": { "version": "10.2.2", @@ -4306,6 +4749,8 @@ }, "node_modules/tmp": { "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", "license": "MIT", "engines": { "node": ">=14.14" @@ -4313,6 +4758,8 @@ }, "node_modules/tmp-promise": { "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", "license": "MIT", "dependencies": { "tmp": "^0.2.0" @@ -4320,6 +4767,8 @@ }, "node_modules/to-buffer": { "version": "1.2.2", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.2.2.tgz", + "integrity": "sha512-db0E3UJjcFhpDhAF4tLo03oli3pwl3dbnzXOUIlRKrp+ldk/VUxzpWYZENsw2SZiuBjHAk7DfB0VU7NKdpb6sw==", "license": "MIT", "dependencies": { "isarray": "^2.0.5", @@ -4343,6 +4792,8 @@ }, "node_modules/toidentifier": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "engines": { "node": ">=0.6" @@ -4387,6 +4838,8 @@ }, "node_modules/type-is": { "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", "license": "MIT", "dependencies": { "media-typer": "0.3.0", @@ -4398,6 +4851,8 @@ }, "node_modules/typed-array-buffer": { "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", "license": "MIT", "dependencies": { "call-bound": "^1.0.3", @@ -4441,6 +4896,8 @@ }, "node_modules/universal-github-app-jwt": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-1.2.0.tgz", + "integrity": "sha512-dncpMpnsKBk0eetwfN8D8OUHGfiDhhJ+mtsbMl+7PfW7mYjiH8LIcqRmYMtzYLgSh47HjfdBtrBwIQ/gizKR3g==", "license": "MIT", "dependencies": { "@types/jsonwebtoken": "^9.0.0", @@ -4449,10 +4906,14 @@ }, "node_modules/universal-user-agent": { "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==", "license": "ISC" }, "node_modules/universalify": { "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -4460,6 +4921,8 @@ }, "node_modules/unpipe": { "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4474,6 +4937,8 @@ }, "node_modules/utils-merge": { "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", "license": "MIT", "engines": { "node": ">= 0.4.0" @@ -4481,6 +4946,8 @@ }, "node_modules/vary": { "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -4488,6 +4955,8 @@ }, "node_modules/wasm-feature-detect": { "version": "1.8.0", + "resolved": "https://registry.npmjs.org/wasm-feature-detect/-/wasm-feature-detect-1.8.0.tgz", + "integrity": "sha512-zksaLKM2fVlnB5jQQDqKXXwYHLQUVH9es+5TOOHwGOVJOCeRBCiPjwSg+3tN2AdTCzjgli4jijCH290kXb/zWQ==", "license": "Apache-2.0" }, "node_modules/which": { @@ -4505,7 +4974,9 @@ } }, "node_modules/which-typed-array": { - "version": "1.1.21", + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", "license": "MIT", "dependencies": { "available-typed-arrays": "^1.0.7", @@ -4599,6 +5070,8 @@ }, "node_modules/wrap-ansi": { "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -4614,10 +5087,14 @@ }, "node_modules/wrappy": { "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC" }, "node_modules/ws": { - "version": "8.21.0", + "version": "8.21.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.1.tgz", + "integrity": "sha512-+0NTnW77fFN/DjQi6k/Sq/Yvk4Sgajw7urW8V+asjXnRgDs9gyGkdb7EzgfhA4goXsRIZKE28fzIXBHEzhuiWw==", "license": "MIT", "engines": { "node": ">=10.0.0" @@ -4650,6 +5127,8 @@ }, "node_modules/xml2js": { "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "license": "MIT", "dependencies": { "sax": ">=0.6.0", @@ -4661,6 +5140,8 @@ }, "node_modules/xmlbuilder": { "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "license": "MIT", "engines": { "node": ">=4.0" @@ -4668,6 +5149,8 @@ }, "node_modules/y18n": { "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", "engines": { "node": ">=10" @@ -4705,6 +5188,8 @@ }, "node_modules/yargs-parser": { "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "engines": { "node": ">=12" @@ -4745,6 +5230,8 @@ }, "node_modules/zstddec": { "version": "0.2.0", + "resolved": "https://registry.npmjs.org/zstddec/-/zstddec-0.2.0.tgz", + "integrity": "sha512-oyPnDa1X5c13+Y7mA/FDMNJrn4S8UNBe0KCqtDmor40Re7ALrPN6npFwyYVRRh+PqozZQdeg23QtbcamZnG5rA==", "license": "MIT AND BSD-3-Clause" }, "packages/cli": { @@ -4763,85 +5250,10 @@ "version": "0.12.27", "dependencies": { "@automattic/wp-codebox-core": "file:../runtime-core", - "@php-wasm/stream-compression": "^3.1.45", - "@php-wasm/universal": "3.1.45", - "@php-wasm/web-8-5": "3.1.45", - "@wp-playground/wordpress": "3.1.45" - } - }, - "packages/runtime-cloudflare/node_modules/@php-wasm/logger": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-cloudflare/node_modules/@php-wasm/progress": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/logger": "3.1.45" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-cloudflare/node_modules/@php-wasm/universal": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/logger": "3.1.45", - "@php-wasm/progress": "3.1.45", "@php-wasm/stream-compression": "3.1.45", - "@php-wasm/util": "3.1.45", - "ini": "4.1.2" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-cloudflare/node_modules/@php-wasm/util": { - "version": "3.1.45", - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-cloudflare/node_modules/@wp-playground/common": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "@php-wasm/util": "3.1.45" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-cloudflare/node_modules/@wp-playground/wordpress": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/logger": "3.1.45", - "@php-wasm/universal": "3.1.45", - "@php-wasm/util": "3.1.45", - "@wp-playground/common": "3.1.45", - "zstddec": "^0.2.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-cloudflare/node_modules/ini": { - "version": "4.1.2", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "@php-wasm/universal": "3.1.46", + "@php-wasm/web-8-5": "3.1.46", + "@wp-playground/wordpress": "3.1.46" } }, "packages/runtime-core": { @@ -4869,201 +5281,6 @@ "pngjs": "^7.0.0" } }, - "packages/runtime-playground/node_modules/@php-wasm/cli-util": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/util": "3.1.45", - "fast-xml-parser": "^5.8.0", - "jsonc-parser": "3.3.1" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/logger": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/cli-util": "3.1.45", - "@php-wasm/logger": "3.1.45", - "@php-wasm/node-5-2": "3.1.45", - "@php-wasm/node-7-4": "3.1.45", - "@php-wasm/node-8-0": "3.1.45", - "@php-wasm/node-8-1": "3.1.45", - "@php-wasm/node-8-2": "3.1.45", - "@php-wasm/node-8-3": "3.1.45", - "@php-wasm/node-8-4": "3.1.45", - "@php-wasm/node-8-5": "3.1.45", - "@php-wasm/universal": "3.1.45", - "@php-wasm/util": "3.1.45", - "fs-ext-extra-prebuilt": "2.2.7", - "wasm-feature-detect": "1.8.0", - "ws": "8.21.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node-5-2": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "wasm-feature-detect": "1.8.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node-7-4": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "wasm-feature-detect": "1.8.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node-8-0": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "wasm-feature-detect": "1.8.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node-8-1": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "wasm-feature-detect": "1.8.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node-8-2": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "wasm-feature-detect": "1.8.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node-8-3": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "wasm-feature-detect": "1.8.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node-8-4": { - "version": "3.1.45", - "resolved": "https://registry.npmjs.org/@php-wasm/node-8-4/-/node-8-4-3.1.45.tgz", - "integrity": "sha512-QElunKrFEpVa11Fdr6Afmw/gI/bCf5rxzAuXLBYfgghJP2w8sTT0M1ku1pocPEvMGeONbQgvHA06sDdKXhHEMA==", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "wasm-feature-detect": "1.8.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/node-8-5": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/universal": "3.1.45", - "wasm-feature-detect": "1.8.0" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/progress": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/logger": "3.1.45" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/universal": { - "version": "3.1.45", - "license": "GPL-2.0-or-later", - "dependencies": { - "@php-wasm/logger": "3.1.45", - "@php-wasm/progress": "3.1.45", - "@php-wasm/stream-compression": "3.1.45", - "@php-wasm/util": "3.1.45", - "ini": "4.1.2" - }, - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/@php-wasm/util": { - "version": "3.1.45", - "engines": { - "node": ">=20.10.0", - "npm": ">=10.2.3" - } - }, - "packages/runtime-playground/node_modules/fs-ext-extra-prebuilt": { - "version": "2.2.7", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "nan": "^2.24.0" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "packages/runtime-playground/node_modules/ini": { - "version": "4.1.2", - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, "packages/wordpress-plugin": { "name": "wp-codebox-wordpress-plugin", "version": "0.8.0" diff --git a/package.json b/package.json index 1ea991cd..397de097 100644 --- a/package.json +++ b/package.json @@ -470,6 +470,7 @@ }, "overrides": { "@php-wasm/stream-compression": "3.1.45", + "@php-wasm/util": "3.1.46", "ajv": "^8.20.0", "express": "^4.22.2", "qs": "^6.15.2", diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json index ebf04a46..b06c25ed 100644 --- a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json +++ b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json @@ -2,7 +2,7 @@ "schema": "wp-codebox/cloudflare-canonical-mdi-seed/v1", "markdownDatabaseIntegrationRevision": "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c", "wordpressInstallSeedSha256": "7212b89f1c01f98a23b89e9a2dd2b214306a85c10adb0355b9c7a6005b4fe822", - "archiveSha256": "ee84fad280beb3ee2965a6f430b849ea31ce2899d2533b2906da7dcf3f306a43", + "archiveSha256": "4f05439e26a63ef749de250a9b56ee3467697b1b1a166aeb90c5660ecfc9e845", "files": [ { "path": "_options/active_plugins.json", @@ -661,8 +661,8 @@ }, { "path": "page/cloudflare-wordpress-runtime.md", - "sha256": "f3d3e42ada4bdde99f505a83d6b69c5b027289f4dd147b5d0e026e441c9914b9", - "size": 4820 + "sha256": "8a85c2e15d53d1867a5a982cbe83335abf5ad9c505c8938db022f5c591e6b9d2", + "size": 8915 }, { "path": "page/privacy-policy.md", diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip index 7f55b077e8cb069a85b519903cc226a732d37ebf..0425432b97c5f5f109fbfd2fb4191d8bdd07d29e 100644 GIT binary patch delta 5166 zcmeHL-)|g89Zwn&DWNE?C<>%WnQlb|lGkUuaS|@h6~`573u#O)$Us#kbGvi5gZFl} zGqXNl;^Q2wCPIQLVj>kGRs0D>1YQtAyg`wuR8>g45MFseB0TWM1D~1Qy<0nW9mpf( zA>N(&-pu#+=kwja+&OUX=LdfJTYdPSU;N&$KJ@nO`_Q}lfxdlU*pVVj$#qZZe$2f3 zb+094$fSLeG~}7`>fX$mbj=$~dMc#>PdaulrD0e+YLP-_mCAZ5nPPgq5L-Vr?M-@; zcOt#}{t)+RExNl)crk15e!yS=y;?L_v34ZkqKE}__X3e>nU!z;V?(8}vr#v{&==L$Faxex2sgJ%n$`EXbr z?)tp%_fp?a<7m9~C$vcpo52aom|^UwG`>816Q zO4^Y$jjFk#`>Ak?edkO#<+7xSY71!tnx!d|LE&ggw;}2)D_ifa2N^9zt)cwK0u(h z3GV5XswGyr{J6Ps?Xk$XR!__By&^hMi_-^iHKX5&XsnG4qr9gn%v$Zkh|Sm@QY%vga#!rXgv zc6b>bH}A)U25)2>T^z@#NJvd$29zUhDY{tNs>>wlMLdXzVv^Gs$fe4w@$(Kv*VnFX z2bq*;Vg)X+Rj$y~r{-%h&rLDEyg7M%fuLE2=q)0K|4Ar%Ni1ln>Dm@lGJ_%!3YmusOHc3DX0Py`OvP@+c%T{;EGB{61K*?0_1T^7@ zuL6;>(Bh*lFRN4Myc9hcMHZXQN%9I^rOiO{R8JC{U24*q$h*KuAY?xUZy>f1t>Mt0 z(Qa;F%d$kAOl1&R9+0bmtF4+W?6|v*EVQ8oLfMnBa*~+e44)ph<4i?3eC)aaCfe(O zwY=NSwDZiRSt48#j-9?WA;1Pi*SbFL6B@44Bp5lVh(zIqO7MYD<_EW)*e}daZhgvJ zee=+3idjOYK~^1AiV*3XD*+C2z#U6i`b6V2qQ2EQVX-eP57T|GRw*oUDu! zukAE%IDwCX7$>m%IDu_DPM8nYUnoWb*9~$SGQy*LKFSyCg{^0cMQ3mw=9UX3{fEUP zO23`{Z$q+^mi-U&@yg$m{yXVHcyHjVN{{IOPVE0+$M+#@CI(;nm}~jw%HSkj63SMZ zC8GEvQp-E>fbx|^0d|?DA=T8;UvO(QM_X*ws<}Yrifvr3G+$|6e07SvTvm^HZsW6$ z=LDT60Fx<)CV%?w^FYlP{-1NM!u|(x?qzdzP#>0~k4fTURqJR*Wljvx{0a75x~?!$?@s^AbM zv<+{~p`ka9Ef)9>+_M86^kT^>i}6}g6!XsJIWuvSHeT!}6bMqHDu4!~;)q|M+zMJD z5>`R066h_ME-zMSgqSsx>MCtKeC0gU?LSq}B(i)uIY=Rp?yH=V12`Zz?jEE_|ak@5ISL&tKc2~yW?_lmU9R@Am?5>Wd62xbod|8bT>C6 za7X|<=;w1jNKYk@jSzRL_ag++9g1GS+P zTf^58uHXgo7_ByGq7k%$RvBNX)s4PZm8hRF+9noFlnSwP-+OmvZoPST+d>5vUP64Br+)ioDbGBCmX24`Qa5uwYHY+?1=Qz&`KLc){m`%U6 zF!V5to-#06xI8-1=?a~fIvP12a8(4C8D=w$@1G}dp|lAX%6;uAWjiHx7zmvp#qxJnKr)6zH7;v%Pa1&c(>aO}CZ ze#r2Gv7%us9)&E^R&Bxx4_P`qb*Nu4rS{{FC*5lf^jH?m=J#ck4DwLI4Tb(jO6w|v ztuTFjQW!j3**`=ZQ{0ihHY#uo?Txk(ix&wz0KSa(5sQ!CD`)=$6111qW6YPnaF`ca zfynH_?wg5LUdT&j@Xwts;~;z^LF94)*}&riKs*g8ZZX#l9Y}0X>;!4gkQ-tvr}C_*qy7q0Pc*0<=Bj&!$08vqQWwGoYEY8eCh)wv=*u~6TwHwk gHr3Fuc>9yx{O$(+{B)%G$2^XF-q!l@SrdK!0hNjowg3PC diff --git a/packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json b/packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json index a68d037e..7d6bcc83 100644 --- a/packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json +++ b/packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json @@ -1,9 +1,9 @@ { "schema": "wp-codebox/wordpress-runtime-artifact/v1", - "key": "runtime/wordpress/6fb95a94d5c06de7ddf35aba94cfc663d281ea500488e5c1206ec374f67a2173.zip", + "key": "runtime/wordpress/279477d490700affba70acc43055045878083358096249314fae4e163dc37502.zip", "archive": { - "sha256": "6fb95a94d5c06de7ddf35aba94cfc663d281ea500488e5c1206ec374f67a2173", - "size": 5658241 + "sha256": "279477d490700affba70acc43055045878083358096249314fae4e163dc37502", + "size": 5798703 }, "source": { "url": "https://downloads.wordpress.org/release/wordpress-7.0.2.zip", @@ -3076,11 +3076,21 @@ "size": 2175, "sha256": "c1e16410d9dc79b5d10e03e08d30d514bf287fd351b0a55e154f17d94388fcae" }, + { + "path": "wordpress/wp-includes/blocks/accordion-heading/style-rtl.min.css", + "size": 753, + "sha256": "8dc20377e42412d142cc15aba7910c060ee2cd952ff7a08a7ed5fd31ec2290cc" + }, { "path": "wordpress/wp-includes/blocks/accordion-heading/style.css", "size": 883, "sha256": "a7a23769f84f139b5f2b84ee716ff217760a1d816915ec94ae30c6a2813a70dc" }, + { + "path": "wordpress/wp-includes/blocks/accordion-heading/style.min.css", + "size": 753, + "sha256": "8dc20377e42412d142cc15aba7910c060ee2cd952ff7a08a7ed5fd31ec2290cc" + }, { "path": "wordpress/wp-includes/blocks/accordion-item.php", "size": 3041, @@ -3091,21 +3101,41 @@ "size": 1421, "sha256": "4343382a05fa00cad01d94bf94af936062edbf126634eba3c6a6e1ff36aa0847" }, + { + "path": "wordpress/wp-includes/blocks/accordion-item/style-rtl.min.css", + "size": 373, + "sha256": "bf1b9d24402a73b2f5f7cedf075d6483c0eb954d301866fe8269de0699fa560b" + }, { "path": "wordpress/wp-includes/blocks/accordion-item/style.css", "size": 518, "sha256": "9ef016b8b75a286b71f67c00fc5d643442b92b1bc31d86f57ca9cec710030c80" }, + { + "path": "wordpress/wp-includes/blocks/accordion-item/style.min.css", + "size": 372, + "sha256": "7cd741776a642088c53b29ac0fe810b2a2c31316fe6ada5a35a8e9c8b50f6ce9" + }, { "path": "wordpress/wp-includes/blocks/accordion-panel/block.json", "size": 1557, "sha256": "360df412acb5b595f359c29455b485c82ae3f29fb55ed4276189f5b487da0d24" }, + { + "path": "wordpress/wp-includes/blocks/accordion-panel/style-rtl.min.css", + "size": 111, + "sha256": "4f932c154716a0972ac0d90e720c028b7a6a315df8e441bf25bb959d07f0ef07" + }, { "path": "wordpress/wp-includes/blocks/accordion-panel/style.css", "size": 123, "sha256": "f037519fb0b84e529070cec8678c6be0b058ed429d2a746d0c3e2cec4be01501" }, + { + "path": "wordpress/wp-includes/blocks/accordion-panel/style.min.css", + "size": 111, + "sha256": "4f932c154716a0972ac0d90e720c028b7a6a315df8e441bf25bb959d07f0ef07" + }, { "path": "wordpress/wp-includes/blocks/accordion.php", "size": 1147, @@ -3116,11 +3146,21 @@ "size": 1979, "sha256": "dc6a77ce09c82d9a17cdbd739397484a98da57ef3ed4df3467486e42ea1fd9c7" }, + { + "path": "wordpress/wp-includes/blocks/accordion/style-rtl.min.css", + "size": 42, + "sha256": "40f0c19e711a931e789c74a99a2dba22b6f38a9aff8391501496b892f198b529" + }, { "path": "wordpress/wp-includes/blocks/accordion/style.css", "size": 49, "sha256": "7282db043a7881206ee3ff55e440181b39548156287f7bd17d686fce4296b92f" }, + { + "path": "wordpress/wp-includes/blocks/accordion/style.min.css", + "size": 42, + "sha256": "40f0c19e711a931e789c74a99a2dba22b6f38a9aff8391501496b892f198b529" + }, { "path": "wordpress/wp-includes/blocks/archives.php", "size": 4385, @@ -3131,21 +3171,61 @@ "size": 1411, "sha256": "2ef40a464f58a1b8c2db3bd26d2103dfc8aec9bec4d0a66b1e79f105fda1e957" }, + { + "path": "wordpress/wp-includes/blocks/archives/style-rtl.min.css", + "size": 89, + "sha256": "0ea955dcec5c9881fd17e07af6e525cf1eb1c5ec47569fa3f1b76d805e4503ba" + }, { "path": "wordpress/wp-includes/blocks/archives/style.css", "size": 105, "sha256": "1e10558c6ca95a24bf9f3ee9c544d45a4953ddb4b121d1a10d5a863b0d3062e3" }, + { + "path": "wordpress/wp-includes/blocks/archives/style.min.css", + "size": 89, + "sha256": "0ea955dcec5c9881fd17e07af6e525cf1eb1c5ec47569fa3f1b76d805e4503ba" + }, { "path": "wordpress/wp-includes/blocks/audio/block.json", "size": 1327, "sha256": "c7dea3ffa058e20e43c5480fb98823c94f12b527689b89db4eadec73a596d60c" }, + { + "path": "wordpress/wp-includes/blocks/audio/editor-rtl.min.css", + "size": 215, + "sha256": "538cbfca5be48be6d1b8b816cb8e2933dd6c4e71a457b7452e2f88a7c7249d69" + }, + { + "path": "wordpress/wp-includes/blocks/audio/editor.min.css", + "size": 213, + "sha256": "b985885fc95df16224a71e6e843a808c4576061d173aa9a1d2c3b60f42a0b88b" + }, + { + "path": "wordpress/wp-includes/blocks/audio/style-rtl.min.css", + "size": 156, + "sha256": "0cc2c5ae130d3214b5501fa21931378f73d42376a7afc9d6418335c2e1cca464" + }, { "path": "wordpress/wp-includes/blocks/audio/style.css", "size": 1927, "sha256": "7da2a5ab63b861de257b720f77b4609ddbacfd39a03f4fb6e6544867b1fe1f01" }, + { + "path": "wordpress/wp-includes/blocks/audio/style.min.css", + "size": 156, + "sha256": "0cc2c5ae130d3214b5501fa21931378f73d42376a7afc9d6418335c2e1cca464" + }, + { + "path": "wordpress/wp-includes/blocks/audio/theme-rtl.min.css", + "size": 176, + "sha256": "bdd319b8cf3b5c738590a593e97eae647011b9eb6e82d46ba7138dd3e1d8d218" + }, + { + "path": "wordpress/wp-includes/blocks/audio/theme.min.css", + "size": 176, + "sha256": "bdd319b8cf3b5c738590a593e97eae647011b9eb6e82d46ba7138dd3e1d8d218" + }, { "path": "wordpress/wp-includes/blocks/avatar.php", "size": 5634, @@ -3156,11 +3236,31 @@ "size": 1287, "sha256": "a33fa4d59ed36493ef740032868db73f4d5de50733f49a84c1bfae41f18ad86a" }, + { + "path": "wordpress/wp-includes/blocks/avatar/editor-rtl.min.css", + "size": 119, + "sha256": "aa68aa13f74991eccd28d1684fc6201b26d44afb7551ecd3640a14de1596d5bb" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/editor.min.css", + "size": 119, + "sha256": "aa68aa13f74991eccd28d1684fc6201b26d44afb7551ecd3640a14de1596d5bb" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/style-rtl.min.css", + "size": 138, + "sha256": "3b4015272fe6385c2de8decabf50fcabdbde41a7dc471827b09e01c0df954270" + }, { "path": "wordpress/wp-includes/blocks/avatar/style.css", "size": 170, "sha256": "55788a5518dee794a80ca8f0af7ad7de0e45973312536823905b6425a1e2f64f" }, + { + "path": "wordpress/wp-includes/blocks/avatar/style.min.css", + "size": 138, + "sha256": "3b4015272fe6385c2de8decabf50fcabdbde41a7dc471827b09e01c0df954270" + }, { "path": "wordpress/wp-includes/blocks/block.php", "size": 4186, @@ -3186,11 +3286,21 @@ "size": 1634, "sha256": "376fd7ec4814fa13c6dc63f8befeab3b66e5a766577d825daa97b682feddc9b1" }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs/style-rtl.min.css", + "size": 344, + "sha256": "5b9cd66b0266d65e954ed26edea4c787b2450b7183673fd68d7fdc2d262ff4d3" + }, { "path": "wordpress/wp-includes/blocks/breadcrumbs/style.css", "size": 453, "sha256": "641a6aab4c35378f103949a709e62295adca302aa911f953ff84525a2f071345" }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs/style.min.css", + "size": 344, + "sha256": "5b9cd66b0266d65e954ed26edea4c787b2450b7183673fd68d7fdc2d262ff4d3" + }, { "path": "wordpress/wp-includes/blocks/button.php", "size": 1752, @@ -3201,21 +3311,61 @@ "size": 3128, "sha256": "e865e767ce9f22c92648cb42e8c4de518cd30c8afa0a05e1ac3be5a8bb31577d" }, + { + "path": "wordpress/wp-includes/blocks/button/editor-rtl.min.css", + "size": 444, + "sha256": "16052e63bd543d16865efd3d68e904f943bbad650ebdb1b41ed4084fe827edef" + }, + { + "path": "wordpress/wp-includes/blocks/button/editor.min.css", + "size": 444, + "sha256": "16052e63bd543d16865efd3d68e904f943bbad650ebdb1b41ed4084fe827edef" + }, + { + "path": "wordpress/wp-includes/blocks/button/style-rtl.min.css", + "size": 2242, + "sha256": "851177307d166983e920351863ef364b2a016becb0dc103dac18da1da5d31565" + }, { "path": "wordpress/wp-includes/blocks/button/style.css", "size": 2536, "sha256": "c7e252900f3f4fbeb86fe61a3dbc353a4ab822d8d12f778e8abbaa9e4b77f4b7" }, + { + "path": "wordpress/wp-includes/blocks/button/style.min.css", + "size": 2242, + "sha256": "851177307d166983e920351863ef364b2a016becb0dc103dac18da1da5d31565" + }, { "path": "wordpress/wp-includes/blocks/buttons/block.json", "size": 1633, "sha256": "c8d119c01a6e222271686923813ac537e6a57e8f25c4d884e39b70e0d8bf0c2a" }, + { + "path": "wordpress/wp-includes/blocks/buttons/editor-rtl.min.css", + "size": 1006, + "sha256": "9d81e12f90e13ab3b0b21e4525c2f2c826ac62b4ec38f2612309a4336ec66aaf" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/editor.min.css", + "size": 1006, + "sha256": "9d81e12f90e13ab3b0b21e4525c2f2c826ac62b4ec38f2612309a4336ec66aaf" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/style-rtl.min.css", + "size": 1395, + "sha256": "4bd8c4991f6811a65be67cffb8b0eb0240bc95c19d8ac0fdd11b9c5f02ade768" + }, { "path": "wordpress/wp-includes/blocks/buttons/style.css", "size": 1734, "sha256": "4125178351ab6d5d5fa8c8c2aefb8852542cf9b37684aa8c0a11d039d58ab7d2" }, + { + "path": "wordpress/wp-includes/blocks/buttons/style.min.css", + "size": 1395, + "sha256": "4bd8c4991f6811a65be67cffb8b0eb0240bc95c19d8ac0fdd11b9c5f02ade768" + }, { "path": "wordpress/wp-includes/blocks/calendar.php", "size": 6075, @@ -3226,11 +3376,21 @@ "size": 1063, "sha256": "714a902598f6e55269e6aef94f572a1f9d445c2e172a398b259415ae4526aea6" }, + { + "path": "wordpress/wp-includes/blocks/calendar/style-rtl.min.css", + "size": 661, + "sha256": "54bd056335a05d96008f31c456a3d97bac8d1fa66fc94045c5f965e606ce2460" + }, { "path": "wordpress/wp-includes/blocks/calendar/style.css", "size": 770, "sha256": "b6b70363351519ccf5b7a50f0f2114d23118930a1d0e7d75bc58327cb0bfa33a" }, + { + "path": "wordpress/wp-includes/blocks/calendar/style.min.css", + "size": 661, + "sha256": "54bd056335a05d96008f31c456a3d97bac8d1fa66fc94045c5f965e606ce2460" + }, { "path": "wordpress/wp-includes/blocks/categories.php", "size": 4938, @@ -3241,21 +3401,71 @@ "size": 1966, "sha256": "57cb1effb888c776747488f25061097fefe4081677fc79b9dcc077ea5e2cac92" }, + { + "path": "wordpress/wp-includes/blocks/categories/editor-rtl.min.css", + "size": 198, + "sha256": "1caef102647e07a852918ae297e5d13f8ffe8655d77fb719b8e7b7cbe5c745c7" + }, + { + "path": "wordpress/wp-includes/blocks/categories/editor.min.css", + "size": 196, + "sha256": "f7bc4be9d5e967607fccc8d7cf7156235ebb1a0ae7eb1bb71eb7ac68c9282602" + }, + { + "path": "wordpress/wp-includes/blocks/categories/style-rtl.min.css", + "size": 318, + "sha256": "e2411202ed63373e74598d29441eafefdc96956814ac8bf926983a0959a05920" + }, { "path": "wordpress/wp-includes/blocks/categories/style.css", "size": 479, "sha256": "89c1882329fdd85ed5bc4b7d918fa71603e0c68b9790ab6904310405cb5fda34" }, + { + "path": "wordpress/wp-includes/blocks/categories/style.min.css", + "size": 318, + "sha256": "e2411202ed63373e74598d29441eafefdc96956814ac8bf926983a0959a05920" + }, { "path": "wordpress/wp-includes/blocks/code/block.json", "size": 1426, "sha256": "b09486298523a4d2299c92b266cf4b5592e54b9de1024c60477e47ebb5fa011d" }, + { + "path": "wordpress/wp-includes/blocks/code/editor-rtl.min.css", + "size": 36, + "sha256": "c6d993d8b4c142cd67b56e173650e6f856f8c578aa0c3e175715bc20a8b60418" + }, + { + "path": "wordpress/wp-includes/blocks/code/editor.min.css", + "size": 36, + "sha256": "c6d993d8b4c142cd67b56e173650e6f856f8c578aa0c3e175715bc20a8b60418" + }, + { + "path": "wordpress/wp-includes/blocks/code/style-rtl.min.css", + "size": 170, + "sha256": "48b8ce46aee1f7c02cf9e8382207efc1bd425a5f39ff0624d8cb7cac54152bc9" + }, { "path": "wordpress/wp-includes/blocks/code/style.css", "size": 251, "sha256": "956a519062fac48120fbbc2aa64481f28ea9129d32fc094761ebcf182cf20a10" }, + { + "path": "wordpress/wp-includes/blocks/code/style.min.css", + "size": 170, + "sha256": "48b8ce46aee1f7c02cf9e8382207efc1bd425a5f39ff0624d8cb7cac54152bc9" + }, + { + "path": "wordpress/wp-includes/blocks/code/theme-rtl.min.css", + "size": 116, + "sha256": "d4b2806986884389f20aa1fb54ab75d306051c9d0ad4b15ff4ab080972062765" + }, + { + "path": "wordpress/wp-includes/blocks/code/theme.min.css", + "size": 116, + "sha256": "d4b2806986884389f20aa1fb54ab75d306051c9d0ad4b15ff4ab080972062765" + }, { "path": "wordpress/wp-includes/blocks/column/block.json", "size": 1616, @@ -3266,11 +3476,31 @@ "size": 1977, "sha256": "7d93c8f8643ce66aac6a4a4f883e98bbf48a205a06e6d39aa3ccd7704e9ce608" }, + { + "path": "wordpress/wp-includes/blocks/columns/editor-rtl.min.css", + "size": 139, + "sha256": "d234973f2af012144ddd1374a534e71d8789c130b6c4db26ed24b4fdfe0697b3" + }, + { + "path": "wordpress/wp-includes/blocks/columns/editor.min.css", + "size": 139, + "sha256": "d234973f2af012144ddd1374a534e71d8789c130b6c4db26ed24b4fdfe0697b3" + }, + { + "path": "wordpress/wp-includes/blocks/columns/style-rtl.min.css", + "size": 1584, + "sha256": "a86fcda3eac552f9b75e4fdf66424076b51dc4822ae9a7c124cbb515f7c30d96" + }, { "path": "wordpress/wp-includes/blocks/columns/style.css", "size": 3649, "sha256": "a8aa7eeeea6aa93b4e93dc27dbd834692851fa5487d6e48d60ca567bf316f4e4" }, + { + "path": "wordpress/wp-includes/blocks/columns/style.min.css", + "size": 1584, + "sha256": "a86fcda3eac552f9b75e4fdf66424076b51dc4822ae9a7c124cbb515f7c30d96" + }, { "path": "wordpress/wp-includes/blocks/comment-author-name.php", "size": 2133, @@ -3281,11 +3511,21 @@ "size": 1457, "sha256": "dd64754200ff448a1438a444e96204349321627dd9ce48288b7d01c63ad99b2b" }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name/style-rtl.min.css", + "size": 52, + "sha256": "b2e8ec3d074effca499a39b187003a0efa23c8d4f624791594ff6e7114febc98" + }, { "path": "wordpress/wp-includes/blocks/comment-author-name/style.css", "size": 59, "sha256": "3f21ab638df66e871cd971f3c79519bb44a106d14d12d74cbb7a9bb3099e0aeb" }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name/style.min.css", + "size": 52, + "sha256": "b2e8ec3d074effca499a39b187003a0efa23c8d4f624791594ff6e7114febc98" + }, { "path": "wordpress/wp-includes/blocks/comment-content.php", "size": 2459, @@ -3296,11 +3536,21 @@ "size": 1284, "sha256": "76a358bf31e1ece76adc9a3495acbcc653340f53206310c5e499026c11fb979e" }, + { + "path": "wordpress/wp-includes/blocks/comment-content/style-rtl.min.css", + "size": 124, + "sha256": "c655d691efff7740069f2c6d31c5b48f432cc92217b9e0018f46ae16b4596a6b" + }, { "path": "wordpress/wp-includes/blocks/comment-content/style.css", "size": 149, "sha256": "59c3178a1a5263525975714af4886e8318d955bf4612a611ea75a35d11dde473" }, + { + "path": "wordpress/wp-includes/blocks/comment-content/style.min.css", + "size": 124, + "sha256": "c655d691efff7740069f2c6d31c5b48f432cc92217b9e0018f46ae16b4596a6b" + }, { "path": "wordpress/wp-includes/blocks/comment-date.php", "size": 1863, @@ -3311,11 +3561,21 @@ "size": 1412, "sha256": "421cab4d6d6bf7c0925a2b192afd25b8b4aaab472e59354c78910eac98f78ef9" }, + { + "path": "wordpress/wp-includes/blocks/comment-date/style-rtl.min.css", + "size": 45, + "sha256": "2324457499a12755a7413350e8b87373b9f12a33dd5c8ac612d13f560d9b8dfe" + }, { "path": "wordpress/wp-includes/blocks/comment-date/style.css", "size": 52, "sha256": "bb31f3c69fa418fe892f0039d90fae36e6046d0346cca3908ac0c13ae6882b2d" }, + { + "path": "wordpress/wp-includes/blocks/comment-date/style.min.css", + "size": 45, + "sha256": "2324457499a12755a7413350e8b87373b9f12a33dd5c8ac612d13f560d9b8dfe" + }, { "path": "wordpress/wp-includes/blocks/comment-edit-link.php", "size": 1713, @@ -3326,11 +3586,21 @@ "size": 1442, "sha256": "6149b9801b1ee80e8e6043457fa0bb2e95cdf6ba35e3342a7e089115b68c1e58" }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link/style-rtl.min.css", + "size": 50, + "sha256": "e41b4d737ec37677937903e9047d4adcac0bde3908369f51ad2414dbba906fa3" + }, { "path": "wordpress/wp-includes/blocks/comment-edit-link/style.css", "size": 57, "sha256": "fa5b8b36e43feb48da7e58ed1d9ccb447bc6d0934c5f95ec6e6b90b4e61cbe98" }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link/style.min.css", + "size": 50, + "sha256": "e41b4d737ec37677937903e9047d4adcac0bde3908369f51ad2414dbba906fa3" + }, { "path": "wordpress/wp-includes/blocks/comment-reply-link.php", "size": 2075, @@ -3341,11 +3611,21 @@ "size": 1211, "sha256": "50ae3ce36e011f4f775c36ea570ecdf2088bf80dce0d7c9c172deb775693286f" }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link/style-rtl.min.css", + "size": 51, + "sha256": "df5545d0b2d61333a2a3fc8c546092320d817d75762e758d8f85782b24272949" + }, { "path": "wordpress/wp-includes/blocks/comment-reply-link/style.css", "size": 58, "sha256": "a4b0205f164113f04bcbbd283f738b2f3a7613dd8543b6fed9c2c51b359ae4b4" }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link/style.min.css", + "size": 51, + "sha256": "df5545d0b2d61333a2a3fc8c546092320d817d75762e758d8f85782b24272949" + }, { "path": "wordpress/wp-includes/blocks/comment-template.php", "size": 4494, @@ -3356,11 +3636,21 @@ "size": 1201, "sha256": "860be0857f206676cf4a261b53b4aa8c81bacb1846bb32034748573aa7f781a1" }, + { + "path": "wordpress/wp-includes/blocks/comment-template/style-rtl.min.css", + "size": 433, + "sha256": "9bff8f9155297e0c4ad4733d0b126b0bbf035a48b7023e7d5f323a5e53dd768d" + }, { "path": "wordpress/wp-includes/blocks/comment-template/style.css", "size": 515, "sha256": "94087a95e0e1d116a9927da11fef35c99fa0a07292071d8c35a73db8228e7377" }, + { + "path": "wordpress/wp-includes/blocks/comment-template/style.min.css", + "size": 432, + "sha256": "b984e8d29d91f445f104545fbc69e3a16a7c5730582c0d898140f5558bf8781e" + }, { "path": "wordpress/wp-includes/blocks/comments-pagination-next.php", "size": 1924, @@ -3381,6 +3671,16 @@ "size": 1089, "sha256": "2f8fb03b8eabcc1627e1199c597cc98e3a29a7d940e227af75658bfcc55851b4" }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-numbers/editor-rtl.min.css", + "size": 212, + "sha256": "ed869b3a186fc914b0154b297f42bee71ea1ca090aef06c9989713f9d4d6e5f6" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-numbers/editor.min.css", + "size": 213, + "sha256": "203b9f9cf3d3c73aee8dab335fff52f23be669397a097fafe6ca3daacbf4e3f6" + }, { "path": "wordpress/wp-includes/blocks/comments-pagination-previous.php", "size": 1790, @@ -3401,11 +3701,31 @@ "size": 1575, "sha256": "bab20f184d9770cd46cb6827ccb409f17d8acaabef2bbdc88a7d74b3680f206e" }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/editor-rtl.min.css", + "size": 378, + "sha256": "5a1bd458cdff1e8badf095262c5b0d2ed1330b4d7fc4437c72c41832a85f36e4" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/editor.min.css", + "size": 378, + "sha256": "5a1bd458cdff1e8badf095262c5b0d2ed1330b4d7fc4437c72c41832a85f36e4" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/style-rtl.min.css", + "size": 740, + "sha256": "a65667e2b164bf124e74981eacb1bcda18f9ce7200d01e0805499239c7ccd83f" + }, { "path": "wordpress/wp-includes/blocks/comments-pagination/style.css", "size": 841, "sha256": "99240f8ea0cdf3ab264dbf0c23970a40cb1b4c96ad46ec3febe15a2e0b9e39a8" }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/style.min.css", + "size": 738, + "sha256": "4f7b0e412d37040de997fff280435436600131585db5231841d66ef4c56f83f7" + }, { "path": "wordpress/wp-includes/blocks/comments-title.php", "size": 2787, @@ -3416,6 +3736,16 @@ "size": 1490, "sha256": "e847288c0ffd7294e5e59f93ebe014a703b6edc08337f0b3b60e84564f31e6e0" }, + { + "path": "wordpress/wp-includes/blocks/comments-title/editor-rtl.min.css", + "size": 56, + "sha256": "f89e6620299843dabe1c8a4b47c01aee439e03b0c7f4c5a5204c99b90770e4b5" + }, + { + "path": "wordpress/wp-includes/blocks/comments-title/editor.min.css", + "size": 56, + "sha256": "f89e6620299843dabe1c8a4b47c01aee439e03b0c7f4c5a5204c99b90770e4b5" + }, { "path": "wordpress/wp-includes/blocks/comments.php", "size": 6784, @@ -3426,11 +3756,31 @@ "size": 1421, "sha256": "f169878850b95af5834d2522620f21beeb13c06124d45517e8cef5251a0aea36" }, + { + "path": "wordpress/wp-includes/blocks/comments/editor-rtl.min.css", + "size": 4460, + "sha256": "eda911c8c9703820a47a9fcc19a1ff82bbc71008df5be9a9260c2cefdf59a4aa" + }, + { + "path": "wordpress/wp-includes/blocks/comments/editor.min.css", + "size": 4458, + "sha256": "5eafd2c141a9d40f9e44feca7ef2861befa1332be01abeaae9008c48d3738f71" + }, + { + "path": "wordpress/wp-includes/blocks/comments/style-rtl.min.css", + "size": 2369, + "sha256": "72b4aa452eed908d0c42249ea52dcda2800aac8eecd3488a311b017b68c4e992" + }, { "path": "wordpress/wp-includes/blocks/comments/style.css", "size": 2882, "sha256": "1dae5a6ac721ee55340aa269746185ca245984395026ec47c5956ef428474656" }, + { + "path": "wordpress/wp-includes/blocks/comments/style.min.css", + "size": 2367, + "sha256": "aeeb7827bb05ec150dc08b2350914dc1428805a70f9e1d46fafb5115bccfe235" + }, { "path": "wordpress/wp-includes/blocks/cover.php", "size": 7422, @@ -3441,11 +3791,31 @@ "size": 3039, "sha256": "b72e63a53251f35cc9d6bb048d2824109cd4e4a1f6212ba03b81291b1f3c6e2c" }, + { + "path": "wordpress/wp-includes/blocks/cover/editor-rtl.min.css", + "size": 1728, + "sha256": "212b5744001035d1b9eb3ea70cf26b94711c5289a36bcbbbc13eae1fba44ab54" + }, + { + "path": "wordpress/wp-includes/blocks/cover/editor.min.css", + "size": 1726, + "sha256": "6a6088f3608085a59599b9adc173bf1b7f20ccb72eba1e3337cac7a4b8ecae35" + }, + { + "path": "wordpress/wp-includes/blocks/cover/style-rtl.min.css", + "size": 20290, + "sha256": "f4b3506d47167e8b6adb9166df612ee070218be75f81ca983695c1c821ae9005" + }, { "path": "wordpress/wp-includes/blocks/cover/style.css", "size": 22791, "sha256": "83076b1c83d5a153b7e78f0b09de8a0814870c0422fee85ee50b118637bde467" }, + { + "path": "wordpress/wp-includes/blocks/cover/style.min.css", + "size": 20262, + "sha256": "bd56a479237f7212eae3a46a3f0e9aeba7dff44dab300249a18fa24587fb63d0" + }, { "path": "wordpress/wp-includes/blocks/details.php", "size": 1498, @@ -3456,21 +3826,71 @@ "size": 1711, "sha256": "24232f6ff091aec0243e2da999ed1ee1ad8ad8d3fbb3d091cb0141e5cb80816f" }, + { + "path": "wordpress/wp-includes/blocks/details/editor-rtl.min.css", + "size": 45, + "sha256": "115c18071c4c04d3b8189118324df7f23fe7ca72411706aa39a3943495348f34" + }, + { + "path": "wordpress/wp-includes/blocks/details/editor.min.css", + "size": 45, + "sha256": "115c18071c4c04d3b8189118324df7f23fe7ca72411706aa39a3943495348f34" + }, + { + "path": "wordpress/wp-includes/blocks/details/style-rtl.min.css", + "size": 81, + "sha256": "b725e3774697bba8856599dd041eac8332c1caf46b5496cb403bd99acc875685" + }, { "path": "wordpress/wp-includes/blocks/details/style.css", "size": 97, "sha256": "b6de946e5946f6c0b65a3fd2fbf0253cd8b58b713cbf3000cd630b48c39e9ff1" }, + { + "path": "wordpress/wp-includes/blocks/details/style.min.css", + "size": 81, + "sha256": "b725e3774697bba8856599dd041eac8332c1caf46b5496cb403bd99acc875685" + }, { "path": "wordpress/wp-includes/blocks/embed/block.json", "size": 1054, "sha256": "adbe144e10b11413126fd6bd59337c124a081ef4da379715a1532c1ea4505b13" }, + { + "path": "wordpress/wp-includes/blocks/embed/editor-rtl.min.css", + "size": 707, + "sha256": "62ed1030f361f99cbe7a54551439e0b809519f52af4839cd415b18727d5335fa" + }, + { + "path": "wordpress/wp-includes/blocks/embed/editor.min.css", + "size": 707, + "sha256": "62ed1030f361f99cbe7a54551439e0b809519f52af4839cd415b18727d5335fa" + }, + { + "path": "wordpress/wp-includes/blocks/embed/style-rtl.min.css", + "size": 1667, + "sha256": "2329e4c9102a51ac33e48ee6ce4087c4f8367de2d3f8400d9825d5296b6aaa8a" + }, { "path": "wordpress/wp-includes/blocks/embed/style.css", "size": 3618, "sha256": "10aac666cfe9be0692e7cb14e1178b86902d93350c66dce6092eb24755a6c8f6" }, + { + "path": "wordpress/wp-includes/blocks/embed/style.min.css", + "size": 1667, + "sha256": "2329e4c9102a51ac33e48ee6ce4087c4f8367de2d3f8400d9825d5296b6aaa8a" + }, + { + "path": "wordpress/wp-includes/blocks/embed/theme-rtl.min.css", + "size": 176, + "sha256": "61feb06a0b8e5b72d231389dbe8f99e05f19998d85541ff126e71ae0c0b9ef14" + }, + { + "path": "wordpress/wp-includes/blocks/embed/theme.min.css", + "size": 176, + "sha256": "61feb06a0b8e5b72d231389dbe8f99e05f19998d85541ff126e71ae0c0b9ef14" + }, { "path": "wordpress/wp-includes/blocks/file.php", "size": 1879, @@ -3481,11 +3901,31 @@ "size": 1880, "sha256": "556a6d9380fa2de72ef6a8f4451b85aef249676fc8bb9d1c30db5e5addb98c53" }, + { + "path": "wordpress/wp-includes/blocks/file/editor-rtl.min.css", + "size": 763, + "sha256": "01cbb9b3830822a2828c733667a04644dc64b15fa1b5cac319ed143b7d51b60b" + }, + { + "path": "wordpress/wp-includes/blocks/file/editor.min.css", + "size": 762, + "sha256": "652620fa19f7942df4d74bca87d51e6fd7fc75f124d434c4d19946dfdedb728d" + }, + { + "path": "wordpress/wp-includes/blocks/file/style-rtl.min.css", + "size": 655, + "sha256": "2e4bb54290332bcd9af8aa33c00a0b3b8994ecdebcc6aa00bc09bfdc3270e0a1" + }, { "path": "wordpress/wp-includes/blocks/file/style.css", "size": 792, "sha256": "c73d18ee55e8bcbb3984fe4df039f2bf1dd4fdadaaa4bab36bb71cc19506648b" }, + { + "path": "wordpress/wp-includes/blocks/file/style.min.css", + "size": 654, + "sha256": "06138a428bab20e366c5257157fde8555463a00744a451c67650efc663fb51dd" + }, { "path": "wordpress/wp-includes/blocks/footnotes.php", "size": 3818, @@ -3496,16 +3936,36 @@ "size": 1430, "sha256": "2604dfb4b47045dd94edf3396b47401c6e6f0e7feba9067a220610975230a4e4" }, + { + "path": "wordpress/wp-includes/blocks/footnotes/style-rtl.min.css", + "size": 288, + "sha256": "d0d52c680f80debec7cf118999e1d57f3a0cccae97828b17cf887ff0d35320a1" + }, { "path": "wordpress/wp-includes/blocks/footnotes/style.css", "size": 342, "sha256": "352c94210682bf52d3630c4165fec4c589e9a5425374139eb7a8a6519e327f5f" }, + { + "path": "wordpress/wp-includes/blocks/footnotes/style.min.css", + "size": 287, + "sha256": "543eade00916013b2bbc1728e8a3899a4e50d5265f283a571475e2b8c6552065" + }, { "path": "wordpress/wp-includes/blocks/freeform/block.json", "size": 519, "sha256": "f3b4aa36e4b63f9c435eff38b041edfd046faf8f12570ffd190f2ca6867d1372" }, + { + "path": "wordpress/wp-includes/blocks/freeform/editor-rtl.min.css", + "size": 909, + "sha256": "2fefe051bc4f775b64cfe82b7f3a1dd3cf76b10d56839478430a30a678bca6a7" + }, + { + "path": "wordpress/wp-includes/blocks/freeform/editor.min.css", + "size": 909, + "sha256": "2fefe051bc4f775b64cfe82b7f3a1dd3cf76b10d56839478430a30a678bca6a7" + }, { "path": "wordpress/wp-includes/blocks/gallery.php", "size": 9067, @@ -3516,21 +3976,81 @@ "size": 3361, "sha256": "a5c552d96d35e7e67f45bf076566bd38f4b9ab294f3e08cec0b7c4bd7491bb8c" }, + { + "path": "wordpress/wp-includes/blocks/gallery/editor-rtl.min.css", + "size": 2057, + "sha256": "09954d1796bd0fbe0e9dce3ad7b3da8fb14e42c20d48d2e70182e0dd662623b5" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/editor.min.css", + "size": 2057, + "sha256": "add74c8cd51fb141e9ddf0707160d8b4ae5636ea302354edbfc8fce8ee9823c8" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/style-rtl.min.css", + "size": 16110, + "sha256": "2db7d891a18aef42f5678ef80ad161f358231f8b0e590afae3cd2b6bd3b88bae" + }, { "path": "wordpress/wp-includes/blocks/gallery/style.css", "size": 19507, "sha256": "41b3026ece5754f41a87ce6e70f5548cf818453ac9159ab81c28a2eb05ba6b51" }, + { + "path": "wordpress/wp-includes/blocks/gallery/style.min.css", + "size": 16120, + "sha256": "58f503a45492e0720e4e4ca1f579603e29181989da3f72e256017f93a1217eb6" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/theme-rtl.min.css", + "size": 123, + "sha256": "0d92ddb05513c897042d202033d79d33463ed3201a4f15a494979bd8517a3ec9" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/theme.min.css", + "size": 123, + "sha256": "0d92ddb05513c897042d202033d79d33463ed3201a4f15a494979bd8517a3ec9" + }, { "path": "wordpress/wp-includes/blocks/group/block.json", "size": 2079, "sha256": "b98ed4476f2f53248d3df4f718a006ddd011dbb62d2a996f87b19f148eaa9f00" }, + { + "path": "wordpress/wp-includes/blocks/group/editor-rtl.min.css", + "size": 1355, + "sha256": "c1602b8a3954159609bbbb5a3a0a6a590085d5ba74b6e6fe0f5a6463d3ac671a" + }, + { + "path": "wordpress/wp-includes/blocks/group/editor.min.css", + "size": 1355, + "sha256": "c1602b8a3954159609bbbb5a3a0a6a590085d5ba74b6e6fe0f5a6463d3ac671a" + }, + { + "path": "wordpress/wp-includes/blocks/group/style-rtl.min.css", + "size": 117, + "sha256": "628253f7b803c3e507e1189320a8c925ac718d5237c1f2c346a183ae02bbd6bb" + }, { "path": "wordpress/wp-includes/blocks/group/style.css", "size": 133, "sha256": "16464a7cf2a3e7dbc446e5b48f4a555c66f3b27022fcaf6f5f147efe383236d0" }, + { + "path": "wordpress/wp-includes/blocks/group/style.min.css", + "size": 117, + "sha256": "628253f7b803c3e507e1189320a8c925ac718d5237c1f2c346a183ae02bbd6bb" + }, + { + "path": "wordpress/wp-includes/blocks/group/theme-rtl.min.css", + "size": 62, + "sha256": "7cdc95241dae1a12350e177473d07acbcce3f01149572f4698cf2e84ac80da1b" + }, + { + "path": "wordpress/wp-includes/blocks/group/theme.min.css", + "size": 62, + "sha256": "7cdc95241dae1a12350e177473d07acbcce3f01149572f4698cf2e84ac80da1b" + }, { "path": "wordpress/wp-includes/blocks/heading.php", "size": 1301, @@ -3541,11 +4061,21 @@ "size": 1784, "sha256": "de2b28fb6a00552e9c57cc03b0249cdaaf73d4da57bcc570c6a42f87fa59f06a" }, + { + "path": "wordpress/wp-includes/blocks/heading/style-rtl.min.css", + "size": 1165, + "sha256": "187c2a0a016f532b73a680d2ec1095537ee7c6a545833c6c244571fdf3c9f7c9" + }, { "path": "wordpress/wp-includes/blocks/heading/style.css", "size": 1976, "sha256": "2a1ad02383103c53d74e2cadd9d1f273b87eef0424ae9b8398481254a6400501" }, + { + "path": "wordpress/wp-includes/blocks/heading/style.min.css", + "size": 1165, + "sha256": "187c2a0a016f532b73a680d2ec1095537ee7c6a545833c6c244571fdf3c9f7c9" + }, { "path": "wordpress/wp-includes/blocks/home-link.php", "size": 5438, @@ -3561,6 +4091,16 @@ "size": 593, "sha256": "08d6b98694e200e999b60558156038cb96ac56b4ddd431ce5bfdbe5686c9c43e" }, + { + "path": "wordpress/wp-includes/blocks/html/editor-rtl.min.css", + "size": 1043, + "sha256": "2ee7c42eca6728e1c399ac476ca230e171a5526dc4c503982f3a8895b4c6df14" + }, + { + "path": "wordpress/wp-includes/blocks/html/editor.min.css", + "size": 1042, + "sha256": "5936f51d0cbd9eda090ee73a8b36f7ad7b3f7aaa0ebea61198200c9d6ee591d4" + }, { "path": "wordpress/wp-includes/blocks/icon.php", "size": 4139, @@ -3571,11 +4111,31 @@ "size": 1447, "sha256": "cbf6fa910e1e5690a188e6781d6c83b3f433c4ec74f7725064aaa672269deb64" }, + { + "path": "wordpress/wp-includes/blocks/icon/editor-rtl.min.css", + "size": 849, + "sha256": "5556d04213fce175d4fdee24e11003ff72482db3fc8860b4f87c26bc53e28729" + }, + { + "path": "wordpress/wp-includes/blocks/icon/editor.min.css", + "size": 849, + "sha256": "5556d04213fce175d4fdee24e11003ff72482db3fc8860b4f87c26bc53e28729" + }, + { + "path": "wordpress/wp-includes/blocks/icon/style-rtl.min.css", + "size": 201, + "sha256": "1e4c6ad9e021546aa1a75c8325532d1238cc2d3183aad5da3781b57362880dcc" + }, { "path": "wordpress/wp-includes/blocks/icon/style.css", "size": 328, "sha256": "b49f67ec6e13ade10bb859a1a954723ce0455d58d69d4188e1bf4de982ba0999" }, + { + "path": "wordpress/wp-includes/blocks/icon/style.min.css", + "size": 201, + "sha256": "1e4c6ad9e021546aa1a75c8325532d1238cc2d3183aad5da3781b57362880dcc" + }, { "path": "wordpress/wp-includes/blocks/image.php", "size": 17672, @@ -3586,11 +4146,41 @@ "size": 3068, "sha256": "b93fbf057972409133d39259123a6a2694c65c2071bdc10a59a4be7847ef737e" }, + { + "path": "wordpress/wp-includes/blocks/image/editor-rtl.min.css", + "size": 2313, + "sha256": "e0a0ad2e06d48a567fd07e7951c00955de4bcc527c0bb7f71500596158af7037" + }, + { + "path": "wordpress/wp-includes/blocks/image/editor.min.css", + "size": 2313, + "sha256": "65b3475ab09bddf99629b48fa085eb3d19c736a818e148bbba47a5a638b64fc4" + }, + { + "path": "wordpress/wp-includes/blocks/image/style-rtl.min.css", + "size": 8794, + "sha256": "638bde21b34c96c32feee2168ff3abc5c48fbf0adbec7ecacc39a34911b2441e" + }, { "path": "wordpress/wp-includes/blocks/image/style.css", "size": 12485, "sha256": "6d3a9dcc001736ed2e447e981ad97f1951e5083a5abb04e11a44f8cff847e109" }, + { + "path": "wordpress/wp-includes/blocks/image/style.min.css", + "size": 8784, + "sha256": "2417f96c8d21991a91de4cffd2c10836c5ff65e2ab367c2243cd02a258cb469b" + }, + { + "path": "wordpress/wp-includes/blocks/image/theme-rtl.min.css", + "size": 188, + "sha256": "1bded44fd9ce83d8626fa1e638456791df38df28767be04332a6ae82ef543e94" + }, + { + "path": "wordpress/wp-includes/blocks/image/theme.min.css", + "size": 188, + "sha256": "1bded44fd9ce83d8626fa1e638456791df38df28767be04332a6ae82ef543e94" + }, { "path": "wordpress/wp-includes/blocks/index.php", "size": 5111, @@ -3606,11 +4196,21 @@ "size": 1395, "sha256": "b97f5ea91e130e88fb3d6affc14d3c2c2990d41fd2336f096c55bd5d6e9f7745" }, + { + "path": "wordpress/wp-includes/blocks/latest-comments/style-rtl.min.css", + "size": 1305, + "sha256": "3c9c8327f73d802b78f7d8f134b0a84bd308fdfc70aab99e8a9750ecbce88276" + }, { "path": "wordpress/wp-includes/blocks/latest-comments/style.css", "size": 1454, "sha256": "ccacc36732e7a5b896f5e8408a857fb4171dfba86271cf3ecc8d8fe087fd4dc2" }, + { + "path": "wordpress/wp-includes/blocks/latest-comments/style.min.css", + "size": 1302, + "sha256": "3a5a541b5946a8d213d206e3d7920ce6e293ebb336b8adaf563b35b82ebe234d" + }, { "path": "wordpress/wp-includes/blocks/latest-posts.php", "size": 8843, @@ -3621,11 +4221,31 @@ "size": 2575, "sha256": "d4829e6b82f35377378ee9448d504d46187b077d9ce15598d19d9f513132ff66" }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/editor-rtl.min.css", + "size": 250, + "sha256": "0f80eaf97b93314675765e37c69efe1c06f4e7477173aea3b49cc53d954931cb" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/editor.min.css", + "size": 248, + "sha256": "e029e81cbc9f19294cd208e11f9a57785318882bb2e6598d607cfbefc434e985" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/style-rtl.min.css", + "size": 1833, + "sha256": "0197009f6a71dc3e40a66309dff56de867c4f711d2b2057980bffaeaa7b418f5" + }, { "path": "wordpress/wp-includes/blocks/latest-posts/style.css", "size": 4072, "sha256": "44b5cd8579c4c24ddbcb1a6830a9c3d4366f4099b4574235304c9bc1850dd1bc" }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/style.min.css", + "size": 1837, + "sha256": "6b43473e0898e1d0809ead87010e7f8b5d032f8d3257b8205c61e342b7f63fa0" + }, { "path": "wordpress/wp-includes/blocks/legacy-widget.php", "size": 4011, @@ -3651,11 +4271,21 @@ "size": 1901, "sha256": "25ed613d2f8fa322a2d93e631641cc6f4cc9bb1837a74adbf6ecc450e8e659f6" }, + { + "path": "wordpress/wp-includes/blocks/list/style-rtl.min.css", + "size": 95, + "sha256": "942b2302016e4ef821e6ce7f339addb7a7b021f151c2132d1d6de54c849b3a34" + }, { "path": "wordpress/wp-includes/blocks/list/style.css", "size": 892, "sha256": "d989fe25818d80ba8ade0c57d1fde3598db66204f62785cbcf2ba45c0862700f" }, + { + "path": "wordpress/wp-includes/blocks/list/style.min.css", + "size": 95, + "sha256": "942b2302016e4ef821e6ce7f339addb7a7b021f151c2132d1d6de54c849b3a34" + }, { "path": "wordpress/wp-includes/blocks/loginout.php", "size": 1664, @@ -3666,21 +4296,51 @@ "size": 1318, "sha256": "6a7432bf153a2718d6fba527658857170ff15d6e6756e1516b027dc5fbeb21fc" }, + { + "path": "wordpress/wp-includes/blocks/loginout/style-rtl.min.css", + "size": 41, + "sha256": "79fc5403cb35199ba3f96d205abed9049556d12321c93ada5ae6e704a6d91e1b" + }, { "path": "wordpress/wp-includes/blocks/loginout/style.css", "size": 48, "sha256": "40e1b3c2a16921b461cc51fa77c58a00e824903744073ce9a1077420b6ae9633" }, + { + "path": "wordpress/wp-includes/blocks/loginout/style.min.css", + "size": 41, + "sha256": "79fc5403cb35199ba3f96d205abed9049556d12321c93ada5ae6e704a6d91e1b" + }, { "path": "wordpress/wp-includes/blocks/math/block.json", "size": 986, "sha256": "a6014ac32c09c1d68f70be29adf67e3b7b689b308df8a17c54cebffa29546ccd" }, + { + "path": "wordpress/wp-includes/blocks/math/editor-rtl.min.css", + "size": 100, + "sha256": "7e21acb80fafa314f4efcd6be57fc54eafd3de1fcb642c03b4035ce73fb70387" + }, + { + "path": "wordpress/wp-includes/blocks/math/editor.min.css", + "size": 100, + "sha256": "7e21acb80fafa314f4efcd6be57fc54eafd3de1fcb642c03b4035ce73fb70387" + }, + { + "path": "wordpress/wp-includes/blocks/math/style-rtl.min.css", + "size": 49, + "sha256": "5aca1354d093f7bd74a9c251e9ba34933ee5ec3ce86d9b0469a4f1cce4f11c3c" + }, { "path": "wordpress/wp-includes/blocks/math/style.css", "size": 60, "sha256": "15bcbf060ae81ad9c9a9d6a0acb997fa914c515c98ad885d00101143290f664b" }, + { + "path": "wordpress/wp-includes/blocks/math/style.min.css", + "size": 49, + "sha256": "5aca1354d093f7bd74a9c251e9ba34933ee5ec3ce86d9b0469a4f1cce4f11c3c" + }, { "path": "wordpress/wp-includes/blocks/media-text.php", "size": 4298, @@ -3691,11 +4351,31 @@ "size": 2928, "sha256": "8bbf956f4de9717599dd21305cb4f0b717961cec075ba47c27b2f889d593dde8" }, + { + "path": "wordpress/wp-includes/blocks/media-text/editor-rtl.min.css", + "size": 853, + "sha256": "525ca0cdc862c1fdae74f6560b5d92ef2f31b4c15796db03697abcc262d6e12e" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/editor.min.css", + "size": 851, + "sha256": "b009b5ae7ee4ece83eebe703831745f197f08f7480bb221dd441f3fc30c1d17b" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/style-rtl.min.css", + "size": 2415, + "sha256": "f2ebde07d5038707919bdb195e8bb6b80566f8d9e35f2d2e65e1743d84f858c7" + }, { "path": "wordpress/wp-includes/blocks/media-text/style.css", "size": 3529, "sha256": "41f2006567e3d1194a50da59a37fd4dabfce2052c1bd26002bf53639ebafa9e3" }, + { + "path": "wordpress/wp-includes/blocks/media-text/style.min.css", + "size": 2415, + "sha256": "246a123620ac962df698ae2b266e98edd0828ac1b86c8c2d6b7d72a61794dbfd" + }, { "path": "wordpress/wp-includes/blocks/missing/block.json", "size": 700, @@ -3706,6 +4386,16 @@ "size": 703, "sha256": "99f9231441fafa79bbb8b1e482cf47f17c18033e3cac013a07503d8009988121" }, + { + "path": "wordpress/wp-includes/blocks/more/editor-rtl.min.css", + "size": 611, + "sha256": "f27312a487a236efd5a324886005d309944e70c03dcb3edfac38f2c2ab1e102c" + }, + { + "path": "wordpress/wp-includes/blocks/more/editor.min.css", + "size": 611, + "sha256": "f27312a487a236efd5a324886005d309944e70c03dcb3edfac38f2c2ab1e102c" + }, { "path": "wordpress/wp-includes/blocks/navigation-link.php", "size": 16405, @@ -3716,6 +4406,16 @@ "size": 1812, "sha256": "a8914cbdd227ee91d158657ecc291c43a411fcfdeb34d4817afe84a262d829e1" }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/editor-rtl.min.css", + "size": 2314, + "sha256": "a9a2f332da0c1219fb372456b26c98da4385e73a6e8c09fd0d4a1663a0a32ead" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/editor.min.css", + "size": 2313, + "sha256": "6515baef6839ca81c53cd48d8ce16c9ce4cc32e453d9d99583639512af031a53" + }, { "path": "wordpress/wp-includes/blocks/navigation-link/shared/item-should-render.php", "size": 1533, @@ -3726,11 +4426,21 @@ "size": 465, "sha256": "f82783dc60953740dd1c36aec572f537e4a85742f3312a4c8d972d18d3826d79" }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/style-rtl.min.css", + "size": 317, + "sha256": "8790836470e0b7b8f66e6f2e45ff81867b285684a63852e6b007659e1e190ee0" + }, { "path": "wordpress/wp-includes/blocks/navigation-link/style.css", "size": 1146, "sha256": "2f6d2eb23297e0dba1dddd95c96dd64c1887747e76f2a6665a9579910a0b5431" }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/style.min.css", + "size": 316, + "sha256": "7dd7835ab92b90455b2e9aabb2fb0e2b55d2713198707588016bb8cecf9d8e17" + }, { "path": "wordpress/wp-includes/blocks/navigation-overlay-close.php", "size": 1765, @@ -3741,11 +4451,21 @@ "size": 1141, "sha256": "fc91fbbe5a06dfdb1d1ad3cabf060d59ee5a603b68ad917d8937959d2b39a7c5" }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close/style-rtl.min.css", + "size": 464, + "sha256": "7428052d984d06efb69e2c42b97fbd4616084df7fdda451d5165bc20d4104967" + }, { "path": "wordpress/wp-includes/blocks/navigation-overlay-close/style.css", "size": 554, "sha256": "7acd0294355c4196bf0d5f1634044e06400ad112fba41879768cd60d911c6a34" }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close/style.min.css", + "size": 464, + "sha256": "7428052d984d06efb69e2c42b97fbd4616084df7fdda451d5165bc20d4104967" + }, { "path": "wordpress/wp-includes/blocks/navigation-submenu.php", "size": 12584, @@ -3756,6 +4476,16 @@ "size": 1826, "sha256": "10788e7533cc449af6e817eb01cfbdac57bb0b5e959658f48bb042564552289a" }, + { + "path": "wordpress/wp-includes/blocks/navigation-submenu/editor-rtl.min.css", + "size": 1099, + "sha256": "e366d6d35919aebbb7add82c0d0e5a64b5124a0fd513fc5096cd108c4ac14c68" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-submenu/editor.min.css", + "size": 1098, + "sha256": "9c8646e5f5b48b470cee0d2cfdea8cfa5120760a98d6d30049a51a5b6429aaad" + }, { "path": "wordpress/wp-includes/blocks/navigation.php", "size": 64446, @@ -3766,16 +4496,46 @@ "size": 3478, "sha256": "df7eeaa48bddd4986a4b76cc8e3e85ad24b29d06dbaec9122d69cad6db2a57ad" }, + { + "path": "wordpress/wp-includes/blocks/navigation/editor-rtl.min.css", + "size": 11673, + "sha256": "224c906ad0f8e9cee80bf61b209353f288f208e5f5c175e72307ab225719e462" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/editor.min.css", + "size": 11670, + "sha256": "e6cab1f73a57267d692636e158d4d32c0dee092c4e4985fcffe089198785c877" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/style-rtl.min.css", + "size": 20716, + "sha256": "43a7c90c5aba15bf4a9795a6c1cc278e99f24d71535b1a145d46ee3024c3218a" + }, { "path": "wordpress/wp-includes/blocks/navigation/style.css", "size": 24623, "sha256": "1fbab5e859243cd1aa473124e829d79b5bfc14e0fead7c67626db9941e94be63" }, + { + "path": "wordpress/wp-includes/blocks/navigation/style.min.css", + "size": 20709, + "sha256": "8970a41c9eb07af6f38cde8079663efdec36fe89b9d71033b5e8686567107dea" + }, { "path": "wordpress/wp-includes/blocks/nextpage/block.json", "size": 554, "sha256": "dffa28ec92eadcba753882479125249f163f95af1f541da72790e8c8017df1a2" }, + { + "path": "wordpress/wp-includes/blocks/nextpage/editor-rtl.min.css", + "size": 592, + "sha256": "8352c7781e57d92b03339a36983dcd17fec06516c3922dc620f34ba68a4d1fdf" + }, + { + "path": "wordpress/wp-includes/blocks/nextpage/editor.min.css", + "size": 592, + "sha256": "8352c7781e57d92b03339a36983dcd17fec06516c3922dc620f34ba68a4d1fdf" + }, { "path": "wordpress/wp-includes/blocks/page-list-item.php", "size": 361, @@ -3796,11 +4556,31 @@ "size": 1786, "sha256": "267146092d9c9df8d89e4957e37a13bb00f74b0a949ba73097a6ca2145138319" }, + { + "path": "wordpress/wp-includes/blocks/page-list/editor-rtl.min.css", + "size": 1056, + "sha256": "1784e6cde1b5750b042ef0ae39511eec4f0eae4e1e8f18163d6dccf2393da258" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/editor.min.css", + "size": 1056, + "sha256": "1784e6cde1b5750b042ef0ae39511eec4f0eae4e1e8f18163d6dccf2393da258" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/style-rtl.min.css", + "size": 404, + "sha256": "6f1226725cfa6ddc35ef66475019751df23f156f1672e59b2a39eee4a7c300d8" + }, { "path": "wordpress/wp-includes/blocks/page-list/style.css", "size": 452, "sha256": "983455af96b98b594c470eb940247da2a5f6fad60d284ab994590cc762f6562f" }, + { + "path": "wordpress/wp-includes/blocks/page-list/style.min.css", + "size": 404, + "sha256": "6f1226725cfa6ddc35ef66475019751df23f156f1672e59b2a39eee4a7c300d8" + }, { "path": "wordpress/wp-includes/blocks/paragraph.php", "size": 1139, @@ -3811,11 +4591,31 @@ "size": 1887, "sha256": "70a5e1bc941b4adb1591dccbe8e1095ecbfd1f7baf6c2a7004d9f9c047d31c80" }, + { + "path": "wordpress/wp-includes/blocks/paragraph/editor-rtl.min.css", + "size": 798, + "sha256": "29500bcd5922c0718c22156c78540f680815521f3ac8c63740b656959f76de32" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/editor.min.css", + "size": 797, + "sha256": "aa9c6ca6a67fdbe4f25b49e7972f76f88375c04f9532b16bdfd9631520293b6e" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/style-rtl.min.css", + "size": 657, + "sha256": "10a3b473645eec27e17c37ff79e41fb055cfe42008719d687af53bf67e4b7d2d" + }, { "path": "wordpress/wp-includes/blocks/paragraph/style.css", "size": 1562, "sha256": "f37a4910749fd26b4bdeae81449d6b5074078ac339fb18f7ced5a952d9b00cea" }, + { + "path": "wordpress/wp-includes/blocks/paragraph/style.min.css", + "size": 655, + "sha256": "622f970d122913e7a6707d8be35940b9417f061eb64b21c3bf1b01b76bfccd62" + }, { "path": "wordpress/wp-includes/blocks/pattern.php", "size": 1788, @@ -3836,11 +4636,21 @@ "size": 1259, "sha256": "3c4c84cdc86e978a90e2a019071fc770b77c9dc048fda11ee86f0e75d10e0658" }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography/style-rtl.min.css", + "size": 54, + "sha256": "c3a97e8df90c0af1ad6d8678089bfb7944b3dcc977aab2d863ee160543eca527" + }, { "path": "wordpress/wp-includes/blocks/post-author-biography/style.css", "size": 61, "sha256": "ab347926fa395caeaefd27a172edae49cdc647c0509fa423bc060476fd055a1f" }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography/style.min.css", + "size": 54, + "sha256": "c3a97e8df90c0af1ad6d8678089bfb7944b3dcc977aab2d863ee160543eca527" + }, { "path": "wordpress/wp-includes/blocks/post-author-name.php", "size": 1951, @@ -3851,11 +4661,21 @@ "size": 1465, "sha256": "9d9606a5c3688d90c1113d0484f42bc939fd6ae4c7b15cc5645642a602ab6645" }, + { + "path": "wordpress/wp-includes/blocks/post-author-name/style-rtl.min.css", + "size": 49, + "sha256": "99d6d57e920928f90fdafecf5a90d93c3dca24dea4d323ae12b61bd2ddb977e9" + }, { "path": "wordpress/wp-includes/blocks/post-author-name/style.css", "size": 56, "sha256": "bd38a1cb30550b25db4bc5cecdc715836bc3a80651966e3e0268e90e91d26dc6" }, + { + "path": "wordpress/wp-includes/blocks/post-author-name/style.min.css", + "size": 49, + "sha256": "99d6d57e920928f90fdafecf5a90d93c3dca24dea4d323ae12b61bd2ddb977e9" + }, { "path": "wordpress/wp-includes/blocks/post-author.php", "size": 2734, @@ -3866,11 +4686,31 @@ "size": 1957, "sha256": "54e5e45b68a627fb2fd1eeea0225cb98084b66df122e025b8ee33271da905ba3" }, + { + "path": "wordpress/wp-includes/blocks/post-author/editor-rtl.min.css", + "size": 95, + "sha256": "31e8f5519b06c8e27633cae49d392be2618e9d1e8fc30c8a9901d770f42499ef" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/editor.min.css", + "size": 95, + "sha256": "31e8f5519b06c8e27633cae49d392be2618e9d1e8fc30c8a9901d770f42499ef" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/style-rtl.min.css", + "size": 357, + "sha256": "e36ab3540cb80f33c4f50fb6dd0534541f532c2fe3f2dc83c2ac5e310c3df652" + }, { "path": "wordpress/wp-includes/blocks/post-author/style.css", "size": 436, "sha256": "29c743c6d57a11ba64d6b347bd8b4d696c29f38eaa1548d5410663a99716aac9" }, + { + "path": "wordpress/wp-includes/blocks/post-author/style.min.css", + "size": 358, + "sha256": "4b47d7b7f17933fcb057d0f09a00c98a836f1a7727c091d0f30d8626f58eb4ac" + }, { "path": "wordpress/wp-includes/blocks/post-comments-count.php", "size": 1275, @@ -3881,11 +4721,21 @@ "size": 1132, "sha256": "1d189828d9c04786d60885e321939c19e6606356df7132eacd4c38317db8160e" }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count/style-rtl.min.css", + "size": 52, + "sha256": "473d5542e8302768c7affb3e4fa767fb2666a5ad64d8a2a19aceae033ea42dac" + }, { "path": "wordpress/wp-includes/blocks/post-comments-count/style.css", "size": 59, "sha256": "ee2dc72a10a48bc6ccc1d7d4bb2e2d449c01cd082ffee6ab95eb9e6ef6011678" }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count/style.min.css", + "size": 52, + "sha256": "473d5542e8302768c7affb3e4fa767fb2666a5ad64d8a2a19aceae033ea42dac" + }, { "path": "wordpress/wp-includes/blocks/post-comments-form.php", "size": 2805, @@ -3896,11 +4746,31 @@ "size": 1340, "sha256": "c7b457d0ed2f01f98632dddf66936ac38826201396636c13feca75a7cb14c2f6" }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/editor-rtl.min.css", + "size": 124, + "sha256": "5c020575e1009b3bafe477f47c60d0bc523569ccf73a8fe114e9ef2730063859" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/editor.min.css", + "size": 124, + "sha256": "5c020575e1009b3bafe477f47c60d0bc523569ccf73a8fe114e9ef2730063859" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/style-rtl.min.css", + "size": 1992, + "sha256": "d2d6339687455805e3d5b66b9f5e5fa98c0637fcc1dfc6fb650e5e099a0a9142" + }, { "path": "wordpress/wp-includes/blocks/post-comments-form/style.css", "size": 2234, "sha256": "127b185692bb444a67029469ea8d517b9b8c7f81d2d54d634a4e1788d39352ac" }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/style.min.css", + "size": 1991, + "sha256": "c83105fde6fa20f285ec2acdb14bf21062e78ac6b8b520864c79ca65909ab410" + }, { "path": "wordpress/wp-includes/blocks/post-comments-link.php", "size": 2237, @@ -3911,11 +4781,21 @@ "size": 1289, "sha256": "57aa85ac73a92a5458c4d75e1cd504aeecce8f42227a010328cc1a204f308d97" }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link/style-rtl.min.css", + "size": 51, + "sha256": "dd15d4270f4ec63259ce1ca86cfd79fd37811078e1b448e7d9d110ec92797cfc" + }, { "path": "wordpress/wp-includes/blocks/post-comments-link/style.css", "size": 58, "sha256": "e74d4a8ed66f08eefa6f205ea57bd4042a148da12ad6819614d6152e73bbc1a7" }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link/style.min.css", + "size": 51, + "sha256": "dd15d4270f4ec63259ce1ca86cfd79fd37811078e1b448e7d9d110ec92797cfc" + }, { "path": "wordpress/wp-includes/blocks/post-content.php", "size": 2347, @@ -3926,11 +4806,21 @@ "size": 1757, "sha256": "f2cde6429ca1c317319193594320b587b46b771507fd3cf4e5218ae9a3311645" }, + { + "path": "wordpress/wp-includes/blocks/post-content/style-rtl.min.css", + "size": 41, + "sha256": "5632fcbd1fa2fa2c20a2c160d11acdaf4d26a45c1478144671d75be2c69fe20c" + }, { "path": "wordpress/wp-includes/blocks/post-content/style.css", "size": 48, "sha256": "4a95959fb196e41d1fbdcc7e21bace395eda401e928c2064e3751408fe324ba2" }, + { + "path": "wordpress/wp-includes/blocks/post-content/style.min.css", + "size": 41, + "sha256": "5632fcbd1fa2fa2c20a2c160d11acdaf4d26a45c1478144671d75be2c69fe20c" + }, { "path": "wordpress/wp-includes/blocks/post-date.php", "size": 3655, @@ -3941,11 +4831,21 @@ "size": 1463, "sha256": "03480b28239717509d7645055d0321e166330300ea70fa3e143bc25140b95585" }, + { + "path": "wordpress/wp-includes/blocks/post-date/style-rtl.min.css", + "size": 42, + "sha256": "d2fcacb015df3f08b4d661b068968206bb991fc6966f0176f1acd6d70c356e68" + }, { "path": "wordpress/wp-includes/blocks/post-date/style.css", "size": 49, "sha256": "b242f6f5e2d87611434f2e96edc80421d57d7288b9a591df2380349caf80560a" }, + { + "path": "wordpress/wp-includes/blocks/post-date/style.min.css", + "size": 42, + "sha256": "d2fcacb015df3f08b4d661b068968206bb991fc6966f0176f1acd6d70c356e68" + }, { "path": "wordpress/wp-includes/blocks/post-excerpt.php", "size": 4315, @@ -3956,11 +4856,31 @@ "size": 1587, "sha256": "673589fb0c180a6307c17945ac01b656a019c7bdceba2becf20f468ac448d0de" }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/editor-rtl.min.css", + "size": 80, + "sha256": "f41e820e6860ec82ade4742af5842d07a6d959e362042deac9407ab35bc025a4" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/editor.min.css", + "size": 80, + "sha256": "f41e820e6860ec82ade4742af5842d07a6d959e362042deac9407ab35bc025a4" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/style-rtl.min.css", + "size": 339, + "sha256": "cd02424f179ba3c421371d8002dadc5290837f3f30b8b47904c48fe4c404d035" + }, { "path": "wordpress/wp-includes/blocks/post-excerpt/style.css", "size": 389, "sha256": "1abd3ef89a38a5048b2ee79ee26a17c538b8906e81efaac73a9d1c10a251fdaa" }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/style.min.css", + "size": 339, + "sha256": "cd02424f179ba3c421371d8002dadc5290837f3f30b8b47904c48fe4c404d035" + }, { "path": "wordpress/wp-includes/blocks/post-featured-image.php", "size": 9248, @@ -3971,11 +4891,31 @@ "size": 2457, "sha256": "fac721751b76cb27ca90823cf051a866c27c8ed63f0ec74fcba528dd4d32f197" }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/editor-rtl.min.css", + "size": 4936, + "sha256": "9e0147b8abb48183eaa66a34326c9313c83b2c8ea8e7a8f8673ef44ce1403d96" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/editor.min.css", + "size": 4936, + "sha256": "e1c37d810d0f1112b2909517f01a05ef4f48a2617ce40480d27fb64f772824eb" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/style-rtl.min.css", + "size": 1833, + "sha256": "24441594aa1e6b2016a479490c781edb54bd2a631d912b833837b650d2318a20" + }, { "path": "wordpress/wp-includes/blocks/post-featured-image/style.css", "size": 2050, "sha256": "f0d6ba32be7201238ebea9fb76c061886db7b78354144dd65fef2361f1f6178b" }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/style.min.css", + "size": 1833, + "sha256": "24441594aa1e6b2016a479490c781edb54bd2a631d912b833837b650d2318a20" + }, { "path": "wordpress/wp-includes/blocks/post-navigation-link.php", "size": 4807, @@ -3986,11 +4926,21 @@ "size": 1358, "sha256": "c93ed40906e3d6d58c2cf003e42c27ace62e3571f4da5d91f1e3b2ac143cdd4e" }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link/style-rtl.min.css", + "size": 656, + "sha256": "4deaffe864b7373606fd3267183806f764e69a69d19e3fd5a7e10392ff822ec2" + }, { "path": "wordpress/wp-includes/blocks/post-navigation-link/style.css", "size": 742, "sha256": "7bf8823266d0c3e5c940c8bf4f8eabdfaf54e0e42e5a2b8a8fa537441d319760" }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link/style.min.css", + "size": 654, + "sha256": "82172503bc289d3b32b80066cf6980d5132fbe48f08212751871994a58244875" + }, { "path": "wordpress/wp-includes/blocks/post-template.php", "size": 5687, @@ -4001,11 +4951,21 @@ "size": 1589, "sha256": "5fed346b6318eb89cc80375b42e252ce7c02e946db39b83708d7d87a05d78ced" }, + { + "path": "wordpress/wp-includes/blocks/post-template/style-rtl.min.css", + "size": 1521, + "sha256": "693c5178584f5add24406f9c2d8e18d91e55a3714b2710b430c86e214446ad54" + }, { "path": "wordpress/wp-includes/blocks/post-template/style.css", "size": 3572, "sha256": "c5d42ac6d78909c5bb4ba4c9b49bb07e9242aee38871e16e42b0c85fc7f5a472" }, + { + "path": "wordpress/wp-includes/blocks/post-template/style.min.css", + "size": 1521, + "sha256": "e3671ed8ee305a452c2929df91397956b3308f8e12ac35772a29fdbc655bd1bd" + }, { "path": "wordpress/wp-includes/blocks/post-terms.php", "size": 3640, @@ -4016,11 +4976,21 @@ "size": 1533, "sha256": "41372d6e5b1390bf1c9f920306162f2d23dd421819407725bcada76a1b34b228" }, + { + "path": "wordpress/wp-includes/blocks/post-terms/style-rtl.min.css", + "size": 117, + "sha256": "0a63691280a665a3e6186b64fcdc7388f7650710168b7cb28428c8e51f97de30" + }, { "path": "wordpress/wp-includes/blocks/post-terms/style.css", "size": 132, "sha256": "735f2d2e6b6c081b4fa4d01b10a119bbf5a4b828910cd287c6de9fdded8c6fae" }, + { + "path": "wordpress/wp-includes/blocks/post-terms/style.min.css", + "size": 117, + "sha256": "0a63691280a665a3e6186b64fcdc7388f7650710168b7cb28428c8e51f97de30" + }, { "path": "wordpress/wp-includes/blocks/post-time-to-read.php", "size": 6411, @@ -4031,11 +5001,21 @@ "size": 1413, "sha256": "01fcbd102049849a3f6d49fc6db38169e09cd40d91d57039400c28346d16aa99" }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read/style-rtl.min.css", + "size": 50, + "sha256": "20151eee8731829b21007824c6078261d3914ea9fe04a2a4834a47dd9675d3c3" + }, { "path": "wordpress/wp-includes/blocks/post-time-to-read/style.css", "size": 57, "sha256": "3ac4f47140117207ea9324d425d4576644e7aa8a7932539e1bb2509023c256cf" }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read/style.min.css", + "size": 50, + "sha256": "20151eee8731829b21007824c6078261d3914ea9fe04a2a4834a47dd9675d3c3" + }, { "path": "wordpress/wp-includes/blocks/post-title.php", "size": 2138, @@ -4046,31 +5026,81 @@ "size": 1753, "sha256": "7a8fdec350a478c6d3710f9e9c939298957e82c943e9c1734b549ff206afe300" }, + { + "path": "wordpress/wp-includes/blocks/post-title/style-rtl.min.css", + "size": 261, + "sha256": "ceb4bb251d54c06a97753b6118da782a43e24dc0f28d6cb0252d42a3aaabde43" + }, { "path": "wordpress/wp-includes/blocks/post-title/style.css", "size": 308, "sha256": "1ab285b72f9f05f4bd6b0fc77ae17359df17b7f75c351d337d052853da737bab" }, + { + "path": "wordpress/wp-includes/blocks/post-title/style.min.css", + "size": 261, + "sha256": "ceb4bb251d54c06a97753b6118da782a43e24dc0f28d6cb0252d42a3aaabde43" + }, { "path": "wordpress/wp-includes/blocks/preformatted/block.json", "size": 1337, "sha256": "d5a9fef98fd49f0820ef74928e1f4e0a775103ad1778f3078fb70edfe17bbae2" }, + { + "path": "wordpress/wp-includes/blocks/preformatted/style-rtl.min.css", + "size": 135, + "sha256": "ac3ebd1b8d17d5e2a4c67cfc4bc1d92857e1c556043f2d09caa295fa989d1023" + }, { "path": "wordpress/wp-includes/blocks/preformatted/style.css", "size": 935, "sha256": "ba55d9b5b02e949b2d815b39323b8759bd3406e3ecdf6658ada0d775125edb79" }, + { + "path": "wordpress/wp-includes/blocks/preformatted/style.min.css", + "size": 135, + "sha256": "ac3ebd1b8d17d5e2a4c67cfc4bc1d92857e1c556043f2d09caa295fa989d1023" + }, { "path": "wordpress/wp-includes/blocks/pullquote/block.json", "size": 1923, "sha256": "70b1e1612a9e3cd61fba13852fbcb213c3b31b87aaf2f75f3e6bb52d583748ba" }, + { + "path": "wordpress/wp-includes/blocks/pullquote/editor-rtl.min.css", + "size": 222, + "sha256": "8146af05ca9bc940471eeff9401c5836cab1eeb122e20ec764dd0c99c55c582c" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/editor.min.css", + "size": 222, + "sha256": "8146af05ca9bc940471eeff9401c5836cab1eeb122e20ec764dd0c99c55c582c" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/style-rtl.min.css", + "size": 1118, + "sha256": "e17f9e872d9482e4a3134da00959f8995ad88e407fada6c17e8252962be9b11d" + }, { "path": "wordpress/wp-includes/blocks/pullquote/style.css", "size": 2075, "sha256": "2f224de46c66cf5999467afff581a1d7af111467c50120453a3dcaaccae4f9dd" }, + { + "path": "wordpress/wp-includes/blocks/pullquote/style.min.css", + "size": 1118, + "sha256": "719147e897375906b9dfeee27ee1251f8640e3992666bef5cc7f2034514ba605" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/theme-rtl.min.css", + "size": 283, + "sha256": "1df1aeaafec585b9386f763a6872d4b448480bd714197c278deb729df69c2b7e" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/theme.min.css", + "size": 283, + "sha256": "1df1aeaafec585b9386f763a6872d4b448480bd714197c278deb729df69c2b7e" + }, { "path": "wordpress/wp-includes/blocks/query-no-results.php", "size": 1847, @@ -4101,6 +5131,16 @@ "size": 1112, "sha256": "33b446f19ff6ad294d7408991b2a80075e7fe390e44291bfe8906536f32fc04e" }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-numbers/editor-rtl.min.css", + "size": 203, + "sha256": "27b061f6c2dafa64f3abb05e27453ea040c23db704fdad64d59ff29681a1610c" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-numbers/editor.min.css", + "size": 204, + "sha256": "d3652b4f9ce9249f7c205a0180005264a8a6c35ef341e1706323af7b71fa4f12" + }, { "path": "wordpress/wp-includes/blocks/query-pagination-previous.php", "size": 3473, @@ -4121,11 +5161,31 @@ "size": 1597, "sha256": "069e71a12010875bf01e9c20a8edac228f7f22e30d631dc8688fc0ba759f3684" }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/editor-rtl.min.css", + "size": 251, + "sha256": "b528925306c9ee82d22315d5c6f8fdb1b2bbd350dcc129c9a0f237a984587764" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/editor.min.css", + "size": 251, + "sha256": "b528925306c9ee82d22315d5c6f8fdb1b2bbd350dcc129c9a0f237a984587764" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/style-rtl.min.css", + "size": 766, + "sha256": "962cc6d7e65fb2689088b17057674e6d819c9a5d0d658ca1de12da19974014af" + }, { "path": "wordpress/wp-includes/blocks/query-pagination/style.css", "size": 871, "sha256": "88258068d6814f43bfe37268aebaad94ec5f113c9c093c38216e00c8546bfc11" }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/style.min.css", + "size": 764, + "sha256": "7278837aa0d5787fa817bcfac8e84ea0cce6a603f32025de917b57279990e966" + }, { "path": "wordpress/wp-includes/blocks/query-title.php", "size": 2771, @@ -4136,11 +5196,21 @@ "size": 1582, "sha256": "09edff84de4545607bb3f8d1092188f2942ca7fbbcb4968c271abaceeae49013" }, + { + "path": "wordpress/wp-includes/blocks/query-title/style-rtl.min.css", + "size": 44, + "sha256": "64225111e4d9a55bc90152663fd0b02d5b319d93f5b1952378be722c6d640d5f" + }, { "path": "wordpress/wp-includes/blocks/query-title/style.css", "size": 51, "sha256": "f0e4ae006b910c0adb01575011233dc99897f441834c01825f38fbe8d5d4b3c2" }, + { + "path": "wordpress/wp-includes/blocks/query-title/style.min.css", + "size": 44, + "sha256": "64225111e4d9a55bc90152663fd0b02d5b319d93f5b1952378be722c6d640d5f" + }, { "path": "wordpress/wp-includes/blocks/query-total.php", "size": 2471, @@ -4151,11 +5221,21 @@ "size": 1352, "sha256": "e8c7b369d9626fae70b7259927ed45a5edba6cff1c4bb606f0bfabef2af6696d" }, + { + "path": "wordpress/wp-includes/blocks/query-total/style-rtl.min.css", + "size": 44, + "sha256": "673579e48dc2a0e1f25952657cbad59e1f4927856e245244207bc3a9b2230f15" + }, { "path": "wordpress/wp-includes/blocks/query-total/style.css", "size": 51, "sha256": "22ac232d4b704be23ca34a46b4b3f8425c9959981e5125b7d8cd7b4733b540d2" }, + { + "path": "wordpress/wp-includes/blocks/query-total/style.min.css", + "size": 44, + "sha256": "673579e48dc2a0e1f25952657cbad59e1f4927856e245244207bc3a9b2230f15" + }, { "path": "wordpress/wp-includes/blocks/query.php", "size": 5690, @@ -4166,16 +5246,46 @@ "size": 1302, "sha256": "f37a436a5c1d84be14dcd8a8f114a817a00b743d73a69253352384997a9fbecc" }, + { + "path": "wordpress/wp-includes/blocks/query/editor-rtl.min.css", + "size": 1499, + "sha256": "bd56c733e1f12d607b7573b605bc3562542c301ba5f96a0db1d7c772590424d5" + }, + { + "path": "wordpress/wp-includes/blocks/query/editor.min.css", + "size": 1499, + "sha256": "bd56c733e1f12d607b7573b605bc3562542c301ba5f96a0db1d7c772590424d5" + }, { "path": "wordpress/wp-includes/blocks/quote/block.json", "size": 2223, "sha256": "ec56d2ece476b32f38f924f5cc2d9b0f8542951222318efb92f284a5b4b366f9" }, + { + "path": "wordpress/wp-includes/blocks/quote/style-rtl.min.css", + "size": 698, + "sha256": "a6730c0693cef057f7f1491875c2b467b5742fa3a383cbd68e738d24410bfb57" + }, { "path": "wordpress/wp-includes/blocks/quote/style.css", "size": 765, "sha256": "1ef9387c601a42e7670e2d5b8f633d7fd988fa478f984fdf6dc58f3ef1924414" }, + { + "path": "wordpress/wp-includes/blocks/quote/style.min.css", + "size": 699, + "sha256": "64b45ce7e07ddda5425b8e14c38b5cde9ded12764b1b13a3aa60fffdc34addbd" + }, + { + "path": "wordpress/wp-includes/blocks/quote/theme-rtl.min.css", + "size": 495, + "sha256": "31278ff95033fca46d9cfb4c660ae6bdacf5a473bfee4969e64e0163af46a354" + }, + { + "path": "wordpress/wp-includes/blocks/quote/theme.min.css", + "size": 492, + "sha256": "a8297149e2638718ce90a6625f35608c38efba0873709f803e044c93caa68d62" + }, { "path": "wordpress/wp-includes/blocks/read-more.php", "size": 1830, @@ -4186,11 +5296,21 @@ "size": 1304, "sha256": "d3463857ed8cf4f81b10131b77a65816759b6e998828131881570f26c36f79ca" }, + { + "path": "wordpress/wp-includes/blocks/read-more/style-rtl.min.css", + "size": 281, + "sha256": "0c33217859ff3f32b53de5ec5e26c9ab04463c4ecbc5cf6f05334c8f8e8684d9" + }, { "path": "wordpress/wp-includes/blocks/read-more/style.css", "size": 309, "sha256": "ab266f741297164cdd48c300dabb8b28ac90ad0aea107d8fd69a80941af6f0f3" }, + { + "path": "wordpress/wp-includes/blocks/read-more/style.min.css", + "size": 281, + "sha256": "0c33217859ff3f32b53de5ec5e26c9ab04463c4ecbc5cf6f05334c8f8e8684d9" + }, { "path": "wordpress/wp-includes/blocks/require-dynamic-blocks.php", "size": 4978, @@ -4211,11 +5331,31 @@ "size": 1447, "sha256": "62cebfa01ba00823a04fe97c6816c9a4eb06099e1bbb93da5fca2a9bf0302aa4" }, + { + "path": "wordpress/wp-includes/blocks/rss/editor-rtl.min.css", + "size": 176, + "sha256": "b73516d2d13afb3e6d1211d1ed60bdea5935440a3f8ade6084f99506bfef253b" + }, + { + "path": "wordpress/wp-includes/blocks/rss/editor.min.css", + "size": 176, + "sha256": "b73516d2d13afb3e6d1211d1ed60bdea5935440a3f8ade6084f99506bfef253b" + }, + { + "path": "wordpress/wp-includes/blocks/rss/style-rtl.min.css", + "size": 652, + "sha256": "bab0c671cbb9cf43964d243414cd01efa78acdfd9b963d4529deb218798d266d" + }, { "path": "wordpress/wp-includes/blocks/rss/style.css", "size": 2587, "sha256": "f7212946b6975af4537d873e0a4254cf68d4a9724edc45c359fd3161514f6917" }, + { + "path": "wordpress/wp-includes/blocks/rss/style.min.css", + "size": 652, + "sha256": "81a8ec4131de719fe46f74b0c619c1bfad324baef9eb043b1882e4716ee68684" + }, { "path": "wordpress/wp-includes/blocks/search.php", "size": 23817, @@ -4226,21 +5366,81 @@ "size": 2038, "sha256": "0e44d6c23d8d2919a23b368b1ec9c752a1b55617e1d96a5c8a9bf9d621327f05" }, + { + "path": "wordpress/wp-includes/blocks/search/editor-rtl.min.css", + "size": 326, + "sha256": "9e0c4870b5b588b6771fca210f183ae477e4e704b22014875f667d527ba5e2e8" + }, + { + "path": "wordpress/wp-includes/blocks/search/editor.min.css", + "size": 326, + "sha256": "9e0c4870b5b588b6771fca210f183ae477e4e704b22014875f667d527ba5e2e8" + }, + { + "path": "wordpress/wp-includes/blocks/search/style-rtl.min.css", + "size": 2390, + "sha256": "168a8df99aacc03b58a07c457e33ab403380d034d31425aa10952ab44956646a" + }, { "path": "wordpress/wp-includes/blocks/search/style.css", "size": 3573, "sha256": "cddd940a20ec40a6bc1f1639a1fb987d5d29e32964fa068108560f93a2948531" }, + { + "path": "wordpress/wp-includes/blocks/search/style.min.css", + "size": 2389, + "sha256": "df6e38a94626af42752ed12f25de7c55f17ae729893490902c55817c5d550efe" + }, + { + "path": "wordpress/wp-includes/blocks/search/theme-rtl.min.css", + "size": 126, + "sha256": "65d0b0f4c02d95b7b7a3eea7473b9ce5fa03140fa611e68ed27275c7011b584b" + }, + { + "path": "wordpress/wp-includes/blocks/search/theme.min.css", + "size": 126, + "sha256": "65d0b0f4c02d95b7b7a3eea7473b9ce5fa03140fa611e68ed27275c7011b584b" + }, { "path": "wordpress/wp-includes/blocks/separator/block.json", "size": 1146, "sha256": "4ce92b86014172f7a64b34413a2ab2edf4b70dcc506234c8080aa4cdecb6127b" }, + { + "path": "wordpress/wp-includes/blocks/separator/editor-rtl.min.css", + "size": 97, + "sha256": "6a3757a9af00c0d33685240ede15011065e7c1df7e5249f5d1e57084257a1d50" + }, + { + "path": "wordpress/wp-includes/blocks/separator/editor.min.css", + "size": 97, + "sha256": "6a3757a9af00c0d33685240ede15011065e7c1df7e5249f5d1e57084257a1d50" + }, + { + "path": "wordpress/wp-includes/blocks/separator/style-rtl.min.css", + "size": 405, + "sha256": "341ddd8c8560a0e964be5dfbec80f7fce2bf5f360bb5dad886620a901fd480f7" + }, { "path": "wordpress/wp-includes/blocks/separator/style.css", "size": 558, "sha256": "f6df036d7b1ecd466096904f74e77c3a4c2ddb3a13db1525e8790036629dde44" }, + { + "path": "wordpress/wp-includes/blocks/separator/style.min.css", + "size": 405, + "sha256": "341ddd8c8560a0e964be5dfbec80f7fce2bf5f360bb5dad886620a901fd480f7" + }, + { + "path": "wordpress/wp-includes/blocks/separator/theme-rtl.min.css", + "size": 437, + "sha256": "0c6438dfff9c2ee9b863aaf7ccc21c4f40cebf35c23f6f43bef165f85f62e62a" + }, + { + "path": "wordpress/wp-includes/blocks/separator/theme.min.css", + "size": 437, + "sha256": "0c6438dfff9c2ee9b863aaf7ccc21c4f40cebf35c23f6f43bef165f85f62e62a" + }, { "path": "wordpress/wp-includes/blocks/shortcode.php", "size": 735, @@ -4251,6 +5451,16 @@ "size": 531, "sha256": "df8a0168d38b39c78830e78f8a7b19458eee7a92569796a6a4dfbd6b78b17ccf" }, + { + "path": "wordpress/wp-includes/blocks/shortcode/editor-rtl.min.css", + "size": 577, + "sha256": "e32da515edffcf9738a17a658ced98d64f38413cddff71db3bfb923a194560a3" + }, + { + "path": "wordpress/wp-includes/blocks/shortcode/editor.min.css", + "size": 577, + "sha256": "e32da515edffcf9738a17a658ced98d64f38413cddff71db3bfb923a194560a3" + }, { "path": "wordpress/wp-includes/blocks/site-logo.php", "size": 6334, @@ -4261,11 +5471,31 @@ "size": 1500, "sha256": "256b7bde9287e86c1a796d55734ccd65452542b47d15f211bc5c587f0be5dccd" }, + { + "path": "wordpress/wp-includes/blocks/site-logo/editor-rtl.min.css", + "size": 2286, + "sha256": "22bffe8f32d74b334b0a1b7d4ba26bd03bf8ed00683c16f829bd1be0750c2782" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/editor.min.css", + "size": 2286, + "sha256": "cf27be4ad6a6377192a2113ca535528abe3dcf94589d0b192fcef020f9cddd21" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/style-rtl.min.css", + "size": 453, + "sha256": "88bfd9470ef9542b8418b116f1f99c732224deeee305a4e7b44ed59bd623ce02" + }, { "path": "wordpress/wp-includes/blocks/site-logo/style.css", "size": 534, "sha256": "3bad81efcf5dba56449a58602c31624570143bf96e9e126fd60aa60cb73bc2f5" }, + { + "path": "wordpress/wp-includes/blocks/site-logo/style.min.css", + "size": 453, + "sha256": "88bfd9470ef9542b8418b116f1f99c732224deeee305a4e7b44ed59bd623ce02" + }, { "path": "wordpress/wp-includes/blocks/site-tagline.php", "size": 1203, @@ -4276,11 +5506,31 @@ "size": 1688, "sha256": "280bd79b2b67c4c80d6ca61a7a98e6c33d7b11725ee73e33796b2b62975530bf" }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/editor-rtl.min.css", + "size": 68, + "sha256": "7e64dc70968924991c1404a753fe55ec082f8e46b119592c1a03b876d8f3b81c" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/editor.min.css", + "size": 68, + "sha256": "7e64dc70968924991c1404a753fe55ec082f8e46b119592c1a03b876d8f3b81c" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/style-rtl.min.css", + "size": 45, + "sha256": "84f0671a6687c88461d6c4c293fbb50978b91ff054e6b104f14410263fd73cfa" + }, { "path": "wordpress/wp-includes/blocks/site-tagline/style.css", "size": 52, "sha256": "0231f5f37c6f7d0c8cfc3e41352a0e37ce568082529aa9952a938f628da8f8dd" }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/style.min.css", + "size": 45, + "sha256": "84f0671a6687c88461d6c4c293fbb50978b91ff054e6b104f14410263fd73cfa" + }, { "path": "wordpress/wp-includes/blocks/site-title.php", "size": 1864, @@ -4291,11 +5541,31 @@ "size": 1797, "sha256": "d55c4519c96203156623fbca64fbba8744254e2ed2ee9772f19e84f39b75f2d1" }, + { + "path": "wordpress/wp-includes/blocks/site-title/editor-rtl.min.css", + "size": 66, + "sha256": "8c94f3a5649721272d9437f79a2415e1e0c879e02100c4e90d9ff12cc59c53ae" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/editor.min.css", + "size": 66, + "sha256": "8c94f3a5649721272d9437f79a2415e1e0c879e02100c4e90d9ff12cc59c53ae" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/style-rtl.min.css", + "size": 232, + "sha256": "eebbf4533110d92d4d5dbb60284b6476db0ac9689191885cad2c90ce2eba01e2" + }, { "path": "wordpress/wp-includes/blocks/site-title/style.css", "size": 275, "sha256": "9d899520dea0df0c3f1dfd86a13ddda45f010c7a00a82f5596effd35983e4083" }, + { + "path": "wordpress/wp-includes/blocks/site-title/style.min.css", + "size": 232, + "sha256": "eebbf4533110d92d4d5dbb60284b6476db0ac9689191885cad2c90ce2eba01e2" + }, { "path": "wordpress/wp-includes/blocks/social-link.php", "size": 67602, @@ -4306,36 +5576,116 @@ "size": 822, "sha256": "ada66d26623f10e9fcd4a5ae99305931e51e6d65e8e58494e9c7d8a42ea6c8ac" }, + { + "path": "wordpress/wp-includes/blocks/social-link/editor-rtl.min.css", + "size": 636, + "sha256": "cbfddb6f1e32670c0b88ede84761ab41182f7f8be8a3993ad754829c8cbfa0e2" + }, + { + "path": "wordpress/wp-includes/blocks/social-link/editor.min.css", + "size": 636, + "sha256": "cbfddb6f1e32670c0b88ede84761ab41182f7f8be8a3993ad754829c8cbfa0e2" + }, { "path": "wordpress/wp-includes/blocks/social-links/block.json", "size": 2403, "sha256": "46fb82a9096a1b61530a490df7402b65b043f3e6eda9adff156060b8dcc515c9" }, + { + "path": "wordpress/wp-includes/blocks/social-links/editor-rtl.min.css", + "size": 882, + "sha256": "31ff5706ecddd7dbc40adb19e791d8675283873c871a97b805838c271480427f" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/editor.min.css", + "size": 881, + "sha256": "16c0b0b7b2de843afa915229547868e33ea9d63d143d35111ac631a8ff58c08e" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/style-rtl.min.css", + "size": 11763, + "sha256": "23f2631f3c37db5bda4844da898a31caf818b418fca050ddd76316b98dbaffdb" + }, { "path": "wordpress/wp-includes/blocks/social-links/style.css", "size": 12982, "sha256": "f0a9b59e1800da133b39ba933df08417364762cc29f5bb916453b2573584b1fb" }, + { + "path": "wordpress/wp-includes/blocks/social-links/style.min.css", + "size": 11762, + "sha256": "553d6c5803709a741e04e02dba67a54a614ebc63539c488823baf92cd7b4fefd" + }, { "path": "wordpress/wp-includes/blocks/spacer/block.json", "size": 677, "sha256": "47b184ba081ebe8bf94626dd8cf53a83de3dcb0d2367c4ca1dd98e4860c45631" }, + { + "path": "wordpress/wp-includes/blocks/spacer/editor-rtl.min.css", + "size": 948, + "sha256": "86f2883d0e756b68373bfd6f9df4e48e71c0f09de7c1923f61b50313833817b7" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/editor.min.css", + "size": 948, + "sha256": "86f2883d0e756b68373bfd6f9df4e48e71c0f09de7c1923f61b50313833817b7" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/style-rtl.min.css", + "size": 28, + "sha256": "7532ff54d8da7a22bb84edffcf2d67be89b6265dcc56db136ab295244f3e1f8b" + }, { "path": "wordpress/wp-includes/blocks/spacer/style.css", "size": 35, "sha256": "d622c5eb5ee6345a9c554a9cc91fa56bc543839f2a48e40f6f0b814bea428f8b" }, + { + "path": "wordpress/wp-includes/blocks/spacer/style.min.css", + "size": 28, + "sha256": "7532ff54d8da7a22bb84edffcf2d67be89b6265dcc56db136ab295244f3e1f8b" + }, { "path": "wordpress/wp-includes/blocks/table/block.json", "size": 4508, "sha256": "40a9197d177d02d46143b84453e057afbaf7d9d4898fe4592329b64d6babd06f" }, + { + "path": "wordpress/wp-includes/blocks/table/editor-rtl.min.css", + "size": 1495, + "sha256": "945b73bfafa8389394025893ac8c1162a9e8fb5fcb2c0d14a70dad15b982c3ff" + }, + { + "path": "wordpress/wp-includes/blocks/table/editor.min.css", + "size": 1495, + "sha256": "945b73bfafa8389394025893ac8c1162a9e8fb5fcb2c0d14a70dad15b982c3ff" + }, + { + "path": "wordpress/wp-includes/blocks/table/style-rtl.min.css", + "size": 3888, + "sha256": "51674c258f5b613b6d7d80ee2966ec330557ab33467327738668dffa645bcdbe" + }, { "path": "wordpress/wp-includes/blocks/table/style.css", "size": 5080, "sha256": "a386d9d775c2c00b8ea7208ec2df1d4de91aa5a658be975da0770af7bce006b4" }, + { + "path": "wordpress/wp-includes/blocks/table/style.min.css", + "size": 3888, + "sha256": "bb5bd8340a93b27f6075624a94b977ef43cc360859c8b8d30d26a6a73ca37613" + }, + { + "path": "wordpress/wp-includes/blocks/table/theme-rtl.min.css", + "size": 232, + "sha256": "0a161230525a2d7528eac14c1e5253b8d631d7e787a183733f01fa6670945e57" + }, + { + "path": "wordpress/wp-includes/blocks/table/theme.min.css", + "size": 232, + "sha256": "0a161230525a2d7528eac14c1e5253b8d631d7e787a183733f01fa6670945e57" + }, { "path": "wordpress/wp-includes/blocks/tag-cloud.php", "size": 1590, @@ -4346,11 +5696,21 @@ "size": 1421, "sha256": "a5fe7791e8f1cc1f23c499c9fedd0f613914de71ba3568ffabfeea780b1d672f" }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud/style-rtl.min.css", + "size": 502, + "sha256": "828211200038113cf4f587100c24628656ad3eabfc35f9fa9c0c0421610c7f60" + }, { "path": "wordpress/wp-includes/blocks/tag-cloud/style.css", "size": 607, "sha256": "b67fce56649938d490d5f8df5884e4a4d6114d954469b74d08cc5f105a30f3a7" }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud/style.min.css", + "size": 503, + "sha256": "14593e359e5efdf71c634319f30796b5a69ff4c4922dd19c1a9453ea12052087" + }, { "path": "wordpress/wp-includes/blocks/template-part.php", "size": 10446, @@ -4361,6 +5721,26 @@ "size": 672, "sha256": "4f0be35529b24372d70c636cb7a251bdd6dac654020bd9d9a25cab34a66946b1" }, + { + "path": "wordpress/wp-includes/blocks/template-part/editor-rtl.min.css", + "size": 1360, + "sha256": "afb251f1761ff388c937ce4b1c2ed969d527ddac7867cab98dce051e3e7f0ac8" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/editor.min.css", + "size": 1360, + "sha256": "afb251f1761ff388c937ce4b1c2ed969d527ddac7867cab98dce051e3e7f0ac8" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/theme-rtl.min.css", + "size": 105, + "sha256": "85b366f1200d3a58cf06d32173c73c7704da73562134c789656c58f0a7e81a17" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/theme.min.css", + "size": 105, + "sha256": "85b366f1200d3a58cf06d32173c73c7704da73562134c789656c58f0a7e81a17" + }, { "path": "wordpress/wp-includes/blocks/term-count.php", "size": 1843, @@ -4371,11 +5751,21 @@ "size": 1298, "sha256": "0e2b3d58fcf3f49558875c34cfe999ebdd5c00bc2fd365d9bb9208376815433b" }, + { + "path": "wordpress/wp-includes/blocks/term-count/style-rtl.min.css", + "size": 43, + "sha256": "db8513c7798a8fbfc6119fb5be7a90c418ddab94b51a188859bc4d1743134ad4" + }, { "path": "wordpress/wp-includes/blocks/term-count/style.css", "size": 50, "sha256": "cd61e48f387bdf1c4e76beb5017b6386c9d3389c9c9059c344a86285f56fd590" }, + { + "path": "wordpress/wp-includes/blocks/term-count/style.min.css", + "size": 43, + "sha256": "db8513c7798a8fbfc6119fb5be7a90c418ddab94b51a188859bc4d1743134ad4" + }, { "path": "wordpress/wp-includes/blocks/term-description.php", "size": 1770, @@ -4386,11 +5776,21 @@ "size": 1264, "sha256": "59d0ac9a7b4ead4f12f2c3dc45a9d414813d2f786ce092005e2f6891eb4d7602" }, + { + "path": "wordpress/wp-includes/blocks/term-description/style-rtl.min.css", + "size": 196, + "sha256": "e31d1c947396c1c9ab41cbc079af8664e673c83970bdfeb50cb4223e09620128" + }, { "path": "wordpress/wp-includes/blocks/term-description/style.css", "size": 224, "sha256": "f23691c6b81750187694528332d2fa626d3e98a8126eae658f81ca715d12a966" }, + { + "path": "wordpress/wp-includes/blocks/term-description/style.min.css", + "size": 196, + "sha256": "e31d1c947396c1c9ab41cbc079af8664e673c83970bdfeb50cb4223e09620128" + }, { "path": "wordpress/wp-includes/blocks/term-name.php", "size": 2038, @@ -4401,11 +5801,21 @@ "size": 1461, "sha256": "0c277931f34fbb3ec24a36e83512b78eb70031cf04b3547b5bdb1eee49cb42ed" }, + { + "path": "wordpress/wp-includes/blocks/term-name/style-rtl.min.css", + "size": 42, + "sha256": "2ae714bef79076276fc834ed507b2b6034424cbb9255d3bb9abe3b5bf653b99d" + }, { "path": "wordpress/wp-includes/blocks/term-name/style.css", "size": 49, "sha256": "87a73237e41b708e51c70f7f6149331f184a055677f53d1d45c369d0dd17a226" }, + { + "path": "wordpress/wp-includes/blocks/term-name/style.min.css", + "size": 42, + "sha256": "2ae714bef79076276fc834ed507b2b6034424cbb9255d3bb9abe3b5bf653b99d" + }, { "path": "wordpress/wp-includes/blocks/term-template.php", "size": 4518, @@ -4416,11 +5826,31 @@ "size": 1466, "sha256": "aeeab09a60449dedd1d7f79ddb9cb49de5048213e1bef505821226fc5ea1ae34" }, + { + "path": "wordpress/wp-includes/blocks/term-template/editor-rtl.min.css", + "size": 367, + "sha256": "0b774f57c5f4064056e360c21ad1f2c936c77c51ae8cfed16f6ed241f5fbac99" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/editor.min.css", + "size": 367, + "sha256": "0b774f57c5f4064056e360c21ad1f2c936c77c51ae8cfed16f6ed241f5fbac99" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/style-rtl.min.css", + "size": 116, + "sha256": "6a2e3afdc9e2cac94f9128283ecf4981972dc703dd40a0c747cbd374a8a40184" + }, { "path": "wordpress/wp-includes/blocks/term-template/style.css", "size": 143, "sha256": "416f2fcdbcd6f85aae3dae35aa56d52526e0109bf6d42047ab21c98d45ab4b34" }, + { + "path": "wordpress/wp-includes/blocks/term-template/style.min.css", + "size": 116, + "sha256": "6a2e3afdc9e2cac94f9128283ecf4981972dc703dd40a0c747cbd374a8a40184" + }, { "path": "wordpress/wp-includes/blocks/terms-query/block.json", "size": 923, @@ -4431,21 +5861,51 @@ "size": 784, "sha256": "436202addcbe99d429d86755a5f379b0fe1db2c450945902f522243baa44852b" }, + { + "path": "wordpress/wp-includes/blocks/text-columns/editor-rtl.min.css", + "size": 86, + "sha256": "9bbe4433e6b7a99b205e25e6b463337fbdaf22470e64716f501a535b201a29e4" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/editor.min.css", + "size": 86, + "sha256": "9bbe4433e6b7a99b205e25e6b463337fbdaf22470e64716f501a535b201a29e4" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/style-rtl.min.css", + "size": 457, + "sha256": "d675326833c1abcda9ea106e1760d84ba2fafe0a2878beddbfc3c482129cc484" + }, { "path": "wordpress/wp-includes/blocks/text-columns/style.css", "size": 537, "sha256": "8502e85cf12eae0cdb5b9521f342eff9d254b61890f4ea063230ef27ff74431c" }, + { + "path": "wordpress/wp-includes/blocks/text-columns/style.min.css", + "size": 457, + "sha256": "cc892c13b7f850fb6e7a14eaa727e96172c4d65924b13aadb240f63bfb09abb4" + }, { "path": "wordpress/wp-includes/blocks/verse/block.json", "size": 1728, "sha256": "674e5ebbd6328629c006c2e2fa18399b7c92a5917349f4844d7370e418dcb5ac" }, + { + "path": "wordpress/wp-includes/blocks/verse/style-rtl.min.css", + "size": 159, + "sha256": "ecc3099a1ebafbe1e6456998e8be7cb95d1cf576bc20ebab0c08f66e3d1cddf9" + }, { "path": "wordpress/wp-includes/blocks/verse/style.css", "size": 191, "sha256": "716c59cff6b649296364afef015f917aa99832aa0b1d3d4d6f58540174af0155" }, + { + "path": "wordpress/wp-includes/blocks/verse/style.min.css", + "size": 159, + "sha256": "ecc3099a1ebafbe1e6456998e8be7cb95d1cf576bc20ebab0c08f66e3d1cddf9" + }, { "path": "wordpress/wp-includes/blocks/video.php", "size": 2733, @@ -4456,11 +5916,41 @@ "size": 1964, "sha256": "932b32d9ec75bbfd5afd1aeb8c8d55987ae3b9e2900a1ad4545c6747b660b355" }, + { + "path": "wordpress/wp-includes/blocks/video/editor-rtl.min.css", + "size": 1112, + "sha256": "ae3521e00582c0853feecebabba3b9872a5844fe8ccd23be4a1f24b5bd2411a6" + }, + { + "path": "wordpress/wp-includes/blocks/video/editor.min.css", + "size": 1109, + "sha256": "c4e0f6c5ccdc991a2a8a826596932c451e09eef3b184f6d5913d76dc347f98a3" + }, + { + "path": "wordpress/wp-includes/blocks/video/style-rtl.min.css", + "size": 291, + "sha256": "5d02b68b7c929c457ff633daa86ab577581bf35504adab59fa31c58aabdfd719" + }, { "path": "wordpress/wp-includes/blocks/video/style.css", "size": 2092, "sha256": "b673f8c6c9b931f78e5221bc297309f2be1c5745f82ee4c9acd4600cac326b93" }, + { + "path": "wordpress/wp-includes/blocks/video/style.min.css", + "size": 291, + "sha256": "5d02b68b7c929c457ff633daa86ab577581bf35504adab59fa31c58aabdfd719" + }, + { + "path": "wordpress/wp-includes/blocks/video/theme-rtl.min.css", + "size": 176, + "sha256": "8effbc651a2a28bb0ac10daffae3347ff2eac1d352d4c0a85aed235e9a5037a5" + }, + { + "path": "wordpress/wp-includes/blocks/video/theme.min.css", + "size": 176, + "sha256": "8effbc651a2a28bb0ac10daffae3347ff2eac1d352d4c0a85aed235e9a5037a5" + }, { "path": "wordpress/wp-includes/blocks/widget-group.php", "size": 2438, diff --git a/packages/runtime-cloudflare/package.json b/packages/runtime-cloudflare/package.json index d0b7e661..c7dd4604 100644 --- a/packages/runtime-cloudflare/package.json +++ b/packages/runtime-cloudflare/package.json @@ -5,9 +5,9 @@ "type": "module", "dependencies": { "@automattic/wp-codebox-core": "file:../runtime-core", - "@php-wasm/stream-compression": "^3.1.45", - "@php-wasm/universal": "3.1.45", - "@php-wasm/web-8-5": "3.1.45", - "@wp-playground/wordpress": "3.1.45" + "@php-wasm/stream-compression": "3.1.45", + "@php-wasm/universal": "3.1.46", + "@php-wasm/web-8-5": "3.1.46", + "@wp-playground/wordpress": "3.1.46" } } diff --git a/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts b/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts index b3887c59..3817a23a 100644 --- a/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts +++ b/packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts @@ -1,7 +1,7 @@ import { isWordPressRuntimeFile } from "./wordpress-runtime-corpus.js" export const WORDPRESS_RUNTIME_ARTIFACT_SCHEMA = "wp-codebox/wordpress-runtime-artifact/v1" -export const WORDPRESS_RUNTIME_MAX_FILES = 2_000 +export const WORDPRESS_RUNTIME_MAX_FILES = 2_200 export const WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES = 24 * 1024 * 1024 export const WORDPRESS_RUNTIME_MAX_ARCHIVE_BYTES = 8 * 1024 * 1024 export const WORDPRESS_RUNTIME_MAX_FILE_BYTES = 8 * 1024 * 1024 diff --git a/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts b/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts index 80077dd0..1713ee9b 100644 --- a/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts +++ b/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts @@ -6,6 +6,7 @@ export interface WordPressArchiveEntry { const SERVER_READ_EXTENSION = /\.(?:php|json|crt|html)$/ const STATIC_ASSET_EXTENSION = /\.(?:css|js|mjs|woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/ +const CORE_BLOCK_REGISTRATION_ASSET = /^wordpress\/wp-includes\/blocks\/.*\.min\.(?:css|js)$/ const STATIC_ARCHIVE_ROOTS = ["wp-admin/", "wp-includes/", "wp-content/themes/"] const SERVER_READ_ASSET_PATHS = new Set([ // wp-includes/view-transitions.php in current WordPress reads this at bootstrap. @@ -18,9 +19,9 @@ const SERVER_READ_ASSET_PATHS = new Set([ export function isWordPressRuntimeFile(path: string, _archivePaths: ReadonlySet): boolean { if (!path.startsWith("wordpress/") || path.endsWith("/") || path.endsWith(".map")) return false - // PHP discovers all executable code and structured metadata server-side. Browser - // assets stay in the archive and are fetched directly by the entry Worker. - return SERVER_READ_EXTENSION.test(path) || path.endsWith("/style.css") || SERVER_READ_ASSET_PATHS.has(path) + // PHP discovers executable code, structured metadata, and minified core block + // assets server-side. Browser requests still fetch those assets from the archive. + return SERVER_READ_EXTENSION.test(path) || path.endsWith("/style.css") || CORE_BLOCK_REGISTRATION_ASSET.test(path) || SERVER_READ_ASSET_PATHS.has(path) } export function wordpressStaticArchivePath(pathname: string): string | null { diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 951ec1a3..e1999905 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -161,6 +161,7 @@ async function secretsMatch(left: string, right: string): Promise { async function runCoordinatedWordPressRequest(request: Request, env: Env, coordinator: DurableObjectStub, route: "wordpress" | "health" | "r2-mutate"): Promise { if (route === "r2-mutate" && request.method !== "POST") return new Response("WordPress state mutation requires POST.", { status: 405 }) let lease = await acquireLease(coordinator, request.url) + let runtime: Runtime | undefined let finalized = false try { if (!lease.pointer) { @@ -170,7 +171,8 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi if (!lease.pointer || lease.pointer.revision !== bootstrapped.pointer.revision) throw new Error("Canonical bootstrap promotion was not observed by its next lease.") cacheRuntime(lease.pointer, bootstrapped) } - const runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) + runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) + const mutatesCanonicalState = isMutation(request, route) let response: Response let canonicalChanges: MarkdownChanges | undefined if (route === "r2-mutate") { @@ -180,11 +182,11 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi } else if (route === "health") { response = await health(runtime) } else { - if (isMutation(request, route)) runtime.php.writeFile(MARKDOWN_CHANGES_PATH, new TextEncoder().encode(JSON.stringify({ created: [], changed: [], deleted: [] }))) + if (mutatesCanonicalState) runtime.php.writeFile(MARKDOWN_CHANGES_PATH, new TextEncoder().encode(JSON.stringify({ created: [], changed: [], deleted: [] }))) response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) - if (isMutation(request, route)) canonicalChanges = readCanonicalChanges(runtime.php) + if (mutatesCanonicalState) canonicalChanges = readCanonicalChanges(runtime.php) } - if (isMutation(request, route)) { + if (mutatesCanonicalState) { if (!canonicalChanges) throw new Error("Canonical mutation completed without an MDI change set.") const next = await persistRuntime(env.WORDPRESS_STATE_BUCKET, runtime, canonicalChanges) await commitLease(coordinator, request.url, lease, next) @@ -192,10 +194,11 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi await releaseLease(coordinator, request.url, lease) } finalized = true - await discardRuntime(runtime) + if (mutatesCanonicalState) await discardRuntime(runtime) return response } catch (error) { if (!finalized) await abortLease(coordinator, request.url, lease) + if (runtime) await discardRuntime(runtime) throw error } } diff --git a/scripts/build-cloudflare-canonical-mdi-seed.php b/scripts/build-cloudflare-canonical-mdi-seed.php index 27f28cb7..ed5f65a5 100644 --- a/scripts/build-cloudflare-canonical-mdi-seed.php +++ b/scripts/build-cloudflare-canonical-mdi-seed.php @@ -59,68 +59,96 @@ function cloudflare_seed_files( string $root ): array { $front_page_content = <<<'HTML'
- -

WordPress at the edge, with durable Markdown state

+ +
+ + + +
+
+

Runtime
PHP 8.5 WebAssembly

+
+ +
+

Application
WordPress 7.0

+
+ +
+

Durable state
Markdown and JSON in R2

+
+ +
+

Coordination
Cloudflare Durable Object

+
+
+ + -

Architecture flow

+

Follow a request

+ + + +
    +
  1. The Worker acquires a short lease from a Durable Object, which serializes requests against the current site revision.
  2. + +
  3. A cold isolate downloads the content-addressed WordPress server corpus and the current canonical revision from R2.
  4. + +
  5. PHP-WASM reconstructs disposable SQLite query state, then WordPress handles the request normally. Warm reads reuse that pointer-scoped runtime.
  6. + +
  7. Browser CSS, JavaScript, fonts, and images bypass PHP and stream from bounded archive reads at the edge.
  8. + +
  9. After a write, Markdown Database Integration flushes canonical changes to immutable R2 objects and the Durable Object atomically advances the revision pointer.
  10. +
+ + + +
+ + + +

The durability boundary

-
-
-

Edge request

- -

WordPress/PHP WebAssembly boots for the request.

-
-
- - - -
-

- -
-

Disposable SQLite

+
+
+

Durable

-

SQLite is reconstructed as query state, not the durable source of truth.

+

Posts, pages, options, users, and metadata are exported as canonical Markdown and JSON. Immutable objects and revision manifests live in R2.

- - - -
-

- -
-

Canonical state

+ +
+
+

Disposable

-

Markdown Database Integration exports canonical Markdown and JSON into immutable, content-addressed R2 objects and revision manifests.

-
-
- - - -
-

- -
-

Serialized writes

- -

A Durable Object serializes writes and atomically advances the current revision. A cold runtime hydrates that manifest and reconstructs SQLite.

+

SQLite and the PHP filesystem are reconstructed runtime state. A Worker isolate can disappear without becoming the source of truth.

-

Verified capabilities

+

What this deployment proves

@@ -131,25 +159,29 @@ function cloudflare_seed_files( string $root ): array {
  • Render the published page publicly.
  • -
  • Recover the site after a cold start from the current revision manifest.
  • +
  • Recover the same published state after replacing the Worker runtime.
  • -

    Bounded by design

    +

    Current operating envelope

    • One site namespace is currently configured.
    • -
    • Requests for a site are serialized while a write lease is held.
    • +
    • Dynamic requests are serialized against one canonical revision.
    • -
    • Each dynamic request currently pays PHP boot cost.
    • +
    • Cold isolates pay reconstruction cost; warm read requests reuse the current PHP runtime.
    • -
    • Cold recovery hydrates the full revision manifest.
    • +
    • Static browser assets are cached independently and never enter PHP request handling.
    + + +

    This is an experimental deployment from Automattic/wp-codebox#1886. Its public behavior is exercised through deployment, authentication, publishing, asset, restart, and canonical-state recovery gates.

    +
    HTML; diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 3ddd469d..4b73486a 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -18,6 +18,13 @@ try { await run("npm", ["run", "provision:cloudflare-wordpress-runtime-corpus", "--", "--local", "--persist-to", stateDirectory]) await startWorker() await assertConcurrentMutations() + const coldHome = await timedWordPressPage(origin, "cold explanatory homepage") + const warmHome = await timedWordPressPage(origin, "warm explanatory homepage") + await assertExplanatoryHomepage(warmHome.body) + if (warmHome.elapsedMs >= coldHome.elapsedMs || warmHome.elapsedMs > 2_000) { + throw new Error(`Warm explanatory homepage did not reuse the canonical runtime: cold=${coldHome.elapsedMs}ms warm=${warmHome.elapsedMs}ms.`) + } + console.log(`Explanatory homepage timing: cold=${coldHome.elapsedMs}ms warm=${warmHome.elapsedMs}ms.`) const adminHtml = await login() const editorHtml = await assertPostNewEditor() const post = await createPost(adminHtml) @@ -37,7 +44,7 @@ try { await assertLinkedAssets(restartedAdmin, "admin after cold restart") cookies.length = 0 await login() - console.log("Cloudflare local runtime gate passed: login, dashboard, post editor, concurrent canonical mutations, authenticated REST post creation, frontend/admin/editor assets, and cold-restart session persistence.") + console.log("Cloudflare local runtime gate passed: explanatory homepage, complete block styles, warm runtime reuse, login, dashboard, post editor, concurrent canonical mutations, authenticated REST post creation, frontend/admin/editor assets, and cold-restart session persistence.") } finally { await stopWorker() await rm(stateDirectory, { recursive: true, force: true }) @@ -155,6 +162,39 @@ async function assertWordPressPage(target, label) { return body } +async function timedWordPressPage(target, label) { + const startedAt = performance.now() + const body = await assertWordPressPage(target, label) + return { body, elapsedMs: Math.round(performance.now() - startedAt) } +} + +async function assertExplanatoryHomepage(html) { + for (const text of [ + "Cloudflare WordPress Runtime", + "WordPress at the edge, with durable Markdown state", + "Follow a request", + "The durability boundary", + "What this deployment proves", + "Current operating envelope", + ]) assertIncludes(html, text, "explanatory homepage") + const inlineCss = [...html.matchAll(/]*>([\s\S]*?)<\/style>/gi)].map((match) => match[1]).join("\n") + const stylesheetUrls = [...html.matchAll(/]*?\brel=["'][^"']*stylesheet[^"']*["'][^>]*?\bhref=["']([^"']+)["']|]*?\bhref=["']([^"']+)["'][^>]*?\brel=["'][^"']*stylesheet/gi)].map((match) => match[1] || match[2]) + const linkedCss = [] + for (const stylesheet of stylesheetUrls) { + const response = await request(new URL(stylesheet, origin)) + const body = await response.text() + if (!response.ok || !response.headers.get("content-type")?.includes("text/css")) throw new Error(`Explanatory homepage stylesheet failed: ${stylesheet} (${response.status}).`) + linkedCss.push(body) + } + const css = `${inlineCss}\n${linkedCss.join("\n")}` + for (const [block, signature] of [ + ["list", /ol,ul\{box-sizing:border-box\}/], + ["columns", /\.wp-block-columns\{align-items:normal/], + ["buttons", /\.wp-block-buttons\{box-sizing:border-box/], + ["navigation", /\.wp-block-navigation\{position:relative/], + ]) if (!signature.test(css)) throw new Error(`Explanatory homepage omitted the core ${block} block stylesheet.`) +} + async function assertAuthenticatedDashboard(target) { const response = await request(target) const body = await response.text() diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 1b833baf..4187afde 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -60,10 +60,12 @@ test("Cloudflare serves only safe browser assets from the WordPress archive", () assert.equal(wordpressStaticContentType("wordpress/wp-admin/images/logo.png"), "image/png") }) -test("WordPress boot corpus excludes browser assets while retaining server metadata", () => { +test("WordPress boot corpus retains server metadata and core block registration assets", () => { const paths = new Set() assert.ok(isWordPressRuntimeFile("wordpress/wp-includes/version.php", paths)) assert.ok(isWordPressRuntimeFile("wordpress/wp-includes/blocks/paragraph/block.json", paths)) + assert.ok(isWordPressRuntimeFile("wordpress/wp-includes/blocks/list/style.min.css", paths)) + assert.ok(isWordPressRuntimeFile("wordpress/wp-includes/blocks/navigation/view.min.js", paths)) assert.ok(isWordPressRuntimeFile("wordpress/wp-content/themes/twentytwentyfive/style.css", paths)) assert.ok(isWordPressRuntimeFile("wordpress/wp-admin/css/view-transitions.min.css", paths)) assert.ok(isWordPressRuntimeFile("wordpress/wp-includes/js/wp-emoji-loader.min.js", paths)) @@ -213,7 +215,8 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /let cachedRuntime/) assert.match(worker, /cachedRuntime\.baseRevision !== pointer\.revision/) assert.match(worker, /promise\.catch\(\(\) =>/) - assert.match(worker, /finalized = true\n await discardRuntime\(runtime\)/) + assert.match(worker, /finalized = true\n if \(mutatesCanonicalState\) await discardRuntime\(runtime\)/) + assert.match(worker, /if \(runtime\) await discardRuntime\(runtime\)/) assert.doesNotMatch(worker, /runtime\.pointer = next/) assert.match(worker, /await abortLease\(coordinator, request\.url, lease\)/) assert.match(worker, /const LEASE_ACQUISITION_TIMEOUT_MS = 100_000/) @@ -248,6 +251,7 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(corpus, /path\.endsWith\("\.map"\)/) assert.match(corpus, /SERVER_READ_EXTENSION/) assert.match(corpus, /path\.endsWith\("\/style\.css"\)/) + assert.match(corpus, /CORE_BLOCK_REGISTRATION_ASSET/) assert.match(corpus, /STATIC_ARCHIVE_ROOTS/) }) @@ -424,19 +428,21 @@ test("canonical MDI seed owns the Cloudflare front page and its architecture exp assert.match(files.get("_options/page_on_front.json") ?? "", /"2"/) assert.match(frontPage, /title: Cloudflare WordPress Runtime/) for (const claim of [ - "WordPress/PHP WebAssembly", - "SQLite is reconstructed as query state", + "real WordPress site running PHP as WebAssembly", + "Follow a request", + "content-addressed WordPress server corpus", + "reconstructs disposable SQLite query state", + "Warm reads reuse that pointer-scoped runtime", + "The durability boundary", "canonical Markdown and JSON", - "content-addressed R2 objects and revision manifests", - "Durable Object serializes writes and atomically advances the current revision", - "cold runtime hydrates that manifest and reconstructs SQLite", + "Immutable objects and revision manifests live in R2", "Log in to WordPress", "block editor", "Render the published page publicly", "One site namespace", - "Requests for a site are serialized", - "PHP boot cost", - "full revision manifest", + "Dynamic requests are serialized", + "Cold isolates pay reconstruction cost", + "Static browser assets are cached independently", ]) assert.match(frontPage, new RegExp(claim)) assert.match(frontPage, //) assert.match(frontPage, //) diff --git a/tests/cloudflare-wordpress-archive-corpus.test.ts b/tests/cloudflare-wordpress-archive-corpus.test.ts index 15dac11c..3d77993d 100644 --- a/tests/cloudflare-wordpress-archive-corpus.test.ts +++ b/tests/cloudflare-wordpress-archive-corpus.test.ts @@ -2,9 +2,9 @@ import assert from "node:assert/strict" import test from "node:test" import { decodeZip } from "@php-wasm/stream-compression" import { isWordPressRuntimeFile, summarizeWordPressRuntimeCorpus, type WordPressArchiveEntry } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" +import { WORDPRESS_RUNTIME_MAX_FILES, WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" const WORDPRESS_ARCHIVE_URL = "https://wordpress.org/latest.zip" -const MAX_RUNTIME_CORPUS_BYTES = 32 * 1024 * 1024 test("WordPress production runtime corpus stays within the Worker materialization budget", async () => { const entries: WordPressArchiveEntry[] = [] @@ -38,5 +38,6 @@ test("WordPress production runtime corpus stays within the Worker materializatio })) assert.ok(entries.some((entry) => entry.path === "wordpress/wp-includes/version.php"), "Archive is missing wp-includes/version.php.") assert.ok(corpus.selectedFiles > 0, "WordPress archive did not yield a runtime corpus.") - assert.ok(corpus.selectedBytes <= MAX_RUNTIME_CORPUS_BYTES, `Selected ${corpus.selectedBytes} bytes exceeds the ${MAX_RUNTIME_CORPUS_BYTES}-byte runtime corpus budget.`) + assert.ok(corpus.selectedFiles <= WORDPRESS_RUNTIME_MAX_FILES, `Selected ${corpus.selectedFiles} files exceeds the ${WORDPRESS_RUNTIME_MAX_FILES}-file runtime corpus budget.`) + assert.ok(corpus.selectedBytes <= WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES, `Selected ${corpus.selectedBytes} bytes exceeds the ${WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES}-byte runtime corpus budget.`) }) From 55fba5cfc58a3e4aeb21b7819fa13245eb2fb3e1 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 13:37:19 -0400 Subject: [PATCH 79/87] Add canonical Cloudflare state restore --- .../runtime-cloudflare/src/request-routing.ts | 2 + packages/runtime-cloudflare/src/worker.ts | 41 +++++++++++++++++++ tests/cloudflare-runtime.test.ts | 4 ++ 3 files changed, 47 insertions(+) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 6efe103f..bcb56680 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -4,6 +4,7 @@ export type WorkerRequestRoute = | { kind: "r2-state" } | { kind: "r2-mutate" } | { kind: "operator-reset" } + | { kind: "operator-restore" } | { kind: "probe"; phase: string } export function routeWorkerRequest(request: Request): WorkerRequestRoute { @@ -13,5 +14,6 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "r2-state") return { kind: "r2-state" } if (phase === "r2-mutate") return { kind: "r2-mutate" } if (phase === "operator-reset") return { kind: "operator-reset" } + if (phase === "operator-restore") return { kind: "operator-restore" } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index e1999905..e0309394 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -86,6 +86,7 @@ export default { const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) + if (route.kind === "operator-restore") return restoreCanonicalWordPress(request, env, coordinator) if (route.kind === "probe") { return runBootProbe(route.phase, env.WORDPRESS_STATE_BUCKET) } @@ -148,6 +149,46 @@ async function resetCanonicalWordPress(request: Request, env: Env, coordinator: return response } +async function restoreCanonicalWordPress(request: Request, env: Env, coordinator: DurableObjectStub): Promise { + if (request.method !== "POST") return new Response("Canonical restore requires POST.", { status: 405 }) + const authorization = request.headers.get("authorization") + if (!env.WORDPRESS_OPERATOR_TOKEN || !authorization || !await secretsMatch(authorization, `Bearer ${env.WORDPRESS_OPERATOR_TOKEN}`)) { + return new Response("Canonical restore authorization failed.", { status: 401 }) + } + let pointer: MarkdownPointer + try { + pointer = await request.json() + } catch { + return new Response("Canonical restore requires a JSON pointer.", { status: 400 }) + } + if (!isCanonicalRestorePointer(pointer)) return new Response("Canonical restore pointer is invalid.", { status: 400 }) + const manifest = await readMarkdownManifest(env.WORDPRESS_STATE_BUCKET, pointer) + if (!manifest || manifest.revision !== pointer.revision || manifest.manifestKey !== pointer.manifestKey || manifest.persistedAt !== pointer.persistedAt || !Array.isArray(manifest.files)) { + return new Response("Canonical restore manifest is unavailable or inconsistent.", { status: 409 }) + } + const lease = await acquireLease(coordinator, request.url) + try { + if (lease.pointer) { + await abortLease(coordinator, request.url, lease) + return new Response("Canonical restore requires an empty current pointer.", { status: 409 }) + } + const restored = await commitLease(coordinator, request.url, lease, pointer) + await discardCachedRuntime() + return Response.json({ restored: true, ...restored }) + } catch (error) { + await abortLease(coordinator, request.url, lease) + throw error + } +} + +function isCanonicalRestorePointer(pointer: unknown): pointer is MarkdownPointer { + if (!pointer || typeof pointer !== "object") return false + const candidate = pointer as Partial + return typeof candidate.revision === "string" && /^[a-f0-9-]{36}$/.test(candidate.revision) + && candidate.manifestKey === `sites/default/markdown/revisions/${candidate.revision}.json` + && typeof candidate.persistedAt === "string" && Number.isFinite(Date.parse(candidate.persistedAt)) +} + async function secretsMatch(left: string, right: string): Promise { const encoder = new TextEncoder() const [leftHash, rightHash] = await Promise.all([crypto.subtle.digest("SHA-256", encoder.encode(left)), crypto.subtle.digest("SHA-256", encoder.encode(right))]) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 4187afde..39fe1886 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -42,6 +42,7 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-state")), { kind: "r2-state" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-mutate")), { kind: "r2-mutate" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=operator-reset")), { kind: "operator-reset" }) + assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=operator-restore")), { kind: "operator-restore" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) @@ -235,6 +236,9 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /cookieStore: false/) assert.match(worker, /maxPhpInstances: 1/) assert.match(worker, /WORDPRESS_AUTH_SECRET/) + assert.match(worker, /Canonical restore requires an empty current pointer/) + assert.match(worker, /readMarkdownManifest\(env\.WORDPRESS_STATE_BUCKET, pointer\)/) + assert.match(worker, /sites\/default\/markdown\/revisions\/\$\{candidate\.revision\}\.json/) assert.match(worker, /canonicalWordPressAuthConstants\(env\)/) assert.match(worker, /authConstants/) assert.match(worker, /const staticResponse = await serveWordPressStaticAsset\(request\)/) From 16e324fc981c4b3103cc5d24a5d9caa981e2c975 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 13:49:04 -0400 Subject: [PATCH 80/87] Cache public pages by canonical revision --- packages/runtime-cloudflare/src/worker.ts | 55 +++++++++++++++++++++++ scripts/cloudflare-local-gate.mjs | 13 +++--- tests/cloudflare-runtime.test.ts | 5 +++ 3 files changed, 68 insertions(+), 5 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index e0309394..a2194f0e 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -212,6 +212,12 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi if (!lease.pointer || lease.pointer.revision !== bootstrapped.pointer.revision) throw new Error("Canonical bootstrap promotion was not observed by its next lease.") cacheRuntime(lease.pointer, bootstrapped) } + const cachedPage = route === "wordpress" ? await matchWordPressPageCache(request, lease.pointer) : null + if (cachedPage) { + await releaseLease(coordinator, request.url, lease) + finalized = true + return cachedPage + } runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) const mutatesCanonicalState = isMutation(request, route) let response: Response @@ -236,6 +242,7 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi } finalized = true if (mutatesCanonicalState) await discardRuntime(runtime) + else if (route === "wordpress") response = await cacheWordPressPage(request, lease.pointer, response) return response } catch (error) { if (!finalized) await abortLease(coordinator, request.url, lease) @@ -244,6 +251,54 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi } } +function isCacheableWordPressPageRequest(request: Request): boolean { + if (request.method !== "GET" && request.method !== "HEAD") return false + if (request.headers.has("authorization") || request.headers.has("cookie")) return false + const url = new URL(request.url) + if (["/wp-admin", "/wp-login.php", "/wp-json"].some((path) => url.pathname === path || url.pathname.startsWith(`${path}/`))) return false + return !url.searchParams.has("preview") && !url.searchParams.has("rest_route") +} + +async function matchWordPressPageCache(request: Request, pointer: MarkdownPointer): Promise { + if (!isCacheableWordPressPageRequest(request)) return null + const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default + if (!cache) return null + try { + const cached = await cache.match(wordPressPageCacheKey(request, pointer)) + return cached ? pageCacheResponse(cached, request.method === "HEAD", "hit") : null + } catch { + return null + } +} + +async function cacheWordPressPage(request: Request, pointer: MarkdownPointer, response: Response): Promise { + if (!isCacheableWordPressPageRequest(request) || response.status !== 200 || !response.headers.get("content-type")?.includes("text/html") || response.headers.has("set-cookie")) return response + if (request.method === "HEAD") return pageCacheResponse(response, true, "miss") + const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default + const cacheable = pageCacheResponse(response, false, "miss") + if (cache) { + try { + await cache.put(wordPressPageCacheKey(request, pointer), cacheable.clone()) + } catch { + // Page caching is an optimization; canonical rendering remains authoritative. + } + } + return cacheable +} + +function wordPressPageCacheKey(request: Request, pointer: MarkdownPointer): Request { + const url = new URL(request.url) + url.searchParams.set("__wp_codebox_revision", pointer.revision) + return new Request(url, { method: "GET" }) +} + +function pageCacheResponse(response: Response, head: boolean, status: "hit" | "miss"): Response { + const headers = new Headers(response.headers) + headers.set("cache-control", "public, max-age=60, s-maxage=31536000") + headers.set("x-wp-codebox-page-cache", status) + return new Response(head ? null : response.body, { status: response.status, statusText: response.statusText, headers }) +} + function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate"): boolean { return route === "r2-mutate" || !["GET", "HEAD", "OPTIONS"].includes(request.method) } diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 4b73486a..05bd6ddf 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -21,8 +21,8 @@ try { const coldHome = await timedWordPressPage(origin, "cold explanatory homepage") const warmHome = await timedWordPressPage(origin, "warm explanatory homepage") await assertExplanatoryHomepage(warmHome.body) - if (warmHome.elapsedMs >= coldHome.elapsedMs || warmHome.elapsedMs > 2_000) { - throw new Error(`Warm explanatory homepage did not reuse the canonical runtime: cold=${coldHome.elapsedMs}ms warm=${warmHome.elapsedMs}ms.`) + if (coldHome.cacheStatus !== "miss" || warmHome.cacheStatus !== "hit" || warmHome.elapsedMs >= coldHome.elapsedMs || warmHome.elapsedMs > 500) { + throw new Error(`Warm explanatory homepage did not use its revision cache: cold=${coldHome.elapsedMs}ms/${coldHome.cacheStatus} warm=${warmHome.elapsedMs}ms/${warmHome.cacheStatus}.`) } console.log(`Explanatory homepage timing: cold=${coldHome.elapsedMs}ms warm=${warmHome.elapsedMs}ms.`) const adminHtml = await login() @@ -44,7 +44,7 @@ try { await assertLinkedAssets(restartedAdmin, "admin after cold restart") cookies.length = 0 await login() - console.log("Cloudflare local runtime gate passed: explanatory homepage, complete block styles, warm runtime reuse, login, dashboard, post editor, concurrent canonical mutations, authenticated REST post creation, frontend/admin/editor assets, and cold-restart session persistence.") + console.log("Cloudflare local runtime gate passed: explanatory homepage, complete block styles, revision page cache, login, dashboard, post editor, concurrent canonical mutations, authenticated REST post creation, frontend/admin/editor assets, and cold-restart session persistence.") } finally { await stopWorker() await rm(stateDirectory, { recursive: true, force: true }) @@ -164,8 +164,11 @@ async function assertWordPressPage(target, label) { async function timedWordPressPage(target, label) { const startedAt = performance.now() - const body = await assertWordPressPage(target, label) - return { body, elapsedMs: Math.round(performance.now() - startedAt) } + const response = await request(target) + const body = await response.text() + assertNoPhpDiagnostics(body, label) + if (response.status !== 200 || !response.headers.get("content-type")?.includes("text/html") || !/]/i.test(body)) throw new Error(`Expected an HTML ${label}, received ${response.status}: ${body}`) + return { body, elapsedMs: Math.round(performance.now() - startedAt), cacheStatus: response.headers.get("x-wp-codebox-page-cache") } } async function assertExplanatoryHomepage(html) { diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 39fe1886..7bfe15f4 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -250,6 +250,11 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /"x-wp-codebox-static": "wordpress-archive"/) assert.match(worker, /cache\.match\(request\)/) assert.match(worker, /cache\.put\(request, response\.clone\(\)\)/) + assert.match(worker, /matchWordPressPageCache\(request, lease\.pointer\)/) + assert.match(worker, /cacheWordPressPage\(request, lease\.pointer, response\)/) + assert.match(worker, /__wp_codebox_revision/) + assert.match(worker, /"x-wp-codebox-page-cache"/) + assert.match(worker, /"public, max-age=60, s-maxage=31536000"/) assert.match(materializer, /materializeWordPressRuntimeArtifact\(php, bucket, wordpressRuntimeArtifactManifest/) assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL/) assert.match(corpus, /path\.endsWith\("\.map"\)/) From 5abafae7acea7d2b3b720da22c8986a3b34fcbce Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 14:06:08 -0400 Subject: [PATCH 81/87] Serve cached Cloudflare pages before runtime boot --- packages/runtime-cloudflare/src/worker.ts | 73 ++++++++++++++++++----- tests/cloudflare-runtime.test.ts | 9 ++- 2 files changed, 65 insertions(+), 17 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index a2194f0e..ff771293 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -34,6 +34,8 @@ const MARKDOWN_RESOLVED_INDEX_PATH = "/tmp/markdown-index-8133b4cf3c66.sqlite" const MARKDOWN_CHANGES_PATH = "/tmp/wp-codebox-canonical-changes.json" const R2_MARKDOWN_REVISION_PREFIX = "sites/default/markdown/revisions" const R2_MARKDOWN_OBJECT_PREFIX = "sites/default/markdown/objects" +const R2_WORDPRESS_PAGE_PREFIX = "sites/default/pages" +const WORDPRESS_PAGE_CACHE_SCHEMA = "v2" const SERIALIZED_MARKDOWN_MUTATION_CODE = ` + body: string +} + interface CanonicalSeedManifest { schema: string markdownDatabaseIntegrationRevision: string @@ -201,6 +215,13 @@ async function secretsMatch(left: string, right: string): Promise { async function runCoordinatedWordPressRequest(request: Request, env: Env, coordinator: DurableObjectStub, route: "wordpress" | "health" | "r2-mutate"): Promise { if (route === "r2-mutate" && request.method !== "POST") return new Response("WordPress state mutation requires POST.", { status: 405 }) + if (route === "wordpress" && isCacheableWordPressPageRequest(request)) { + const state = await coordinatorCall(coordinator, request.url, "state") + if (state.pointer) { + const cachedPage = await matchWordPressPageCache(request, state.pointer, env.WORDPRESS_STATE_BUCKET) + if (cachedPage) return cachedPage + } + } let lease = await acquireLease(coordinator, request.url) let runtime: Runtime | undefined let finalized = false @@ -212,7 +233,7 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi if (!lease.pointer || lease.pointer.revision !== bootstrapped.pointer.revision) throw new Error("Canonical bootstrap promotion was not observed by its next lease.") cacheRuntime(lease.pointer, bootstrapped) } - const cachedPage = route === "wordpress" ? await matchWordPressPageCache(request, lease.pointer) : null + const cachedPage = route === "wordpress" ? await matchWordPressPageCache(request, lease.pointer, env.WORDPRESS_STATE_BUCKET) : null if (cachedPage) { await releaseLease(coordinator, request.url, lease) finalized = true @@ -242,7 +263,7 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi } finalized = true if (mutatesCanonicalState) await discardRuntime(runtime) - else if (route === "wordpress") response = await cacheWordPressPage(request, lease.pointer, response) + else if (route === "wordpress") response = await cacheWordPressPage(request, lease.pointer, response, env.WORDPRESS_STATE_BUCKET) return response } catch (error) { if (!finalized) await abortLease(coordinator, request.url, lease) @@ -259,29 +280,43 @@ function isCacheableWordPressPageRequest(request: Request): boolean { return !url.searchParams.has("preview") && !url.searchParams.has("rest_route") } -async function matchWordPressPageCache(request: Request, pointer: MarkdownPointer): Promise { +async function matchWordPressPageCache(request: Request, pointer: MarkdownPointer, bucket: R2Bucket): Promise { if (!isCacheableWordPressPageRequest(request)) return null const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default - if (!cache) return null try { - const cached = await cache.match(wordPressPageCacheKey(request, pointer)) - return cached ? pageCacheResponse(cached, request.method === "HEAD", "hit") : null + const cached = cache ? await cache.match(wordPressPageCacheKey(request, pointer)) : null + if (cached) return pageCacheResponse(cached, request.method === "HEAD", "hit", "edge") + const object = await bucket.get(await wordPressPageSnapshotKey(request, pointer)) + if (!object) return null + const snapshot = JSON.parse(await object.text()) as WordPressPageSnapshot + if (snapshot.schema !== "wp-codebox/wordpress-page/v1" || snapshot.status !== 200 || !Array.isArray(snapshot.headers) || typeof snapshot.body !== "string") return null + const response = new Response(snapshot.body, { status: snapshot.status, statusText: snapshot.statusText, headers: snapshot.headers }) + if (cache) await cache.put(wordPressPageCacheKey(request, pointer), response.clone()) + return pageCacheResponse(response, request.method === "HEAD", "hit", "r2") } catch { return null } } -async function cacheWordPressPage(request: Request, pointer: MarkdownPointer, response: Response): Promise { +async function cacheWordPressPage(request: Request, pointer: MarkdownPointer, response: Response, bucket: R2Bucket): Promise { if (!isCacheableWordPressPageRequest(request) || response.status !== 200 || !response.headers.get("content-type")?.includes("text/html") || response.headers.has("set-cookie")) return response - if (request.method === "HEAD") return pageCacheResponse(response, true, "miss") + if (request.method === "HEAD") return pageCacheResponse(response, true, "miss", "render") const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default - const cacheable = pageCacheResponse(response, false, "miss") - if (cache) { - try { - await cache.put(wordPressPageCacheKey(request, pointer), cacheable.clone()) - } catch { - // Page caching is an optimization; canonical rendering remains authoritative. + const cacheable = pageCacheResponse(response, false, "miss", "render") + try { + const snapshot: WordPressPageSnapshot = { + schema: "wp-codebox/wordpress-page/v1", + status: cacheable.status, + statusText: cacheable.statusText, + headers: Array.from(cacheable.headers.entries()), + body: await cacheable.clone().text(), } + await Promise.all([ + cache ? cache.put(wordPressPageCacheKey(request, pointer), cacheable.clone()) : Promise.resolve(), + bucket.put(await wordPressPageSnapshotKey(request, pointer), JSON.stringify(snapshot), { httpMetadata: { contentType: "application/json" } }), + ]) + } catch { + // Page caching is an optimization; canonical rendering remains authoritative. } return cacheable } @@ -289,13 +324,21 @@ async function cacheWordPressPage(request: Request, pointer: MarkdownPointer, re function wordPressPageCacheKey(request: Request, pointer: MarkdownPointer): Request { const url = new URL(request.url) url.searchParams.set("__wp_codebox_revision", pointer.revision) + url.searchParams.set("__wp_codebox_page_cache", WORDPRESS_PAGE_CACHE_SCHEMA) return new Request(url, { method: "GET" }) } -function pageCacheResponse(response: Response, head: boolean, status: "hit" | "miss"): Response { +async function wordPressPageSnapshotKey(request: Request, pointer: MarkdownPointer): Promise { + const digest = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(new URL(request.url).toString())) + const hash = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("") + return `${R2_WORDPRESS_PAGE_PREFIX}/${pointer.revision}/${hash}.json` +} + +function pageCacheResponse(response: Response, head: boolean, status: "hit" | "miss", source: "edge" | "r2" | "render"): Response { const headers = new Headers(response.headers) headers.set("cache-control", "public, max-age=60, s-maxage=31536000") headers.set("x-wp-codebox-page-cache", status) + headers.set("x-wp-codebox-page-cache-source", source) return new Response(head ? null : response.body, { status: response.status, statusText: response.statusText, headers }) } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 7bfe15f4..3163081d 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -250,10 +250,15 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /"x-wp-codebox-static": "wordpress-archive"/) assert.match(worker, /cache\.match\(request\)/) assert.match(worker, /cache\.put\(request, response\.clone\(\)\)/) - assert.match(worker, /matchWordPressPageCache\(request, lease\.pointer\)/) - assert.match(worker, /cacheWordPressPage\(request, lease\.pointer, response\)/) + assert.match(worker, /coordinatorCall\(coordinator, request\.url, "state"\)/) + assert.ok(worker.indexOf('coordinatorCall(coordinator, request.url, "state")') < worker.indexOf("let lease = await acquireLease")) + assert.match(worker, /matchWordPressPageCache\(request, lease\.pointer, env\.WORDPRESS_STATE_BUCKET\)/) + assert.match(worker, /cacheWordPressPage\(request, lease\.pointer, response, env\.WORDPRESS_STATE_BUCKET\)/) assert.match(worker, /__wp_codebox_revision/) + assert.match(worker, /R2_WORDPRESS_PAGE_PREFIX/) + assert.match(worker, /"wp-codebox\/wordpress-page\/v1"/) assert.match(worker, /"x-wp-codebox-page-cache"/) + assert.match(worker, /"x-wp-codebox-page-cache-source"/) assert.match(worker, /"public, max-age=60, s-maxage=31536000"/) assert.match(materializer, /materializeWordPressRuntimeArtifact\(php, bucket, wordpressRuntimeArtifactManifest/) assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL/) From b0fcead169f98fcdd2ec5a9aefb4603f60b78164 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 14:12:42 -0400 Subject: [PATCH 82/87] Declare PHPUnit config in readonly fixture --- tests/playground-phpunit-readonly-cache.integration.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/playground-phpunit-readonly-cache.integration.test.ts b/tests/playground-phpunit-readonly-cache.integration.test.ts index 020635c2..cdd080c6 100644 --- a/tests/playground-phpunit-readonly-cache.integration.test.ts +++ b/tests/playground-phpunit-readonly-cache.integration.test.ts @@ -62,13 +62,14 @@ async function writeFixture(): Promise { await mkdir(join(plugin, "tests"), { recursive: true }) await mkdir(dependency, { recursive: true }) await writeFile(join(plugin, "readonly-phpunit-fixture.php"), "tests\n") await writeFile(join(plugin, "source-sentinel.bin"), sentinel) await writeFile(join(plugin, "tests", "ReadonlyCacheTest.php"), "assertTrue(is_multisite()); } public function test_sentinel_is_available(): void { $this->assertGreaterThan(0, filesize(dirname(__DIR__) . \'/source-sentinel.bin\')); } public function test_dependency_activation_runs_after_install(): void { $this->assertGreaterThanOrEqual(1, get_option(\'wp_codebox_dependency_activation_users\')); } public function test_dependency_plugins_loaded_runs_once(): void { $this->assertSame(1, (int) get_option(\'wp_codebox_dependency_plugins_loaded_count\')); } public function test_wp_cli_namespaced_stdout_is_available(): void { $this->assertTrue(eval(\'namespace cli; return is_resource(STDOUT);\')); } }\n") await writeFile(join(dependency, "activation-dependency.php"), " 1)))); });\n") } async function digestTree(directory: string): Promise { - const files = ["readonly-phpunit-fixture.php", "source-sentinel.bin", "tests/ReadonlyCacheTest.php"] + const files = ["readonly-phpunit-fixture.php", "phpunit.xml.dist", "source-sentinel.bin", "tests/ReadonlyCacheTest.php"] const hash = createHash("sha256") for (const file of files) { hash.update(file) From 0f144aae4a7f0ea29d76897499d24a8b2aabfac1 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 17:51:55 -0400 Subject: [PATCH 83/87] Persist Cloudflare WordPress media in R2 --- .../src/upload-persistence.ts | 42 +++++ packages/runtime-cloudflare/src/worker.ts | 173 ++++++++++++++++-- scripts/cloudflare-local-gate.mjs | 58 +++++- tests/cloudflare-runtime.test.ts | 28 ++- 4 files changed, 279 insertions(+), 22 deletions(-) create mode 100644 packages/runtime-cloudflare/src/upload-persistence.ts diff --git a/packages/runtime-cloudflare/src/upload-persistence.ts b/packages/runtime-cloudflare/src/upload-persistence.ts new file mode 100644 index 00000000..064d71b0 --- /dev/null +++ b/packages/runtime-cloudflare/src/upload-persistence.ts @@ -0,0 +1,42 @@ +export const R2_UPLOAD_OBJECT_PREFIX = "sites/default/uploads/objects" +export const MAX_UPLOAD_FILES = 5000 +export const MAX_UPLOAD_FILE_BYTES = 16 * 1024 * 1024 +export const MAX_UPLOAD_TOTAL_BYTES = 64 * 1024 * 1024 + +export interface UploadFileMetadata { + path: string + size: number +} + +export interface UploadManifestFile extends UploadFileMetadata { + objectKey: string + sha256: string +} + +export function validateUploadMetadata(value: unknown): asserts value is UploadFileMetadata[] { + if (!Array.isArray(value) || value.length > MAX_UPLOAD_FILES) throw new Error("Canonical upload file count is invalid.") + let total = 0 + const paths = new Set() + for (const file of value) { + if (!file || typeof file !== "object" || typeof file.path !== "string" || !isCanonicalUploadPath(file.path) || paths.has(file.path) + || !Number.isSafeInteger(file.size) || file.size < 0 || file.size > MAX_UPLOAD_FILE_BYTES) { + throw new Error("Canonical upload metadata contains an invalid file.") + } + paths.add(file.path) + total += file.size + } + if (total > MAX_UPLOAD_TOTAL_BYTES) throw new Error("Canonical upload files exceed their byte budget.") +} + +export function validateUploadManifestFiles(value: unknown): asserts value is UploadManifestFile[] { + validateUploadMetadata(value) + for (const file of value as UploadManifestFile[]) { + if (!/^[a-f0-9]{64}$/.test(file.sha256) || file.objectKey !== `${R2_UPLOAD_OBJECT_PREFIX}/${file.sha256}`) { + throw new Error("Canonical upload manifest contains an invalid file.") + } + } +} + +function isCanonicalUploadPath(path: string): boolean { + return path.length > 0 && !path.startsWith("/") && !path.includes("\\") && !path.split("/").includes("..") +} diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index ff771293..80fd5fcb 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -9,6 +9,7 @@ import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, clo import { leaseRetryDelayMs } from "./lease-retry.js" import { routeWorkerRequest } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" +import { R2_UPLOAD_OBJECT_PREFIX, validateUploadManifestFiles, validateUploadMetadata } from "./upload-persistence.js" import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" import { materializeWordPressRuntimeArtifact, type WordPressRuntimeArtifactManifest } from "./wordpress-runtime-artifact.js" @@ -29,6 +30,7 @@ const MARKDOWN_DATABASE_INTEGRATION_REVISION = "2a8ee7f6a46e1d64b4606f1ee3c97e14 const SITE_URL = "https://wp-codebox-runtime.invalid" const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite" const MARKDOWN_ROOT = "/wordpress/wp-content/markdown" +const UPLOADS_ROOT = "/wordpress/wp-content/uploads" const MARKDOWN_INDEX_PATH = "/tmp/markdown-index.sqlite" const MARKDOWN_RESOLVED_INDEX_PATH = "/tmp/markdown-index-8133b4cf3c66.sqlite" const MARKDOWN_CHANGES_PATH = "/tmp/wp-codebox-canonical-changes.json" @@ -87,6 +89,8 @@ export default { if (staticResponse) return staticResponse const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") + const uploadResponse = await serveWordPressUpload(request, env.WORDPRESS_STATE_BUCKET, coordinator) + if (uploadResponse) return uploadResponse if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) if (route.kind === "operator-restore") return restoreCanonicalWordPress(request, env, coordinator) if (route.kind === "probe") { @@ -109,6 +113,7 @@ interface MarkdownManifestFile { interface MarkdownManifest extends MarkdownPointer { files: MarkdownManifestFile[] + uploads?: MarkdownManifestFile[] } interface RuntimeFile { @@ -434,7 +439,8 @@ async function discardRuntime(runtime: Runtime): Promise { } async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: string, authConstants: Record): Promise { - return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision(bucket, pointer), new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants, bucket, true), pointer } + const revision = await readCanonicalRevision(bucket, pointer) + return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, revision.markdown, new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants, bucket, true, revision.uploads), pointer } } async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { @@ -492,7 +498,7 @@ async function canonicalWordPressAuthConstants(env: Env): Promise { validateMarkdownChanges(changes) const changedPaths = [...changes.created, ...changes.changed].sort((left, right) => left.localeCompare(right)) - return persistMarkdownRevision(bucket, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT, changedPaths), runtime.pointer, changes) + return persistMarkdownRevision(bucket, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT, changedPaths), runtime.pointer, changes, await collectUploadFiles(runtime.php)) } function readCanonicalChanges(php: PHP): MarkdownChanges { @@ -538,26 +544,38 @@ function validateMarkdownChanges(changes: MarkdownChanges): void { } function isCanonicalRelativePath(path: string): boolean { - return path.length > 0 && !path.startsWith("/") && !path.split("/").includes("..") + return path.length > 0 && !path.startsWith("/") && !path.includes("\\") && !path.split("/").includes("..") } -async function readMarkdownRevision(bucket: R2Bucket, pointer: MarkdownPointer): Promise { +async function readCanonicalRevision(bucket: R2Bucket, pointer: MarkdownPointer): Promise<{ markdown: RuntimeFile[]; uploads: RuntimeFile[] }> { const manifestObject = await bucket.get(pointer.manifestKey) if (!manifestObject) throw new Error(`R2 Markdown manifest is missing: ${pointer.manifestKey}`) const manifest = await manifestObject.json() - return Promise.all(manifest.files.map(async (file): Promise => { + validateUploadManifestFiles(manifest.uploads ?? []) + return { + markdown: await readManifestFiles(bucket, manifest.files, "Markdown"), + uploads: await readManifestFiles(bucket, manifest.uploads ?? [], "upload"), + } +} + +async function readManifestFiles(bucket: R2Bucket, files: MarkdownManifestFile[], label: string): Promise { + return Promise.all(files.map(async (file): Promise => { const object = await bucket.get(file.objectKey) - if (!object) throw new Error(`R2 Markdown object is missing: ${file.objectKey}`) - return { path: file.path, bytes: new Uint8Array(await object.arrayBuffer()) } + if (!object) throw new Error(`R2 ${label} object is missing: ${file.objectKey}`) + const bytes = new Uint8Array(await object.arrayBuffer()) + if (bytes.byteLength !== file.size || await sha256Hex(bytes) !== file.sha256) throw new Error(`R2 ${label} object failed integrity validation: ${file.objectKey}`) + return { path: file.path, bytes } })) } -async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], current?: MarkdownPointer, changes?: MarkdownChanges): Promise { +async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], current?: MarkdownPointer, changes?: MarkdownChanges, uploads: RuntimeFile[] = []): Promise { const currentManifest = current ? await readMarkdownManifest(bucket, current) : null if (current && !currentManifest) throw new Error(`R2 Markdown manifest is missing: ${current.manifestKey}`) + const uploadManifestFiles = await persistUploadObjects(bucket, uploads, currentManifest?.uploads ?? []) + const uploadsUnchanged = JSON.stringify(currentManifest?.uploads ?? []) === JSON.stringify(uploadManifestFiles) if (current && currentManifest && changes) { validateMarkdownChanges(changes) - if (!changes.created.length && !changes.changed.length && !changes.deleted.length) return current + if (!changes.created.length && !changes.changed.length && !changes.deleted.length && uploadsUnchanged) return current const manifestFiles = new Map(currentManifest.files.map((file) => [file.path, file])) for (const path of changes.deleted) manifestFiles.delete(path) const filesByPath = new Map(files.map((file) => [file.path, file])) @@ -569,7 +587,7 @@ async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], c await bucket.put(objectKey, file.bytes) manifestFiles.set(path, { path, objectKey, sha256, size: file.bytes.byteLength }) } - return persistMarkdownManifest(bucket, [...manifestFiles.values()].sort((left, right) => left.path.localeCompare(right.path))) + return persistMarkdownManifest(bucket, [...manifestFiles.values()].sort((left, right) => left.path.localeCompare(right.path)), uploadManifestFiles) } const currentFiles = new Map(currentManifest?.files.map((file) => [file.path, file]) ?? []) const manifestFiles = await Promise.all(files.map(async (file): Promise => { @@ -581,18 +599,40 @@ async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], c return { path: file.path, objectKey, sha256, size: file.bytes.byteLength } })) if (current && currentManifest) { - if (JSON.stringify(currentManifest.files) === JSON.stringify(manifestFiles)) return current + if (JSON.stringify(currentManifest.files) === JSON.stringify(manifestFiles) && uploadsUnchanged) return current + } + + return persistMarkdownManifest(bucket, manifestFiles, uploadManifestFiles) +} + +async function persistUploadObjects(bucket: R2Bucket, files: RuntimeFile[], current: MarkdownManifestFile[]): Promise { + validateUploadFiles(files) + const currentFiles = new Map(current.map((file) => [file.path, file])) + const persisted: MarkdownManifestFile[] = [] + for (const file of files) { + const sha256 = await sha256Hex(file.bytes) + const existing = currentFiles.get(file.path) + if (existing?.sha256 === sha256 && existing.size === file.bytes.byteLength) { + persisted.push(existing) + continue + } + const objectKey = `${R2_UPLOAD_OBJECT_PREFIX}/${sha256}` + if (!await bucket.head(objectKey)) await bucket.put(objectKey, file.bytes) + persisted.push({ path: file.path, objectKey, sha256, size: file.bytes.byteLength }) } + return persisted +} - return persistMarkdownManifest(bucket, manifestFiles) +function validateUploadFiles(files: RuntimeFile[]): void { + validateUploadMetadata(files.map((file) => ({ path: file.path, size: file.bytes.byteLength }))) } -async function persistMarkdownManifest(bucket: R2Bucket, files: MarkdownManifestFile[]): Promise { +async function persistMarkdownManifest(bucket: R2Bucket, files: MarkdownManifestFile[], uploads: MarkdownManifestFile[] = []): Promise { const revision = crypto.randomUUID() const manifestKey = `${R2_MARKDOWN_REVISION_PREFIX}/${revision}.json` const persistedAt = new Date().toISOString() const pointer: MarkdownPointer = { revision, manifestKey, persistedAt } - const manifest: MarkdownManifest = { ...pointer, files } + const manifest: MarkdownManifest = { ...pointer, files, uploads } await bucket.put(manifestKey, JSON.stringify(manifest), { httpMetadata: { contentType: "application/json" }, }) @@ -845,6 +885,7 @@ async function bootWordPressRuntime( authConstants: Partial> = {}, runtimeBucket?: R2Bucket, shouldPatchCanonicalRuntimePoliciesAtInit = false, + uploadFiles?: RuntimeFile[], ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, @@ -868,8 +909,8 @@ async function bootWordPressRuntime( // Browser cookies are carried by the Worker Fetch request; Playground's // internal store would overwrite that header after an isolate restart. cookieStore: false, - hooks: streamWordPressFiles || databaseSeed || markdownFiles ? { - beforeWordPressFiles: streamWordPressFiles || markdownFiles ? async (php: PHP) => { + hooks: streamWordPressFiles || databaseSeed || markdownFiles || uploadFiles?.length ? { + beforeWordPressFiles: streamWordPressFiles || markdownFiles || uploadFiles?.length ? async (php: PHP) => { if (streamWordPressFiles) await materializeWordPressServerFiles(php, runtimeBucket) if (markdownFiles) { await materializeMarkdownDatabaseIntegration(php) @@ -877,6 +918,7 @@ async function bootWordPressRuntime( materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed) } + if (uploadFiles?.length) materializeRuntimeFiles(php, UPLOADS_ROOT, uploadFiles) if (shouldPatchCanonicalRuntimePoliciesAtInit) { patchCanonicalRuntimePoliciesAtInit(php) patchCanonicalThemeJsonCustomCss(php) @@ -980,6 +1022,24 @@ function collectRuntimeFiles(php: PHP, root: string, paths?: string[]): RuntimeF return files.sort((left, right) => left.path.localeCompare(right.path)) } +async function collectUploadFiles(php: PHP): Promise { + if (!php.isDir(UPLOADS_ROOT)) return [] + const output = (await php.run({ code: `isFile()) continue; + $path = str_replace('\\\\', '/', $file->getPathname()); + $files[] = array('path' => substr($path, strlen($root) + 1), 'size' => $file->getSize()); +} +usort($files, static fn($left, $right) => strcmp($left['path'], $right['path'])); +echo json_encode($files, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);` })).text.trim() + const metadata: unknown = JSON.parse(output) + validateUploadMetadata(metadata) + return metadata.map((file) => ({ path: file.path, bytes: php.readFileAsBuffer(`${UPLOADS_ROOT}/${file.path}`) })) +} + async function sha256Hex(bytes: Uint8Array): Promise { const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes).buffer) return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("") @@ -1018,6 +1078,87 @@ async function materializeWordPressServerFiles(php: PHP, bucket: R2Bucket | unde return materializeWordPressRuntimeArtifact(php, bucket, wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest) } +async function serveWordPressUpload(request: Request, bucket: R2Bucket, coordinator: DurableObjectStub): Promise { + if (request.method !== "GET" && request.method !== "HEAD") return null + const url = new URL(request.url) + if (!url.pathname.startsWith("/wp-content/uploads/")) return null + let path: string + try { + path = decodeURIComponent(url.pathname.slice("/wp-content/uploads/".length)) + } catch { + return new Response("Invalid WordPress upload path.", { status: 400 }) + } + if (!isCanonicalRelativePath(path)) return new Response("Invalid WordPress upload path.", { status: 400 }) + const state = await coordinatorCall(coordinator, request.url, "state") + if (!state.pointer) return null + const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default + const cacheKey = wordPressUploadCacheKey(request, state.pointer) + if (request.method === "GET" && cache) { + try { + const cached = await cache.match(cacheKey) + if (cached) return cached + } catch { + // R2 remains authoritative when the edge cache is unavailable. + } + } + const manifest = await readMarkdownManifest(bucket, state.pointer) + validateUploadManifestFiles(manifest?.uploads ?? []) + const file = manifest?.uploads?.find((candidate) => candidate.path === path) + if (!file) return new Response("WordPress upload not found.", { status: 404 }) + const object = await bucket.get(file.objectKey) + if (!object) throw new Error(`R2 upload object is missing: ${file.objectKey}`) + if (object.size !== file.size) throw new Error(`R2 upload object size is inconsistent: ${file.objectKey}`) + let body: Uint8Array | null = null + if (request.method === "GET") { + body = new Uint8Array(await object.arrayBuffer()) + if (await sha256Hex(body) !== file.sha256) throw new Error(`R2 upload object failed integrity validation: ${file.objectKey}`) + } + const headers = new Headers({ + "cache-control": "public, max-age=60", + "content-length": String(file.size), + "content-type": wordPressUploadContentType(path), + etag: `"${file.sha256}"`, + "x-wp-codebox-static": "r2-upload", + }) + const response = new Response(body ? Uint8Array.from(body).buffer : null, { status: 200, headers }) + if (request.method === "GET" && cache) { + try { + await cache.put(cacheKey, response.clone()) + } catch { + // R2 remains authoritative when the edge cache is unavailable. + } + } + return response +} + +function wordPressUploadCacheKey(request: Request, pointer: MarkdownPointer): Request { + const url = new URL(request.url) + url.searchParams.set("__wp_codebox_revision", pointer.revision) + return new Request(url, { method: "GET" }) +} + +function wordPressUploadContentType(path: string): string { + const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase() + return ({ + avif: "image/avif", + gif: "image/gif", + jpeg: "image/jpeg", + jpg: "image/jpeg", + json: "application/json", + mp3: "audio/mpeg", + mp4: "video/mp4", + ogg: "audio/ogg", + pdf: "application/pdf", + png: "image/png", + svg: "image/svg+xml", + txt: "text/plain; charset=utf-8", + wav: "audio/wav", + webm: "video/webm", + webp: "image/webp", + xml: "application/xml", + } as Record)[extension] ?? "application/octet-stream" +} + async function serveWordPressStaticAsset(request: Request): Promise { if (request.method !== "GET" && request.method !== "HEAD") return null const archivePath = wordpressStaticArchivePath(new URL(request.url).pathname) diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 05bd6ddf..874c8388 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -27,6 +27,8 @@ try { console.log(`Explanatory homepage timing: cold=${coldHome.elapsedMs}ms warm=${warmHome.elapsedMs}ms.`) const adminHtml = await login() const editorHtml = await assertPostNewEditor() + const media = await createMedia(adminHtml) + await assertMediaFile(media, "uploaded media") const post = await createPost(adminHtml) const frontPage = await assertWordPressPage(`${origin}/${post.slug}/`, "published post") assertIncludes(frontPage, post.title, "published post") @@ -41,10 +43,12 @@ try { const restartedAdmin = await assertAuthenticatedDashboard(new URL("/wp-admin/", origin)) const restartedPost = await assertWordPressPage(`${origin}/${post.slug}/`, "post after cold restart") assertIncludes(restartedPost, post.title, "post after cold restart") + await assertMediaFile(media, "media after cold restart") + await assertMediaMetadata(media, "media metadata after cold restart") await assertLinkedAssets(restartedAdmin, "admin after cold restart") cookies.length = 0 await login() - console.log("Cloudflare local runtime gate passed: explanatory homepage, complete block styles, revision page cache, login, dashboard, post editor, concurrent canonical mutations, authenticated REST post creation, frontend/admin/editor assets, and cold-restart session persistence.") + console.log("Cloudflare local runtime gate passed: explanatory homepage, complete block styles, revision page cache, login, dashboard, post editor, concurrent canonical mutations, authenticated REST post and media creation, direct R2 upload serving, frontend/admin/editor assets, and cold-restart content, media, and session persistence.") } finally { await stopWorker() await rm(stateDirectory, { recursive: true, force: true }) @@ -105,8 +109,7 @@ async function login() { } async function createPost(adminHtml) { - const nonce = adminHtml.match(/"nonce":"([^"]+)"/)?.[1] - if (!nonce) throw new Error("wp-admin did not expose a REST nonce.") + const nonce = restNonce(adminHtml) const title = `Cloudflare durable post ${Date.now()}` const response = await request(`${origin}/wp-json/wp/v2/posts`, { method: "POST", @@ -121,6 +124,55 @@ async function createPost(adminHtml) { return { slug: post.slug, title } } +async function createMedia(adminHtml) { + const nonce = restNonce(adminHtml) + const bytes = Buffer.from("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNk+A8AAQUBAScY42YAAAAASUVORK5CYII=", "base64") + const filename = `cloudflare-durable-media-${Date.now()}.png` + const response = await request(`${origin}/wp-json/wp/v2/media`, { + method: "POST", + headers: { + "content-disposition": `attachment; filename="${filename}"`, + "content-type": "image/png", + "x-wp-nonce": nonce, + }, + body: bytes, + }) + const body = await response.text() + assertNoPhpDiagnostics(body, "REST media creation") + if (response.status !== 201) throw new Error(`Expected REST media creation, received ${response.status}: ${body}`) + const media = JSON.parse(body) + if (!Number.isInteger(media.id) || typeof media.source_url !== "string" || !media.source_url.includes(`/wp-content/uploads/`) || !media.source_url.endsWith(`/${filename}`)) { + throw new Error(`Unexpected REST media response: ${body}`) + } + return { id: media.id, sourceUrl: media.source_url, filename, bytes } +} + +async function assertMediaFile(media, label) { + const response = await request(new URL(media.sourceUrl, origin)) + const bytes = Buffer.from(await response.arrayBuffer()) + if (response.status !== 200 || response.headers.get("content-type") !== "image/png" || response.headers.get("x-wp-codebox-static") !== "r2-upload" || !bytes.equals(media.bytes)) { + throw new Error(`${label} was not served intact from R2: status=${response.status} content-type=${response.headers.get("content-type")} source=${response.headers.get("x-wp-codebox-static")} bytes=${bytes.length}.`) + } + const head = await request(new URL(media.sourceUrl, origin), { method: "HEAD" }) + if (head.status !== 200 || head.headers.get("content-length") !== String(media.bytes.length) || head.headers.get("x-wp-codebox-static") !== "r2-upload" || (await head.arrayBuffer()).byteLength !== 0) { + throw new Error(`${label} HEAD semantics were invalid.`) + } +} + +async function assertMediaMetadata(media, label) { + const response = await request(`${origin}/wp-json/wp/v2/media/${media.id}`) + const body = await response.text() + assertNoPhpDiagnostics(body, label) + const attachment = JSON.parse(body) + if (response.status !== 200 || attachment.id !== media.id || !attachment.source_url?.endsWith(`/${media.filename}`)) throw new Error(`${label} was not restored: ${body}`) +} + +function restNonce(adminHtml) { + const nonce = adminHtml.match(/"nonce":"([^"]+)"/)?.[1] + if (!nonce) throw new Error("wp-admin did not expose a REST nonce.") + return nonce +} + async function assertPostNewEditor() { const response = await request(`${origin}/wp-admin/post-new.php`) const body = await response.text() diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 3163081d..1f1ea70d 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -11,11 +11,25 @@ import { leaseRetryDelayMs } from "../packages/runtime-cloudflare/src/lease-retr import { routeWorkerRequest } from "../packages/runtime-cloudflare/src/request-routing.js" import { toFetchResponse, toPHPRequest } from "../packages/runtime-cloudflare/src/request-translation.js" import { WordPressStateCoordinator } from "../packages/runtime-cloudflare/src/state-coordinator.js" +import { MAX_UPLOAD_FILE_BYTES, R2_UPLOAD_OBJECT_PREFIX, validateUploadManifestFiles, validateUploadMetadata } from "../packages/runtime-cloudflare/src/upload-persistence.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" import { materializeWordPressRuntimeArtifact, WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, wordpressRuntimeArtifactKey, type WordPressRuntimeArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" const execFileAsync = promisify(execFile) +test("Cloudflare upload manifests reject unbounded or non-canonical R2 files", () => { + const sha256 = "a".repeat(64) + const valid = [{ path: "2026/07/photo.png", size: 128, sha256, objectKey: `${R2_UPLOAD_OBJECT_PREFIX}/${sha256}` }] + assert.doesNotThrow(() => validateUploadManifestFiles(valid)) + assert.doesNotThrow(() => validateUploadManifestFiles([])) + assert.throws(() => validateUploadMetadata([{ path: "../secret", size: 1 }]), /invalid file/) + assert.throws(() => validateUploadMetadata([{ path: "2026\\secret", size: 1 }]), /invalid file/) + assert.throws(() => validateUploadMetadata([{ path: "same.png", size: 1 }, { path: "same.png", size: 1 }]), /invalid file/) + assert.throws(() => validateUploadMetadata([{ path: "large.png", size: MAX_UPLOAD_FILE_BYTES + 1 }]), /invalid file/) + assert.throws(() => validateUploadManifestFiles([{ ...valid[0], objectKey: "runtime/wordpress/archive.zip" }]), /invalid file/) + assert.throws(() => validateUploadManifestFiles([{ ...valid[0], sha256: "not-a-digest" }]), /invalid file/) +}) + test("Cloudflare health response preserves the Codebox execution envelope", async () => { const health = { schema: CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, @@ -199,8 +213,8 @@ test("Cloudflare canonical runtime patches the unique init call with runtime per assert.match(patcher, /return \(string\) _wp_array_get\( \$this->theme_json, array\( 'styles', 'css' \), '' \)/) assert.doesNotMatch(patcher, /mu-plugins|add_action\(|WP_Site_Health|wp_schedule_site_health_cron|\$GLOBALS\['wp_filter'\]/) assert.doesNotMatch(worker, /materializeCanonicalCronAdapter|wp-codebox-canonical-cron-policy/) - assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalRuntimePoliciesAtInit = false,/) - assert.match(worker, /authConstants, bucket, true\), pointer/) + assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalRuntimePoliciesAtInit = false,\n uploadFiles\?: RuntimeFile\[\],/) + assert.match(worker, /authConstants, bucket, true, revision\.uploads\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonicalBootstrapPasswordCode/) assert.match(worker, /canonicalBootstrapUrlCode/) @@ -221,7 +235,8 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.doesNotMatch(worker, /runtime\.pointer = next/) assert.match(worker, /await abortLease\(coordinator, request\.url, lease\)/) assert.match(worker, /const LEASE_ACQUISITION_TIMEOUT_MS = 100_000/) - assert.match(worker, /bootWordPressRuntime\("do-not-attempt-installing", true, true, undefined, await readMarkdownRevision/) + assert.match(worker, /const revision = await readCanonicalRevision\(bucket, pointer\)/) + assert.match(worker, /bootWordPressRuntime\("do-not-attempt-installing", true, true, undefined, revision\.markdown/) const noPointerBoot = worker.slice(worker.indexOf("async function bootstrapCanonicalRuntime"), worker.indexOf("async function persistRuntime")) assert.match(noPointerBoot, /packagedCanonicalMarkdownSeed\(\)/) assert.match(noPointerBoot, /canonicalBootstrapPasswordCode\(passwordFile\)/) @@ -260,6 +275,13 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /"x-wp-codebox-page-cache"/) assert.match(worker, /"x-wp-codebox-page-cache-source"/) assert.match(worker, /"public, max-age=60, s-maxage=31536000"/) + assert.match(worker, /serveWordPressUpload\(request, env\.WORDPRESS_STATE_BUCKET, coordinator\)/) + assert.ok(worker.indexOf("serveWordPressUpload(request, env.WORDPRESS_STATE_BUCKET, coordinator)") < worker.indexOf("runCoordinatedWordPressRequest(request, env, coordinator, route.kind)")) + assert.match(worker, /R2_UPLOAD_OBJECT_PREFIX/) + assert.match(worker, /collectUploadFiles\(runtime\.php\)/) + assert.match(worker, /materializeRuntimeFiles\(php, UPLOADS_ROOT, uploadFiles\)/) + assert.match(worker, /"x-wp-codebox-static": "r2-upload"/) + assert.equal((worker.match(/validateUploadManifestFiles\(manifest\??\.uploads \?\? \[\]\)/g) ?? []).length, 2) assert.match(materializer, /materializeWordPressRuntimeArtifact\(php, bucket, wordpressRuntimeArtifactManifest/) assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL/) assert.match(corpus, /path\.endsWith\("\.map"\)/) From b90b8b4ab3f52141494f48b2e9064869f39ec063 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 18:27:50 -0400 Subject: [PATCH 84/87] Serve WordPress assets from R2 ranges --- packages/runtime-cloudflare/README.md | 6 +- .../assets/wordpress-static-artifact.json | 13149 ++++++++++++++++ .../src/wordpress-runtime-corpus.ts | 10 +- .../src/wordpress-static-artifact.ts | 37 + packages/runtime-cloudflare/src/worker.ts | 77 +- scripts/cloudflare-local-gate.mjs | 14 +- ...ate-cloudflare-wordpress-runtime-corpus.ts | 35 +- ...on-cloudflare-wordpress-runtime-corpus.mjs | 25 +- tests/cloudflare-runtime.test.ts | 34 +- ...loudflare-wordpress-archive-corpus.test.ts | 9 +- 10 files changed, 13338 insertions(+), 58 deletions(-) create mode 100644 packages/runtime-cloudflare/assets/wordpress-static-artifact.json create mode 100644 packages/runtime-cloudflare/src/wordpress-static-artifact.ts diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 63d74582..de745464 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -16,15 +16,15 @@ The Worker forwards browser cookies directly to Playground and disables Playgrou The bundled MDI source is pinned to immutable commit `2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c` from [MDI PR #139](https://github.com/Automattic/markdown-database-integration/pull/139). The bundle generator, worker provenance, and source-contract test use the same revision. -The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache. +WordPress server files and browser assets are separate deployment artifacts, never Worker-module imports. `generate:cloudflare-wordpress-runtime-corpus` downloads the pinned WordPress release once. It emits a deterministic server ZIP containing only `isWordPressRuntimeFile()` entries and one concatenated browser-asset blob containing the complete supported `wp-admin`, `wp-includes`, and bundled-theme surface. Checked-in manifests record the content-addressed R2 keys, hashes, budgets, and each browser asset's exact byte range. Cold boot validates and streams the server ZIP into PHP MEMFS. Browser requests use one bounded R2 range read, validate the selected bytes, and populate an immutable artifact-versioned Worker cache without fetching or decoding the WordPress release at request time. Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpress/wp-settings.php` after the R2 WordPress corpus is materialized and before WordPress executes. The disabled-cron scheduling policy requires exactly one canonical `do_action( 'init' );` needle and fails closed otherwise. When `DISABLE_WP_CRON` is true, it removes only core's `wp_cron`, `wp_schedule_delete_old_privacy_export_files`, and `wp_schedule_update_checks` callbacks through public `remove_action()` before executing the original `init` call once. This prevents browser requests from recreating cron work that cannot execute in the PHP-WASM/MDI shutdown path. Durable cron execution belongs to a separate explicit queue or scheduled-Worker contract. ## Verification 1. Run `npm run generate:cloudflare-canonical-mdi-seed` and `npm run generate:cloudflare-wordpress-runtime-corpus` to regenerate deterministic runtime artifacts and manifests. -2. Run `npm run provision:cloudflare-wordpress-runtime-corpus -- --local --persist-to ` to verify and upload the exact content-addressed artifact into isolated local R2 storage. Use `--remote` only for an explicit deployment operation. +2. Run `npm run provision:cloudflare-wordpress-runtime-corpus -- --local --persist-to ` to verify and upload both exact content-addressed artifacts into isolated local R2 storage. For an authorized deployment, run the provisioner with `--remote` and require both uploads to succeed before deploying the Worker that imports their manifests. 3. Run `npm run test:cloudflare-runtime` for routing, canonical-state, artifact validation, source contract, and TypeScript coverage. 4. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. -5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions the artifact before workerd starts, then injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. +5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions both artifacts before workerd starts, then injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/assets/wordpress-static-artifact.json b/packages/runtime-cloudflare/assets/wordpress-static-artifact.json new file mode 100644 index 00000000..c5543469 --- /dev/null +++ b/packages/runtime-cloudflare/assets/wordpress-static-artifact.json @@ -0,0 +1,13149 @@ +{ + "schema": "wp-codebox/wordpress-static-artifact/v1", + "key": "runtime/wordpress-static/865d49b5c5e87df2b5bd19bc4d2a093dece6345a48c75df69ab6b6879108f760.bin", + "blob": { + "sha256": "865d49b5c5e87df2b5bd19bc4d2a093dece6345a48c75df69ab6b6879108f760", + "size": 63646828 + }, + "source": { + "url": "https://downloads.wordpress.org/release/wordpress-7.0.2.zip", + "version": "7.0.2", + "identity": "Fri, 17 Jul 2026 18:01:55 GMT" + }, + "files": [ + { + "path": "wordpress/wp-admin/css/about-rtl.css", + "offset": 0, + "size": 28508, + "sha256": "2727aad40269bbd75d93c9b8f9e012591a06d439bb59d5b7de80d5c79223be6e" + }, + { + "path": "wordpress/wp-admin/css/about-rtl.min.css", + "offset": 28508, + "size": 21880, + "sha256": "f73463398582139c9c58d5b657d9d06f7e0f0b395544a541a0378c27b3549611" + }, + { + "path": "wordpress/wp-admin/css/about.css", + "offset": 50388, + "size": 28474, + "sha256": "28b529ffd14504b922d35600ed37cc65ee0f5280bc1029471241e3bc34024b4a" + }, + { + "path": "wordpress/wp-admin/css/about.min.css", + "offset": 78862, + "size": 21881, + "sha256": "e54252bde4d74904593434f7c03fb2ad7c522f2bc9de9b12469ac73cc0004e69" + }, + { + "path": "wordpress/wp-admin/css/admin-menu-rtl.css", + "offset": 100743, + "size": 18723, + "sha256": "c62825482b817ba829bbb3a250f537f9eeded15f2704d7e59e5fb289c02246e9" + }, + { + "path": "wordpress/wp-admin/css/admin-menu-rtl.min.css", + "offset": 119466, + "size": 15165, + "sha256": "e77ecdf13545d306a52278acb621a3357fd78efba0f63e3600a10a0bd59818fd" + }, + { + "path": "wordpress/wp-admin/css/admin-menu.css", + "offset": 134631, + "size": 18674, + "sha256": "118085a50458f80ba94f24c623ea027fff42d7c59c0d5e47c9f79281de40041c" + }, + { + "path": "wordpress/wp-admin/css/admin-menu.min.css", + "offset": 153305, + "size": 15151, + "sha256": "6be1dd07f3371c294eab0e0413a614b09f9e055d0b766caf64eecb4632f73565" + }, + { + "path": "wordpress/wp-admin/css/code-editor-rtl.css", + "offset": 168456, + "size": 1618, + "sha256": "b08fc1a73263a4b949d7c55f6bc17e4d9714d13eec780e874819f4ac1b26abc9" + }, + { + "path": "wordpress/wp-admin/css/code-editor-rtl.min.css", + "offset": 170074, + "size": 1439, + "sha256": "d59ad8d0b7ab04b164b5ed254729d280358f9a6378741ee9f9f9e767d3f9f347" + }, + { + "path": "wordpress/wp-admin/css/code-editor.css", + "offset": 171513, + "size": 1579, + "sha256": "c491c7180157ac34d74a59a9675241b894b09f5043f73bfb93cdf987d98fff21" + }, + { + "path": "wordpress/wp-admin/css/code-editor.min.css", + "offset": 173092, + "size": 1435, + "sha256": "2ee84ee8d7086be38bc0c5ec53d2d47419d6da63a5f2d8bf750304e9c2d5c527" + }, + { + "path": "wordpress/wp-admin/css/color-picker-rtl.css", + "offset": 174527, + "size": 3957, + "sha256": "5d251d6223f3d04c95379f550526c24f5979c4d6a6824f13786eb57d6f0340bc" + }, + { + "path": "wordpress/wp-admin/css/color-picker-rtl.min.css", + "offset": 178484, + "size": 3201, + "sha256": "a5de01a2a71c1ddb2c25343758a7e1088d2c5c0a412657c581cdb2aadd6ba369" + }, + { + "path": "wordpress/wp-admin/css/color-picker.css", + "offset": 181685, + "size": 3919, + "sha256": "a97c135906dac602d7e2a942bcc3860cec8d6f7a0edb4d45afaad4b4fcecffe7" + }, + { + "path": "wordpress/wp-admin/css/color-picker.min.css", + "offset": 185604, + "size": 3198, + "sha256": "e99e4a8b1b932c22ef2f2fcf130b5d56abdd5a67389a60706112f7239625ed86" + }, + { + "path": "wordpress/wp-admin/css/colors/blue/colors-rtl.css", + "offset": 188802, + "size": 24824, + "sha256": "096ddea2c133905749d88059b7e62eecd67dc241d608350419ecd2efd13a3849" + }, + { + "path": "wordpress/wp-admin/css/colors/blue/colors-rtl.min.css", + "offset": 213626, + "size": 21424, + "sha256": "776a12b32a1e32b59f79729c59e08d1c7902e4cff90e18702ade69e98356ce3c" + }, + { + "path": "wordpress/wp-admin/css/colors/blue/colors.css", + "offset": 235050, + "size": 24823, + "sha256": "6a7fdbd4fb2ed2cbb09511812228a4ba9ce970d568fe49875cc9a2beebddb978" + }, + { + "path": "wordpress/wp-admin/css/colors/blue/colors.min.css", + "offset": 259873, + "size": 21423, + "sha256": "253d6fc6f3861ac439da1b0824873a550c5590901e863bc53c61d3bd3b0dc19a" + }, + { + "path": "wordpress/wp-admin/css/colors/coffee/colors-rtl.css", + "offset": 281296, + "size": 25387, + "sha256": "ec9e8dc0fa61663d16ca61f0631c1a31633d1b2d458c5b4828f2a902b28bb817" + }, + { + "path": "wordpress/wp-admin/css/colors/coffee/colors-rtl.min.css", + "offset": 306683, + "size": 21949, + "sha256": "8163890109a3af3799cd7dc7d2dd470a47e579532ddf5d279fcf479b3479c4fe" + }, + { + "path": "wordpress/wp-admin/css/colors/coffee/colors.css", + "offset": 328632, + "size": 25386, + "sha256": "46c566139035f1e564a844490ca2bcdf1929900a7d15ba17bff788708ff3d2f1" + }, + { + "path": "wordpress/wp-admin/css/colors/coffee/colors.min.css", + "offset": 354018, + "size": 21948, + "sha256": "a890308cb8ad9661b9d05ce9a4d11043283a66725864164f30cf7da48a70360f" + }, + { + "path": "wordpress/wp-admin/css/colors/ectoplasm/colors-rtl.css", + "offset": 375966, + "size": 25203, + "sha256": "7bf13d1b9539b7fada23851754bb5864c2bd0a9ebef5208c3bb8bde06145ee2b" + }, + { + "path": "wordpress/wp-admin/css/colors/ectoplasm/colors-rtl.min.css", + "offset": 401169, + "size": 21783, + "sha256": "6415d08904575b59da89f0e074d261f2edacf0fd95624ceaf43d9e63e2cbb106" + }, + { + "path": "wordpress/wp-admin/css/colors/ectoplasm/colors.css", + "offset": 422952, + "size": 25202, + "sha256": "13ff81c7ed9f4406748d4d5d5699a253cc4cf865937076009237572290eb668c" + }, + { + "path": "wordpress/wp-admin/css/colors/ectoplasm/colors.min.css", + "offset": 448154, + "size": 21782, + "sha256": "336e2b9b69e3f0ba431c80a354a8d398fe30b80d97545a7de4864fd97872d7e1" + }, + { + "path": "wordpress/wp-admin/css/colors/light/colors-rtl.css", + "offset": 469936, + "size": 24942, + "sha256": "29f958e92904a71bf6d747e247345ebd0f12d36e296d00d02a081c88e042c020" + }, + { + "path": "wordpress/wp-admin/css/colors/light/colors-rtl.min.css", + "offset": 494878, + "size": 21460, + "sha256": "1d6be0c1643dc2a26c97d6001f032763ac7d884760feb620a6cf6a3fd55fce36" + }, + { + "path": "wordpress/wp-admin/css/colors/light/colors.css", + "offset": 516338, + "size": 24941, + "sha256": "eda6988c8c4447fdffc502b4a510ba077b68e1fa7c44f202ecdf105dd6e40716" + }, + { + "path": "wordpress/wp-admin/css/colors/light/colors.min.css", + "offset": 541279, + "size": 21459, + "sha256": "38a4fbf711158de09ecf66bb99fe9b42fb9fb074df197571359286c22e717117" + }, + { + "path": "wordpress/wp-admin/css/colors/midnight/colors-rtl.css", + "offset": 562738, + "size": 25388, + "sha256": "b17e38fac46f941101b5815d8264eb33444af7fdffe1af689c178219c373f6d7" + }, + { + "path": "wordpress/wp-admin/css/colors/midnight/colors-rtl.min.css", + "offset": 588126, + "size": 21950, + "sha256": "702c42f21787b6bfef66ed829de530022f5cb1839400de303a7915823f4a01e3" + }, + { + "path": "wordpress/wp-admin/css/colors/midnight/colors.css", + "offset": 610076, + "size": 25387, + "sha256": "5b5d49af304d6ea3b236a0bb9e6b1c3ab2228b737b31b0a19296c3bd8dca6fc8" + }, + { + "path": "wordpress/wp-admin/css/colors/midnight/colors.min.css", + "offset": 635463, + "size": 21949, + "sha256": "e82b2996f56f8cd11567ff70070c2d4e43e3814c9b95a57dbb4c0805a6faa863" + }, + { + "path": "wordpress/wp-admin/css/colors/modern/colors-rtl.css", + "offset": 657412, + "size": 25092, + "sha256": "a64d7f8d6455e8b315d81fd26d7fcce579ac3f60edf617cb10dc1642899aad30" + }, + { + "path": "wordpress/wp-admin/css/colors/modern/colors-rtl.min.css", + "offset": 682504, + "size": 21600, + "sha256": "a1243c2f0f2a57d7059818bc3441c7357d96f1a37ed141dce2879aa31c366977" + }, + { + "path": "wordpress/wp-admin/css/colors/modern/colors.css", + "offset": 704104, + "size": 25091, + "sha256": "9bd7b1e822253b7a194f50824acdc6b3a6ce065773e0a31344ef39a9b7af6e11" + }, + { + "path": "wordpress/wp-admin/css/colors/modern/colors.min.css", + "offset": 729195, + "size": 21599, + "sha256": "51e0ca541e1de2107c10c4a201ae7148d2feb75ca28bb68a36aeafa9e20ae46d" + }, + { + "path": "wordpress/wp-admin/css/colors/ocean/colors-rtl.css", + "offset": 750794, + "size": 25146, + "sha256": "749fe808fcd6201df81b8f61f10ca699b01835328a4d43b8fbf4817c389eb092" + }, + { + "path": "wordpress/wp-admin/css/colors/ocean/colors-rtl.min.css", + "offset": 775940, + "size": 21726, + "sha256": "4d7782c005341322d35f133c49e579550744f7e0f3af007d8606f152af0c6532" + }, + { + "path": "wordpress/wp-admin/css/colors/ocean/colors.css", + "offset": 797666, + "size": 25145, + "sha256": "79e57ce50184dc067870f2da4bfd6f6aeda78856c0b3ea226d5d01ffb301b659" + }, + { + "path": "wordpress/wp-admin/css/colors/ocean/colors.min.css", + "offset": 822811, + "size": 21725, + "sha256": "a97b87263f6f66f92f988fd21ad29258c3ad7236dadb6b955843600539600345" + }, + { + "path": "wordpress/wp-admin/css/colors/sunrise/colors-rtl.css", + "offset": 844536, + "size": 25775, + "sha256": "2cd108bf783d733fe4230aa2123ae1fd45ab84aa4bdba7b3d554b921115e6eb9" + }, + { + "path": "wordpress/wp-admin/css/colors/sunrise/colors-rtl.min.css", + "offset": 870311, + "size": 22319, + "sha256": "d68f4bd6f4750ab2a05e486709a27f476024c76ccc71a7b138b4d342d1d39d02" + }, + { + "path": "wordpress/wp-admin/css/colors/sunrise/colors.css", + "offset": 892630, + "size": 25774, + "sha256": "877eaae66178843bcf155ad52adc0fb7d0c5e95d195a4326e61e10250aea9534" + }, + { + "path": "wordpress/wp-admin/css/colors/sunrise/colors.min.css", + "offset": 918404, + "size": 22318, + "sha256": "e50a702e9d1ff51750264b99f8ee726435b964e51aa1d233d54ac800bbe55ab1" + }, + { + "path": "wordpress/wp-admin/css/common-rtl.css", + "offset": 940722, + "size": 80918, + "sha256": "fbdedbb7744f0725e16be54b8da544b5eed9c7f51e46b94834e41af6a539c8ac" + }, + { + "path": "wordpress/wp-admin/css/common-rtl.min.css", + "offset": 1021640, + "size": 61575, + "sha256": "1f59ffa0a653c8324c3ac59e2709b9e43fa5da947cedece4b17fd682dcf3ea3a" + }, + { + "path": "wordpress/wp-admin/css/common.css", + "offset": 1083215, + "size": 80834, + "sha256": "53807b7bb21176021df74095c033b7fa8fe8fb9a52f197fdb1a3e6a0bab4c23c" + }, + { + "path": "wordpress/wp-admin/css/common.min.css", + "offset": 1164049, + "size": 61526, + "sha256": "1f9bcaa77aebe3c79c26ca6ec07fc55e9253e0ac373f4f946380e09e4163048a" + }, + { + "path": "wordpress/wp-admin/css/customize-controls-rtl.css", + "offset": 1225575, + "size": 72569, + "sha256": "190fb19ac7ea9059d70b8947186f0c71895e1f603f412e2e72ebfd8701cf9d38" + }, + { + "path": "wordpress/wp-admin/css/customize-controls-rtl.min.css", + "offset": 1298144, + "size": 60473, + "sha256": "76d9f2bef467dedeb34314d8d8c79ab8f592079a8857f49c4304f2edf9039213" + }, + { + "path": "wordpress/wp-admin/css/customize-controls.css", + "offset": 1358617, + "size": 72491, + "sha256": "77c8310c7df5708f7dda8c62fbef110e4a351dd910118e6403712d51b91e027b" + }, + { + "path": "wordpress/wp-admin/css/customize-controls.min.css", + "offset": 1431108, + "size": 60430, + "sha256": "b7fd6d982966135fe3fb6f27dafa3bf75dc5604379f89e3f53ab4677700d9e4e" + }, + { + "path": "wordpress/wp-admin/css/customize-nav-menus-rtl.css", + "offset": 1491538, + "size": 22662, + "sha256": "a80019758570d5a3e8d33cfb7eab1d0144c7529ea08f6989aeef86c32e0e42f6" + }, + { + "path": "wordpress/wp-admin/css/customize-nav-menus-rtl.min.css", + "offset": 1514200, + "size": 18526, + "sha256": "caf4c4fb2fa60bf87ef2b14a4dd4d5f3a3e92eb9b3369fa7c00a1bbb709394a9" + }, + { + "path": "wordpress/wp-admin/css/customize-nav-menus.css", + "offset": 1532726, + "size": 22616, + "sha256": "b456429b94b40f84ab820cfcaaa3f2189449d0ab6547b10d1817647bde634294" + }, + { + "path": "wordpress/wp-admin/css/customize-nav-menus.min.css", + "offset": 1555342, + "size": 18515, + "sha256": "3d1b0cf467e0a15976c725a7edd3c190e4ef32e3b30ae217618d066d9d881bba" + }, + { + "path": "wordpress/wp-admin/css/customize-widgets-rtl.css", + "offset": 1573857, + "size": 13594, + "sha256": "52701c8ee36a02ed0c21c9de8594b75d16f3644558ae3aa5e2057028775948fa" + }, + { + "path": "wordpress/wp-admin/css/customize-widgets-rtl.min.css", + "offset": 1587451, + "size": 10384, + "sha256": "6c8ba1c24ae983a62a268fae1ea73b35de26331d35130ac28a1c7dc3802692f9" + }, + { + "path": "wordpress/wp-admin/css/customize-widgets.css", + "offset": 1597835, + "size": 13557, + "sha256": "3489bf0563d800659f9785059c56cff2314f65faded9f35afe70508f3a338ad1" + }, + { + "path": "wordpress/wp-admin/css/customize-widgets.min.css", + "offset": 1611392, + "size": 10382, + "sha256": "41c29a661348dd08555191500662bc3ab4ae2f19052442c37f0e38915c934fce" + }, + { + "path": "wordpress/wp-admin/css/dashboard-rtl.css", + "offset": 1621774, + "size": 30439, + "sha256": "8745a3b075b07c4f1d09f9e36ba64c287af524e9da97bb96cf378e4134db6ec0" + }, + { + "path": "wordpress/wp-admin/css/dashboard-rtl.min.css", + "offset": 1652213, + "size": 23367, + "sha256": "81a18243ea7307f68bc857f9f05ee54c80c203c6e13b996f9c1e25e897d7f415" + }, + { + "path": "wordpress/wp-admin/css/dashboard.css", + "offset": 1675580, + "size": 30409, + "sha256": "9b85e16cb3936ab01ee67f6ee4389b40398678bcc833de7e8439557d77e61f27" + }, + { + "path": "wordpress/wp-admin/css/dashboard.min.css", + "offset": 1705989, + "size": 23372, + "sha256": "3fa76b561347943d3d9b3d677266cb1f045930da8eb2fdc2e57922179a5c1ef1" + }, + { + "path": "wordpress/wp-admin/css/deprecated-media-rtl.css", + "offset": 1729361, + "size": 6654, + "sha256": "817efc52beb365d0a628392a049f41835cbd68df6187d87de5de4ac1c600ef8e" + }, + { + "path": "wordpress/wp-admin/css/deprecated-media-rtl.min.css", + "offset": 1736015, + "size": 5497, + "sha256": "0ad5324a874dd5ded95bd5eda6213abfbf4c1a3a9412a6c0539306adb4961104" + }, + { + "path": "wordpress/wp-admin/css/deprecated-media.css", + "offset": 1741512, + "size": 6615, + "sha256": "a3e53706e945a8b72e1da9d0ea268afc5163ea5c727f7b80fee861eb45c52d42" + }, + { + "path": "wordpress/wp-admin/css/deprecated-media.min.css", + "offset": 1748127, + "size": 5493, + "sha256": "88cda5ce9f13449af94aa5cfc23a3b1e7f634f9b56e23f603613ef43d5cb3d92" + }, + { + "path": "wordpress/wp-admin/css/edit-rtl.css", + "offset": 1753620, + "size": 38765, + "sha256": "023d6783fbac06d5b5b90e01944b9e1aabde58d33acddbeba3e600a04eefc564" + }, + { + "path": "wordpress/wp-admin/css/edit-rtl.min.css", + "offset": 1792385, + "size": 30588, + "sha256": "8d1ce7a73517e68f68daa677d28c1a982dadbbff913d803bd9c57debb4b3c924" + }, + { + "path": "wordpress/wp-admin/css/edit.css", + "offset": 1822973, + "size": 38720, + "sha256": "0073510d7196c2e78d6cd6ff97c650ba4d270bda13df0abb93d62556483ad8c1" + }, + { + "path": "wordpress/wp-admin/css/edit.min.css", + "offset": 1861693, + "size": 30578, + "sha256": "fa1623599a47c4f595277ba7881ef86552dca2b347829464acd37ca96503b803" + }, + { + "path": "wordpress/wp-admin/css/farbtastic-rtl.css", + "offset": 1892271, + "size": 647, + "sha256": "bb7a56f76820ca15b462fb2c3b9dd39401535943ac0a3231e7df2dc43cbcdb2d" + }, + { + "path": "wordpress/wp-admin/css/farbtastic-rtl.min.css", + "offset": 1892918, + "size": 538, + "sha256": "bccd00ef7e876f73085eeb0d0d79d14c9ff41b70bd3effc8092c8c53c3146af6" + }, + { + "path": "wordpress/wp-admin/css/farbtastic.css", + "offset": 1893456, + "size": 611, + "sha256": "6a637d75055fbe91ef03c800937a27afb5081abd9cacac6ad019f2721aade193" + }, + { + "path": "wordpress/wp-admin/css/farbtastic.min.css", + "offset": 1894067, + "size": 537, + "sha256": "a2971a092b39c7640c0822ab94718f0a153d74711121504be8779368db4fdad2" + }, + { + "path": "wordpress/wp-admin/css/forms-rtl.css", + "offset": 1894604, + "size": 39886, + "sha256": "fa743fa272c1819a65e94c29fb51eff58c6ca82df909ebd58272f2053e60546d" + }, + { + "path": "wordpress/wp-admin/css/forms-rtl.min.css", + "offset": 1934490, + "size": 30916, + "sha256": "4d654fd8029efd8a52b58c33d87085166d91a974759824a5043a13bee613d29c" + }, + { + "path": "wordpress/wp-admin/css/forms.css", + "offset": 1965406, + "size": 39852, + "sha256": "3e77f4763f7f7baf17681cc6bf877d18aaa748e22b9d935095488d5fef311daf" + }, + { + "path": "wordpress/wp-admin/css/forms.min.css", + "offset": 2005258, + "size": 30917, + "sha256": "0cd1ac9e871de2e392e06038f109659092069ae30404e8d58fa3fe34e74535b6" + }, + { + "path": "wordpress/wp-admin/css/install-rtl.css", + "offset": 2036175, + "size": 6497, + "sha256": "bf63ee06a913bf4e9cd254ede219eca22b286f4b0bca5b17329d58a7ab78699c" + }, + { + "path": "wordpress/wp-admin/css/install-rtl.min.css", + "offset": 2042672, + "size": 5264, + "sha256": "4138a54857cd3f9c73a0a8a901d3f6a001dad6dde01e921bfc4a162c4ccb761e" + }, + { + "path": "wordpress/wp-admin/css/install.css", + "offset": 2047936, + "size": 6464, + "sha256": "e11dc5caf0d3538b676a0f9f561bb10bd00e95c94cc6d2adbcc3382dd179450f" + }, + { + "path": "wordpress/wp-admin/css/install.min.css", + "offset": 2054400, + "size": 5266, + "sha256": "23823bb7407aa48971b1971841a8d879e82c2f436482e8dcf7bf149dcaae4676" + }, + { + "path": "wordpress/wp-admin/css/l10n-rtl.css", + "offset": 2059666, + "size": 4579, + "sha256": "a322eef401a4369f2007bac3507c455062a6097ad6b5be827cccc9e20b099d6b" + }, + { + "path": "wordpress/wp-admin/css/l10n-rtl.min.css", + "offset": 2064245, + "size": 3204, + "sha256": "8530de93f7d10a0fba8e32de6249cf8954331dcb24ed162106afe27c9d229b81" + }, + { + "path": "wordpress/wp-admin/css/l10n.css", + "offset": 2067449, + "size": 4540, + "sha256": "e8adbaa73d562000c749e693805b68cb9ceac1b72f1b5b8eebec06ff63c798c8" + }, + { + "path": "wordpress/wp-admin/css/l10n.min.css", + "offset": 2071989, + "size": 3200, + "sha256": "8a77610fd0190ea9ecb57063433a619486dec13a59b1c2ce3b502b5c7cad7454" + }, + { + "path": "wordpress/wp-admin/css/list-tables-rtl.css", + "offset": 2075189, + "size": 45771, + "sha256": "084f8ec4c9a09cbbc5df32e3408139dc4ce88838fbbbe74d6e34a2278b94d7c4" + }, + { + "path": "wordpress/wp-admin/css/list-tables-rtl.min.css", + "offset": 2120960, + "size": 37036, + "sha256": "3275f01c7b44f1f7f5ce1acc035e77e2d206c8e72babb0832611aa75b9bee321" + }, + { + "path": "wordpress/wp-admin/css/list-tables.css", + "offset": 2157996, + "size": 45712, + "sha256": "828138c058e61d66eff34ba90b2b9dad2daf798e8c01b293254ccb29ed1960cf" + }, + { + "path": "wordpress/wp-admin/css/list-tables.min.css", + "offset": 2203708, + "size": 37012, + "sha256": "5f01224cddb9e519451f2b4c24132cb069618376f5ff5f85768153cd7db6b1aa" + }, + { + "path": "wordpress/wp-admin/css/login-rtl.css", + "offset": 2240720, + "size": 8291, + "sha256": "7991925def6e9fcad4187d575a57a298d84a1216a265d799e3d2f22e79b5f040" + }, + { + "path": "wordpress/wp-admin/css/login-rtl.min.css", + "offset": 2249011, + "size": 6642, + "sha256": "9ddae652f5ad497e5d501719c74241adfafb876905eb87efa5367ce81e2d672d" + }, + { + "path": "wordpress/wp-admin/css/login.css", + "offset": 2255653, + "size": 8253, + "sha256": "d0823e21e3b6a0ed8d49be076685cbdbe722b8835299d3aae924f62a8f7743d7" + }, + { + "path": "wordpress/wp-admin/css/login.min.css", + "offset": 2263906, + "size": 6639, + "sha256": "d917e93ff3478089d27e78528f756673b27882dca384e74f85868bb7d8962209" + }, + { + "path": "wordpress/wp-admin/css/media-rtl.css", + "offset": 2270545, + "size": 28399, + "sha256": "7179dd3ca19098080854b63ad3dd0e6722360ba24f5263469e60545c95bf5fb1" + }, + { + "path": "wordpress/wp-admin/css/media-rtl.min.css", + "offset": 2298944, + "size": 22909, + "sha256": "05944fe5b134799afd8162e90c5e68c79361ea02834f3805a5c13dcd64b830ab" + }, + { + "path": "wordpress/wp-admin/css/media.css", + "offset": 2321853, + "size": 28348, + "sha256": "d5783620dd4915726c0fdf49c59f0ff1ba504367fdd7a2c9ffbbb6a90e036ce6" + }, + { + "path": "wordpress/wp-admin/css/media.min.css", + "offset": 2350201, + "size": 22893, + "sha256": "17bf08cc853b865b0de589a63d2f5f1a0d2426ceeac882997d96ca297735dcb3" + }, + { + "path": "wordpress/wp-admin/css/nav-menus-rtl.css", + "offset": 2373094, + "size": 18143, + "sha256": "c383aa72b93549d22c0695ae93182303aa30b4f99ade3c45ee20fdfa889b5542" + }, + { + "path": "wordpress/wp-admin/css/nav-menus-rtl.min.css", + "offset": 2391237, + "size": 14327, + "sha256": "46b85b0a23a2332cac197b0e2588c6cc3c5d997f5fdf98a4186ea4fb815d0120" + }, + { + "path": "wordpress/wp-admin/css/nav-menus.css", + "offset": 2405564, + "size": 18081, + "sha256": "f4feed96ae288595ef3f82a5ef68001684a86a04d67e89d03a890e9d600879d1" + }, + { + "path": "wordpress/wp-admin/css/nav-menus.min.css", + "offset": 2423645, + "size": 14300, + "sha256": "4879084c42f60f933fadab8cbc5591634d1d8c498c0b864f43cd9f99e64e7815" + }, + { + "path": "wordpress/wp-admin/css/revisions-rtl.css", + "offset": 2437945, + "size": 10767, + "sha256": "bf807d22172cd1422f6d4ec8197603667c1c410f3c13f4548fa9aea991e72b26" + }, + { + "path": "wordpress/wp-admin/css/revisions-rtl.min.css", + "offset": 2448712, + "size": 8812, + "sha256": "63e8fb9399f23de11c460ccb9db90319327b523e8c6b1845c0373875a1476960" + }, + { + "path": "wordpress/wp-admin/css/revisions.css", + "offset": 2457524, + "size": 10720, + "sha256": "6f2a2934ccc422911259d01cc1a89b63be898f99f4f766ff3d74ea03fd885878" + }, + { + "path": "wordpress/wp-admin/css/revisions.min.css", + "offset": 2468244, + "size": 8800, + "sha256": "74454c0a2b959a8ddc62c7220113170c5e9ba7f9699bb519e39d326fccd177fc" + }, + { + "path": "wordpress/wp-admin/css/site-health-rtl.css", + "offset": 2477044, + "size": 6503, + "sha256": "d60d915acaef399e210b4664fec48d60af19da020e147bef1c4deb0ac4f20fee" + }, + { + "path": "wordpress/wp-admin/css/site-health-rtl.min.css", + "offset": 2483547, + "size": 5376, + "sha256": "e5b07fed684fceae2a6a5cde37ebf55b7214ab7d356191070309dec110d9cc08" + }, + { + "path": "wordpress/wp-admin/css/site-health.css", + "offset": 2488923, + "size": 6462, + "sha256": "df96db7978979531ee2764826810d07324eac062e6676974838dd6a470ee95cd" + }, + { + "path": "wordpress/wp-admin/css/site-health.min.css", + "offset": 2495385, + "size": 5370, + "sha256": "70b7ff6c3c039bf69c9308773c16d0c6df58ab55c7a8d68e6a7e537e16651978" + }, + { + "path": "wordpress/wp-admin/css/site-icon-rtl.css", + "offset": 2500755, + "size": 4648, + "sha256": "e4bd903340087c0f420b07100e6c63b3dcd396babae9d57c078446580bc15292" + }, + { + "path": "wordpress/wp-admin/css/site-icon-rtl.min.css", + "offset": 2505403, + "size": 3948, + "sha256": "f398ecbd5bfd0966b6d86e9a9cd07cb4be0ceb1dd9bbcb24ef2041e921cb81af" + }, + { + "path": "wordpress/wp-admin/css/site-icon.css", + "offset": 2509351, + "size": 4603, + "sha256": "0f6b8ff014eb4bf6ef10edb05b28e0dc1adc690edecbe530cc5658200cf33b14" + }, + { + "path": "wordpress/wp-admin/css/site-icon.min.css", + "offset": 2513954, + "size": 3938, + "sha256": "5a682fbea56069e25921501bf4f8c891147868e0154fb0cde7bb889747a03da3" + }, + { + "path": "wordpress/wp-admin/css/themes-rtl.css", + "offset": 2517892, + "size": 44052, + "sha256": "2cac655094d945b8c57542d85c7bf7cd7d7be5361c3da1574b7b1667b7820bb1" + }, + { + "path": "wordpress/wp-admin/css/themes-rtl.min.css", + "offset": 2561944, + "size": 34328, + "sha256": "f7b3b1bc9c9cf3cf15485dba40770038eccb98b080dda3b0fefeeef5d729ea46" + }, + { + "path": "wordpress/wp-admin/css/themes.css", + "offset": 2596272, + "size": 43994, + "sha256": "41155c4b39a01c188b37d721403e9d6b847941b14b771e54b04fe7175fe96998" + }, + { + "path": "wordpress/wp-admin/css/themes.min.css", + "offset": 2640266, + "size": 34305, + "sha256": "caa6b8324b833679b05c0b27bf6ab696a1a33d243dfe5cc5988865bbc5ba1181" + }, + { + "path": "wordpress/wp-admin/css/view-transitions.css", + "offset": 2674571, + "size": 185, + "sha256": "ab01321eee4efa25d86edc3ec2daaa39deb42feaad2a10dbd3d187b9dd208876" + }, + { + "path": "wordpress/wp-admin/css/view-transitions.min.css", + "offset": 2674756, + "size": 192, + "sha256": "5af068ba94aa90d200974e6d5b5eb0c6a0f20ee2394b24fd9f7a026316971ef6" + }, + { + "path": "wordpress/wp-admin/css/widgets-rtl.css", + "offset": 2674948, + "size": 17887, + "sha256": "6fe17e4af2d84d120255eccb6c3f302dec86caec5a2e19c4bc41f131ec1d9ed1" + }, + { + "path": "wordpress/wp-admin/css/widgets-rtl.min.css", + "offset": 2692835, + "size": 14622, + "sha256": "4d7bae3282e5fdb39e70b3c6fb6e1048dca73f1f1b1bf753fb2811feab1b582d" + }, + { + "path": "wordpress/wp-admin/css/widgets.css", + "offset": 2707457, + "size": 17850, + "sha256": "2fd0be79f357ab31f39a360682304ae3b0a3a88f1cb03da8329c5fb1b207db34" + }, + { + "path": "wordpress/wp-admin/css/widgets.min.css", + "offset": 2725307, + "size": 14620, + "sha256": "19761ae551375361889455c402b7d739e2bfe017c77f40dc586972c5fcda41fc" + }, + { + "path": "wordpress/wp-admin/css/wp-admin-rtl.css", + "offset": 2739927, + "size": 490, + "sha256": "c9f0a6535e0d37f953fcfa0085fbca75cff7243d9d8f2d1f2b1f1d08fd0778bf" + }, + { + "path": "wordpress/wp-admin/css/wp-admin-rtl.min.css", + "offset": 2740417, + "size": 550, + "sha256": "a3580344b7c792fc1bc49b8d30d61aed5a593c1159ec4469b420debb43cbf04e" + }, + { + "path": "wordpress/wp-admin/css/wp-admin.css", + "offset": 2740967, + "size": 395, + "sha256": "afddea0c36d0336392012fd91eea197ab40ffd9b2fff383626eeb9ab09ebb4c0" + }, + { + "path": "wordpress/wp-admin/css/wp-admin.min.css", + "offset": 2741362, + "size": 490, + "sha256": "4aabbf40156d65b9613aae00064cd0eb07fe0ae4a8b702caf9b1ffcf0b049ba0" + }, + { + "path": "wordpress/wp-admin/images/about-header-credits-rtl.webp", + "offset": 2741852, + "size": 40906, + "sha256": "b61028bfa229691f11db56a95e9994d840608fd7a9d244a8b2af8ae4a1fd29d1" + }, + { + "path": "wordpress/wp-admin/images/about-header-credits.webp", + "offset": 2782758, + "size": 40960, + "sha256": "1a6b2a866e20b8c55675cc5d5a1c227149851f9f4fcaa8a85414e839da1cafce" + }, + { + "path": "wordpress/wp-admin/images/about-header-default-rtl.webp", + "offset": 2823718, + "size": 18810, + "sha256": "af614f15e65ec57d06c07441f315779d01b5172e9339ec0eaac9e96cd522a956" + }, + { + "path": "wordpress/wp-admin/images/about-header-default.webp", + "offset": 2842528, + "size": 20030, + "sha256": "80ed7698a10da883f407f427e8a22e5be31276a678b9ec8cb36c051e8a11afb7" + }, + { + "path": "wordpress/wp-admin/images/about-header-freedoms-rtl.webp", + "offset": 2862558, + "size": 43814, + "sha256": "d406a33d697b575f00d66fa731e1f879a418d227ed7388a88dcb51fa99076037" + }, + { + "path": "wordpress/wp-admin/images/about-header-freedoms.webp", + "offset": 2906372, + "size": 40888, + "sha256": "e221b83c87c9c4363771969780f49cfdc6e423079ee3b19503c2c33e3d0d8994" + }, + { + "path": "wordpress/wp-admin/images/about-header-get-involved-rtl.webp", + "offset": 2947260, + "size": 15424, + "sha256": "4d3439e2417e00c6dbda0a3bf20a9c29bba08c9ef0737060321b6cd9102ff863" + }, + { + "path": "wordpress/wp-admin/images/about-header-get-involved.webp", + "offset": 2962684, + "size": 18168, + "sha256": "1191d78a25b4542c8abc035b4d87010c2d0e7bbb02bdc0f5a17832a49d293334" + }, + { + "path": "wordpress/wp-admin/images/about-header-privacy-rtl.webp", + "offset": 2980852, + "size": 13612, + "sha256": "447f2240557c00ab95314bb86cfaa5228189d5514c39364c3c9d637753fb594f" + }, + { + "path": "wordpress/wp-admin/images/about-header-privacy.webp", + "offset": 2994464, + "size": 10926, + "sha256": "cbab7d1280cc79f6ee8000c8fdf69c5eb97fb45d1324c39ae2f20c7dc7482804" + }, + { + "path": "wordpress/wp-admin/images/about-release-badge.svg", + "offset": 3005390, + "size": 4243, + "sha256": "89f4c892f72202765daefecea322a2058bcc21491b797bb994d615c4df04d9e6" + }, + { + "path": "wordpress/wp-admin/images/about-release-logo.svg", + "offset": 3009633, + "size": 4015, + "sha256": "6d7e4987de45bd1a6529f632a91a6d64ca7dc6e8f1e14f74975ba80d09aa01d9" + }, + { + "path": "wordpress/wp-admin/images/about-texture.png", + "offset": 3013648, + "size": 102792, + "sha256": "9b10f56e137d5a2f251b00c99a06694765f67065acfede74b2132bb821f5daf1" + }, + { + "path": "wordpress/wp-admin/images/align-center-2x.png", + "offset": 3116440, + "size": 147, + "sha256": "5c665c5e03d9f5379a72a2d3fbf4cbb0ed03be51080cbc59afe958399d2805a3" + }, + { + "path": "wordpress/wp-admin/images/align-center.png", + "offset": 3116587, + "size": 546, + "sha256": "41730d62b3b3ccb3de8c29c5f105894a91e9dc5170e46f4de0cf9de16d2e14de" + }, + { + "path": "wordpress/wp-admin/images/align-left-2x.png", + "offset": 3117133, + "size": 143, + "sha256": "00e982796093fc18a026909f41987b549395cd81b36424c331272f5bce9a0c06" + }, + { + "path": "wordpress/wp-admin/images/align-left.png", + "offset": 3117276, + "size": 554, + "sha256": "b0261b9415889b20d832fc2ebec7aae376da50d76513660476a827a15f319c4e" + }, + { + "path": "wordpress/wp-admin/images/align-none-2x.png", + "offset": 3117830, + "size": 121, + "sha256": "57439290bd3467f872aaedfb174d9ff5b039f155d776b013bfeb966f9569eb3c" + }, + { + "path": "wordpress/wp-admin/images/align-none.png", + "offset": 3117951, + "size": 417, + "sha256": "94784b48584e0d172d584fdb8f852da873a7bfee58cc43daa00fa69f596e80af" + }, + { + "path": "wordpress/wp-admin/images/align-right-2x.png", + "offset": 3118368, + "size": 142, + "sha256": "22393b4e731ac6aa96a79c1858a5589636f4e95cb1dcb2c86eae4d12290e2b86" + }, + { + "path": "wordpress/wp-admin/images/align-right.png", + "offset": 3118510, + "size": 509, + "sha256": "1595d6389b2fcc27afba09acea6916ae26f583c09c7ab5c1dd32ca926f52f808" + }, + { + "path": "wordpress/wp-admin/images/arrows-2x.png", + "offset": 3119019, + "size": 863, + "sha256": "a571cb5e488d5527801f72b5de8bb49e9d1af24186810f7983e1d3a272634095" + }, + { + "path": "wordpress/wp-admin/images/arrows.png", + "offset": 3119882, + "size": 243, + "sha256": "0aaa720437588d352173a8d73a99d0872555fb14c8ce0d3e2afe14d1a09264c3" + }, + { + "path": "wordpress/wp-admin/images/browser-rtl.png", + "offset": 3120125, + "size": 40170, + "sha256": "43a5c8d84f1c79da408dafa8a7de98f26707f1e2caf9938239d5054cf5132730" + }, + { + "path": "wordpress/wp-admin/images/browser.png", + "offset": 3160295, + "size": 40626, + "sha256": "2f962464206d5088f4705306a16b25be29f3ca28d1d5b40fe58337b22595f835" + }, + { + "path": "wordpress/wp-admin/images/bubble_bg-2x.gif", + "offset": 3200921, + "size": 424, + "sha256": "49b86dba8130e2e341c20ac22905304a0f3672cc8b7d01440906f82f1d255fe0" + }, + { + "path": "wordpress/wp-admin/images/bubble_bg.gif", + "offset": 3201345, + "size": 398, + "sha256": "4b26bde2dd28b4b05ff5d08239f2628bd55654a284b55b8b7c56423952d96c59" + }, + { + "path": "wordpress/wp-admin/images/comment-grey-bubble-2x.png", + "offset": 3201743, + "size": 258, + "sha256": "be73260f755788150851bd814f3cfc1e7a02203a280832ee959d0eb7ba531371" + }, + { + "path": "wordpress/wp-admin/images/comment-grey-bubble.png", + "offset": 3202001, + "size": 114, + "sha256": "429dddc805d611b94ed1db86f589f0c8c0f8aa1d377bb81eccb4c85f5a50381a" + }, + { + "path": "wordpress/wp-admin/images/contribute-code.svg", + "offset": 3202115, + "size": 9469, + "sha256": "5383af9878f5f7647fd4f46aecef497eaed7c16803ffa3071b710a1bfd89dd27" + }, + { + "path": "wordpress/wp-admin/images/contribute-main.svg", + "offset": 3211584, + "size": 17876, + "sha256": "d17763d0cf6ac7899dcf6c50075b682e983d0b0ff278e752b13061af2551dcbb" + }, + { + "path": "wordpress/wp-admin/images/contribute-no-code.svg", + "offset": 3229460, + "size": 8946, + "sha256": "17cf12d1a2f8693922386a4696d6e4b9786a4afff0dc65f86b014e0a33826212" + }, + { + "path": "wordpress/wp-admin/images/dashboard-background.svg", + "offset": 3238406, + "size": 3344, + "sha256": "eb55d8055063f46e176b6780b55e8d98b999b9ad3b85fbca55c64b0221480546" + }, + { + "path": "wordpress/wp-admin/images/date-button-2x.gif", + "offset": 3241750, + "size": 996, + "sha256": "4526f2fd5155238388619dcd936afe8ac1db268c0953fb2e1796ddbe948b7f6b" + }, + { + "path": "wordpress/wp-admin/images/date-button.gif", + "offset": 3242746, + "size": 400, + "sha256": "2737dc957ae0c2f933a0ec56c139c3fc59a9b7a25afd926db98c51b25f82f54b" + }, + { + "path": "wordpress/wp-admin/images/freedom-1.svg", + "offset": 3243146, + "size": 1447, + "sha256": "c641e751bcfb5d44aa4609d9f94c3aa9297f1baf564196b292aff3ee4e90ff72" + }, + { + "path": "wordpress/wp-admin/images/freedom-2.svg", + "offset": 3244593, + "size": 8120, + "sha256": "59e2923003e08c636372d0a073fa51a7ec5be56e27a7e8456a98f1c65aad5eaf" + }, + { + "path": "wordpress/wp-admin/images/freedom-3.svg", + "offset": 3252713, + "size": 1825, + "sha256": "bbe2f5a3f339f85b802cb480da9f29bfad3e8643ec5f241c7612231f6ae42e63" + }, + { + "path": "wordpress/wp-admin/images/freedom-4.svg", + "offset": 3254538, + "size": 3221, + "sha256": "2eae3f779a48188a6d217863bee67296573d3d437074aedff371ff10b5a2e2a7" + }, + { + "path": "wordpress/wp-admin/images/generic.png", + "offset": 3257759, + "size": 719, + "sha256": "2744c3560f9dcfd18d1c31a2c235d45dfe0f7f0f40f2e1b4128ab4392ed4b431" + }, + { + "path": "wordpress/wp-admin/images/icons32-2x.png", + "offset": 3258478, + "size": 21770, + "sha256": "5f40286567fe503b3207d8a215de93df31c74a1100e6e825b44090c4a9bfcdf8" + }, + { + "path": "wordpress/wp-admin/images/icons32-vs-2x.png", + "offset": 3280248, + "size": 21396, + "sha256": "a7e59c3ca136c7a3e78917c1d2bd426312f55db67e830074bae69a0379bedc84" + }, + { + "path": "wordpress/wp-admin/images/icons32-vs.png", + "offset": 3301644, + "size": 8007, + "sha256": "5738ed527d891b0c85acb760b0d98c005bc00616f21781795a0794fe9399565a" + }, + { + "path": "wordpress/wp-admin/images/icons32.png", + "offset": 3309651, + "size": 8023, + "sha256": "fc62c3317da17cf8f85825f3f8ce0d8e07088d6d94f8f27c95687854f1d35d76" + }, + { + "path": "wordpress/wp-admin/images/imgedit-icons-2x.png", + "offset": 3317674, + "size": 7664, + "sha256": "3d5e3539a53f1f012d600322028bbd8d080328a99fc5cf27db484ec9343a9415" + }, + { + "path": "wordpress/wp-admin/images/imgedit-icons.png", + "offset": 3325338, + "size": 4055, + "sha256": "c92e717fb0028cdd62d501c1b765a0cafecfd4a22cb7858cf68fb20ea008fa35" + }, + { + "path": "wordpress/wp-admin/images/list-2x.png", + "offset": 3329393, + "size": 1523, + "sha256": "3410fa2846b29bf96a2e9a513461b6beace994d1155a9e5aa8e0aa4e6539c856" + }, + { + "path": "wordpress/wp-admin/images/list.png", + "offset": 3330916, + "size": 1003, + "sha256": "028d2ff8bbfde66a0f8444234e2611aa460c810d6ce4ed9591ec6be39f03b355" + }, + { + "path": "wordpress/wp-admin/images/loading.gif", + "offset": 3331919, + "size": 1368, + "sha256": "c732ad2ea9a815acb85d81820997596e062c7a2e1771364d11c1223f1065d13a" + }, + { + "path": "wordpress/wp-admin/images/marker.png", + "offset": 3333287, + "size": 360, + "sha256": "a75d1f8231055a4cacd6b22fe7cd139c45759702d855318a235ea4e2243e4000" + }, + { + "path": "wordpress/wp-admin/images/mask.png", + "offset": 3333647, + "size": 2001, + "sha256": "d053129e2c88c186cfeb9370bae1d3b791ea08b9adf08597a14d87c0f6a3a8f4" + }, + { + "path": "wordpress/wp-admin/images/media-button-2x.png", + "offset": 3335648, + "size": 850, + "sha256": "8801ccfa131e8aed1db636cfd6d0b871834a044e43c67e3429469f6358ee2036" + }, + { + "path": "wordpress/wp-admin/images/media-button-image.gif", + "offset": 3336498, + "size": 200, + "sha256": "717b58d66fb415062f4b67c462dd284fa777924061e8afe62aa7a79c88213be1" + }, + { + "path": "wordpress/wp-admin/images/media-button-music.gif", + "offset": 3336698, + "size": 206, + "sha256": "bd0af3d59c1d7747d458026edc0e2eb71d4a26a9e5874b4e72350d55caa6bae2" + }, + { + "path": "wordpress/wp-admin/images/media-button-other.gif", + "offset": 3336904, + "size": 248, + "sha256": "50b12f29207a4a8a7c5217368531955099a5b95a5443d9ef55ee2e6ceeb01f5b" + }, + { + "path": "wordpress/wp-admin/images/media-button-video.gif", + "offset": 3337152, + "size": 133, + "sha256": "25b659c5b9b0d120944f0c8765946ce3225f91d4b2e9ecaf6ad63378e5bd11d6" + }, + { + "path": "wordpress/wp-admin/images/media-button.png", + "offset": 3337285, + "size": 323, + "sha256": "fe2a417e3c463355368f730c141382b4ce0814bbb6e40856f0794c6109b89678" + }, + { + "path": "wordpress/wp-admin/images/menu-2x.png", + "offset": 3337608, + "size": 12672, + "sha256": "8a8e6a54d219084b846673c294e060a4a20adc76520ba0ff697100af9e379b49" + }, + { + "path": "wordpress/wp-admin/images/menu-vs-2x.png", + "offset": 3350280, + "size": 12453, + "sha256": "8bee902dfc5c56f197a593ee47cf09cba8a4924214c2f69314bebfd5cc6f6ac6" + }, + { + "path": "wordpress/wp-admin/images/menu-vs.png", + "offset": 3362733, + "size": 5086, + "sha256": "637b7f9aa72a0d71f6a09c9ee2d5298b1dad67c4992dd1e45566aecf0f797ac7" + }, + { + "path": "wordpress/wp-admin/images/menu.png", + "offset": 3367819, + "size": 5039, + "sha256": "c34e25051a24133a3f884cca6883cbdc5c3158294c3f1e58d801e2d4e4d0a2af" + }, + { + "path": "wordpress/wp-admin/images/no.png", + "offset": 3372858, + "size": 755, + "sha256": "65b0d4223629c8d40101dbd0ffd3c0bdc9cac63aa5e9884d1118023c00a61cba" + }, + { + "path": "wordpress/wp-admin/images/post-formats-vs.png", + "offset": 3373613, + "size": 2450, + "sha256": "082ebbb05bb6199229e004d0180a8e0872248dd277216436d27b1a1abe8de578" + }, + { + "path": "wordpress/wp-admin/images/post-formats.png", + "offset": 3376063, + "size": 2157, + "sha256": "e4d4f061d6756fa40df72ef0fc048de007da97d863cedecad66ad67d386ffe5a" + }, + { + "path": "wordpress/wp-admin/images/post-formats32-vs.png", + "offset": 3378220, + "size": 5111, + "sha256": "c91998b51243a5f2ce34b3fd108993059f3b22a7f3c52c61846f60b1d59e11a3" + }, + { + "path": "wordpress/wp-admin/images/post-formats32.png", + "offset": 3383331, + "size": 5142, + "sha256": "4e5746754b7aa38c8a5827f7562de88aae9d5133dffdc0631ed3ba1ca23417b9" + }, + { + "path": "wordpress/wp-admin/images/privacy.svg", + "offset": 3388473, + "size": 922, + "sha256": "7fdc9a3fa1d0119336769a7478f1217aceb6fbccf1b426bb6a8cb879fa4b2b93" + }, + { + "path": "wordpress/wp-admin/images/resize-2x.gif", + "offset": 3389395, + "size": 151, + "sha256": "e687b47932717f99fe5af06fa0a67eb4a2285b971e826000601b43b8691a4c27" + }, + { + "path": "wordpress/wp-admin/images/resize-rtl-2x.gif", + "offset": 3389546, + "size": 150, + "sha256": "77324bf0e9bdc25f0d18136c1dafa19d548027ba7e9ed692efcefbf72f114983" + }, + { + "path": "wordpress/wp-admin/images/resize-rtl.gif", + "offset": 3389696, + "size": 70, + "sha256": "4053742d5df37aba95272b6b9fb73280e57bd86adc2cca514e9aaf7e29db093e" + }, + { + "path": "wordpress/wp-admin/images/resize.gif", + "offset": 3389766, + "size": 64, + "sha256": "a93611f3808fb802d701f38826133f03c335fad13f73fd71474d03601c50163b" + }, + { + "path": "wordpress/wp-admin/images/se.png", + "offset": 3389830, + "size": 120, + "sha256": "85a026667ca42d3f870c8d9f9e1f89e20938a011ffd53f460d224162fcb60fa5" + }, + { + "path": "wordpress/wp-admin/images/sort-2x.gif", + "offset": 3389950, + "size": 97, + "sha256": "03100820524aff93296baf4c972ca237b6b37cc6b2457c7d171225ae89026b88" + }, + { + "path": "wordpress/wp-admin/images/sort.gif", + "offset": 3390047, + "size": 55, + "sha256": "765006901f84842d7eab1978920cb54f2205099214c71115d0d9e1fe3b1780e9" + }, + { + "path": "wordpress/wp-admin/images/spinner-2x.gif", + "offset": 3390102, + "size": 7536, + "sha256": "3e9fd050059a29fbab9bff42df8fd81207702d8feed30256d375d61cba089045" + }, + { + "path": "wordpress/wp-admin/images/spinner.gif", + "offset": 3397638, + "size": 3656, + "sha256": "5c2d44038a6c53bd74f3a26e690c1974e1385fffbc6def4c59f91dc1f738f0b3" + }, + { + "path": "wordpress/wp-admin/images/stars-2x.png", + "offset": 3401294, + "size": 1257, + "sha256": "54f7086a4d933b717c143b1231e95650dd25081d42f0d4e96ca068a1e741dba0" + }, + { + "path": "wordpress/wp-admin/images/stars.png", + "offset": 3402551, + "size": 924, + "sha256": "f7de6e5ecbce2b2d99c238159065f0707a666231d1b92cf0d43ff922f550fd5b" + }, + { + "path": "wordpress/wp-admin/images/w-logo-blue.png", + "offset": 3403475, + "size": 3113, + "sha256": "a8c9355719e180f67753c823b87c29f40e21df91c20b44eb92d4cb36ef575d09" + }, + { + "path": "wordpress/wp-admin/images/w-logo-gray.png", + "offset": 3406588, + "size": 4497, + "sha256": "da9b9ce641489f87bc08fc580178e1dc24e24fda9a5bf609d0ddb37743153dcb" + }, + { + "path": "wordpress/wp-admin/images/w-logo-white.png", + "offset": 3411085, + "size": 5395, + "sha256": "c84ef2471e911d4aa6c335048040186f4b73dadb701bf20ec5806ad303cd942f" + }, + { + "path": "wordpress/wp-admin/images/wheel.png", + "offset": 3416480, + "size": 6047, + "sha256": "9f51cf596c2c8d23886a16b5ef30768ba17fefed27def598270b706de00a9d19" + }, + { + "path": "wordpress/wp-admin/images/wordpress-logo-gray.svg", + "offset": 3422527, + "size": 1521, + "sha256": "531a32f2c02a785a75778932b749a8aa38cb364191b93e371e3bb003b742f421" + }, + { + "path": "wordpress/wp-admin/images/wordpress-logo-white.svg", + "offset": 3424048, + "size": 1639, + "sha256": "a069a75b57617e5a262c71466627b5f05a67d4e70646efe72dda4e1ad6130b41" + }, + { + "path": "wordpress/wp-admin/images/wordpress-logo.png", + "offset": 3425687, + "size": 2480, + "sha256": "16fc59dad281b476356556c5d382811f3fabc51d771002615ef5f56da6160178" + }, + { + "path": "wordpress/wp-admin/images/wordpress-logo.svg", + "offset": 3428167, + "size": 1521, + "sha256": "a0bbefd626f1e76f9245ec6c6101b679ba27412b71b32fc43eccda9db40f394b" + }, + { + "path": "wordpress/wp-admin/images/wpspin_light-2x.gif", + "offset": 3429688, + "size": 8875, + "sha256": "e8a839ef2ddca2cf9f89f0661e5fcce03c58b0f240b38c53b33780a6d5a79a75" + }, + { + "path": "wordpress/wp-admin/images/wpspin_light.gif", + "offset": 3438563, + "size": 2052, + "sha256": "2a8f12a7ebe254858e4b9c2a61f9e6e82acb18958dedb5c03265413618ef5518" + }, + { + "path": "wordpress/wp-admin/images/xit-2x.gif", + "offset": 3440615, + "size": 825, + "sha256": "03bd4516b71adfd84327f9a4f13d6f0824d445a22ecae0742781bf1b3b7f436d" + }, + { + "path": "wordpress/wp-admin/images/xit.gif", + "offset": 3441440, + "size": 181, + "sha256": "cd3ba896382ba0845bf20deb332650e6fd0e2f8d40137c2057c79d1cb88719b1" + }, + { + "path": "wordpress/wp-admin/images/yes.png", + "offset": 3441621, + "size": 539, + "sha256": "36e18dacc49af4f3a0dfcb7d6be8fd2fb0c7877a37ff0bf66161de9353f6d5e1" + }, + { + "path": "wordpress/wp-admin/js/accordion.js", + "offset": 3442160, + "size": 2933, + "sha256": "9d42a035a03e2782fedbd8140dd910f0598f236f450709f4fdbc42c38eb04427" + }, + { + "path": "wordpress/wp-admin/js/accordion.min.js", + "offset": 3445093, + "size": 758, + "sha256": "e7f0374ed9c114f632f28261c70df18b5784f5dfa7ce0e7b520049a49b621623" + }, + { + "path": "wordpress/wp-admin/js/application-passwords.js", + "offset": 3445851, + "size": 6394, + "sha256": "e535345c324730f86140388a6900ac334478d9e7d54e2074e1c6a2ba3a923d83" + }, + { + "path": "wordpress/wp-admin/js/application-passwords.min.js", + "offset": 3452245, + "size": 3024, + "sha256": "3b8199e6bc6e82d87830577af49c29a2a2e0421c4476359ee95d967af4a83822" + }, + { + "path": "wordpress/wp-admin/js/auth-app.js", + "offset": 3455269, + "size": 5796, + "sha256": "d5706f56b6d3aad8fc7e5f8003bd1e394e4907d3039e7458beebbbb0d6bf989f" + }, + { + "path": "wordpress/wp-admin/js/auth-app.min.js", + "offset": 3461065, + "size": 2084, + "sha256": "a99084790aa95c1f075ad0345fb12163e65aad7c7b084ba10355be117b628c2f" + }, + { + "path": "wordpress/wp-admin/js/code-editor.js", + "offset": 3463149, + "size": 17963, + "sha256": "55bc12562b5bd53e347a6f59465b9d6406d309a803f608e4a3f1f52e18fb23c6" + }, + { + "path": "wordpress/wp-admin/js/code-editor.min.js", + "offset": 3481112, + "size": 3543, + "sha256": "50188ee89f0af9d906849a9f697fb32a337b384f8e9a1b4a7d2ac286c65675c3" + }, + { + "path": "wordpress/wp-admin/js/color-picker.js", + "offset": 3484655, + "size": 9768, + "sha256": "6ab9254b61e142411f1249dc75171b46bf424d693ddc97e9377e2c847f2f7fc8" + }, + { + "path": "wordpress/wp-admin/js/color-picker.min.js", + "offset": 3494423, + "size": 3486, + "sha256": "40b06ab4be79e91158c086495a79d88eeb4d13f4a9e8384fa2af3b5d908d3419" + }, + { + "path": "wordpress/wp-admin/js/comment.js", + "offset": 3497909, + "size": 2919, + "sha256": "332e875fe63b64b8b9e412f9df78ea96572a66b301d32b7b677fe64f680b799d" + }, + { + "path": "wordpress/wp-admin/js/comment.min.js", + "offset": 3500828, + "size": 1315, + "sha256": "2e98955531bb82033d6392c968697bb1781ccd8b9b666453e039feca55450407" + }, + { + "path": "wordpress/wp-admin/js/common.js", + "offset": 3502143, + "size": 62736, + "sha256": "38b80d3a7945f935208faea54e473ac0e6d3a8faf7753d344433b8e4c7e02dd3" + }, + { + "path": "wordpress/wp-admin/js/common.min.js", + "offset": 3564879, + "size": 23762, + "sha256": "8b1daa09af39e53b7e20599c683e74649ffe2846caf6b4a0f1a7021eadf13864" + }, + { + "path": "wordpress/wp-admin/js/custom-background.js", + "offset": 3588641, + "size": 3435, + "sha256": "8361c5c4334b6d186a6f9a28c2f8266a36ebff5951da119785d97b79b756e606" + }, + { + "path": "wordpress/wp-admin/js/custom-background.min.js", + "offset": 3592076, + "size": 1206, + "sha256": "e3078a09f1ec143dc3c8523ae1f956d7ba000f5d59f9636cdb497aee714d4242" + }, + { + "path": "wordpress/wp-admin/js/custom-header.js", + "offset": 3593282, + "size": 2023, + "sha256": "42c8f871753814b50a24dc185b2209da5509873b603a823f5f419dc1e86d5bf4" + }, + { + "path": "wordpress/wp-admin/js/customize-controls.js", + "offset": 3595305, + "size": 295335, + "sha256": "6bff7b564bdb33529c16b6e513b31687c2d9b2f3f77a1df62775e265c33a59a5" + }, + { + "path": "wordpress/wp-admin/js/customize-controls.min.js", + "offset": 3890640, + "size": 112322, + "sha256": "17565e1804bf539d544bcc1794b64aa4c52a3bd58066d184604c561e3dabcac3" + }, + { + "path": "wordpress/wp-admin/js/customize-nav-menus.js", + "offset": 4002962, + "size": 114135, + "sha256": "16e009106de972d7950c89e7e2377fa017ca914bf09f132bab070786b22f5fab" + }, + { + "path": "wordpress/wp-admin/js/customize-nav-menus.min.js", + "offset": 4117097, + "size": 48272, + "sha256": "391719c036b5e93a9c4d9c5b8797737f78d79273d39932dc68db8f612808247b" + }, + { + "path": "wordpress/wp-admin/js/customize-widgets.js", + "offset": 4165369, + "size": 71727, + "sha256": "5329b79a5a7bac633a1e47e25dea8336929ef8fc27dd36ca57bcb079ae924c89" + }, + { + "path": "wordpress/wp-admin/js/customize-widgets.min.js", + "offset": 4237096, + "size": 28065, + "sha256": "08402e37b264cb2c5e4a10997912653cfa2e68e1540bf895dfa17eb93df38035" + }, + { + "path": "wordpress/wp-admin/js/dashboard.js", + "offset": 4265161, + "size": 27666, + "sha256": "8c2f4900f80ad6f386462649fd0e5fd69f3b947be741a8f8624cad4762e14aa3" + }, + { + "path": "wordpress/wp-admin/js/dashboard.min.js", + "offset": 4292827, + "size": 8862, + "sha256": "ebf1de5f790e4d7c5ceef7ce718150e4a4462e6bfaf2d6455aa0da7490c32acc" + }, + { + "path": "wordpress/wp-admin/js/edit-comments.js", + "offset": 4301689, + "size": 38139, + "sha256": "24d11804da0c3518c4cb4eac2e7e9fe19809fc3588d68d2471c9154aeeddcbc0" + }, + { + "path": "wordpress/wp-admin/js/edit-comments.min.js", + "offset": 4339828, + "size": 15515, + "sha256": "7707356945ee55ab21d26b8bd5507854e1f3020ef1bf10666e420478f182d114" + }, + { + "path": "wordpress/wp-admin/js/editor-expand.js", + "offset": 4355343, + "size": 42606, + "sha256": "837782a41b89fe6d44d0b6a54c240b2fed4580a2073559f6e0bcdee3b6ab1731" + }, + { + "path": "wordpress/wp-admin/js/editor-expand.min.js", + "offset": 4397949, + "size": 13451, + "sha256": "bde4fc357e5d6ec3e588a6b5a3320e187e13f5d2c00e2c406a4a85cf06c46091" + }, + { + "path": "wordpress/wp-admin/js/editor.js", + "offset": 4411400, + "size": 45055, + "sha256": "e43c80e0e937dc7e738e254405d6020c5dfd2265f12ce915c32c1a41788ae56f" + }, + { + "path": "wordpress/wp-admin/js/editor.min.js", + "offset": 4456455, + "size": 13085, + "sha256": "6d00f39c080d764f6924a2b2242b1caab820b95c8ba288c3cbf8e05f6c46e59e" + }, + { + "path": "wordpress/wp-admin/js/farbtastic.js", + "offset": 4469540, + "size": 7849, + "sha256": "36f87cb710c7fb702fe3545a0d1ab22758f18f68023b096021c66cf6841db50f" + }, + { + "path": "wordpress/wp-admin/js/gallery.js", + "offset": 4477389, + "size": 5543, + "sha256": "cfd71b1d3674d0d92ada80dcf599021cc4217e2f4e69e405ef7e55ccde41a0f7" + }, + { + "path": "wordpress/wp-admin/js/gallery.min.js", + "offset": 4482932, + "size": 3741, + "sha256": "4a3feb6212b698ef869d410c49454572ec2d66060182df056a05a416815ed74b" + }, + { + "path": "wordpress/wp-admin/js/image-edit.js", + "offset": 4486673, + "size": 40936, + "sha256": "ff59de5eaf42d1260fb63a051d8e5b890dc4016b5bdd92d235cafaf93bce0168" + }, + { + "path": "wordpress/wp-admin/js/image-edit.min.js", + "offset": 4527609, + "size": 15515, + "sha256": "cf3f12afdb7fae12fbab700df421489ad9c4201e4d72ee25cf7c0c12a7073296" + }, + { + "path": "wordpress/wp-admin/js/inline-edit-post.js", + "offset": 4543124, + "size": 20685, + "sha256": "e21c3642dc1573460cb8da1c6fb32d5d6dabc361a9327dcf883ef272db967040" + }, + { + "path": "wordpress/wp-admin/js/inline-edit-post.min.js", + "offset": 4563809, + "size": 9613, + "sha256": "042d936d064f75f7df0df86d0fb1c5adb5c7c7f49d785bfc284d24a5c6cd2e98" + }, + { + "path": "wordpress/wp-admin/js/inline-edit-tax.js", + "offset": 4573422, + "size": 7797, + "sha256": "9690b9da60da0ea42b1622c6637c3fa1a11e2eb0274cb3af377c3e899ecc54da" + }, + { + "path": "wordpress/wp-admin/js/inline-edit-tax.min.js", + "offset": 4581219, + "size": 2997, + "sha256": "8c59c0c50e2494833c928289b5a77fb6c97fe1ec077711325053a8286747bc24" + }, + { + "path": "wordpress/wp-admin/js/iris.min.js", + "offset": 4584216, + "size": 23643, + "sha256": "ab52d53b8df3da6c127e38e08336523b1c5a7917cd850a2254ef58c3553dcf0d" + }, + { + "path": "wordpress/wp-admin/js/language-chooser.js", + "offset": 4607859, + "size": 890, + "sha256": "772d5e2d8e824f567b2dd20fbfe51c231d09efb47f3806eaf1f9ad74c8b1f442" + }, + { + "path": "wordpress/wp-admin/js/language-chooser.min.js", + "offset": 4608749, + "size": 423, + "sha256": "ccc2c261625acaeefe4b29d33f962ff6cff548edb2d37782f5522f3d3e56b816" + }, + { + "path": "wordpress/wp-admin/js/link.js", + "offset": 4609172, + "size": 4873, + "sha256": "28723b29ad63764fd8a473ff022d59ed384ca4fe60951319541bf6c0672a4727" + }, + { + "path": "wordpress/wp-admin/js/link.min.js", + "offset": 4614045, + "size": 2319, + "sha256": "cba41bf933db528241603a85f63481fc793a6bb4c4693691d5a83d80afc624d1" + }, + { + "path": "wordpress/wp-admin/js/media-gallery.js", + "offset": 4616364, + "size": 1303, + "sha256": "17aa86131027d304717035b7a0822949157eb962332b479a99e07df53b6b774a" + }, + { + "path": "wordpress/wp-admin/js/media-gallery.min.js", + "offset": 4617667, + "size": 611, + "sha256": "ba5fc060cb4b178391c111727ef4cff9800d60979141674961b0007dc397014d" + }, + { + "path": "wordpress/wp-admin/js/media-upload.js", + "offset": 4618278, + "size": 3465, + "sha256": "05658109605b673a44ca90dd1a85e646b9f92bc0862e26abb1b5527e977fd9c6" + }, + { + "path": "wordpress/wp-admin/js/media-upload.min.js", + "offset": 4621743, + "size": 1152, + "sha256": "30cac3693b641b4c3ef678eacd92f8dc38ac68c498bd4f9d92e832471ada8b78" + }, + { + "path": "wordpress/wp-admin/js/media.js", + "offset": 4622895, + "size": 6765, + "sha256": "c166ec0a00fdc47ca7289cc354eda0a5f47330d8b00b24045960b48499736295" + }, + { + "path": "wordpress/wp-admin/js/media.min.js", + "offset": 4629660, + "size": 2439, + "sha256": "c51d563411bcf18d5899e9166f032d6a4e8373ee950c06f4bdc98d98372d49f0" + }, + { + "path": "wordpress/wp-admin/js/nav-menu.js", + "offset": 4632099, + "size": 62617, + "sha256": "22a82cd753b04e837aa06db170d76236a316b15bbdadd6cc1f39baeb17c76424" + }, + { + "path": "wordpress/wp-admin/js/nav-menu.min.js", + "offset": 4694716, + "size": 30785, + "sha256": "896736081006fbbfa1db75f7f8b1b1bba497db25455b21de8b85a13c4873e944" + }, + { + "path": "wordpress/wp-admin/js/password-strength-meter.js", + "offset": 4725501, + "size": 4236, + "sha256": "64c6796bc2c2106690eb64cdd094f0b77543a01c16936754e6ed93457587c2a7" + }, + { + "path": "wordpress/wp-admin/js/password-strength-meter.min.js", + "offset": 4729737, + "size": 1123, + "sha256": "fcbe9e9ff2d1c20cab10bf43dc49914e188b44ae21f34257b4a0ef5cae90f7ac" + }, + { + "path": "wordpress/wp-admin/js/password-toggle.js", + "offset": 4730860, + "size": 1339, + "sha256": "00e9f0b94e9b0660fd3a540d17b2982977337f2a2cf4e1d5fa39b0041b74f091" + }, + { + "path": "wordpress/wp-admin/js/password-toggle.min.js", + "offset": 4732199, + "size": 847, + "sha256": "c4da88173fc9a464b849bfa20d369c1818ca5cef8db2b1b3094efca9c356e54e" + }, + { + "path": "wordpress/wp-admin/js/plugin-install.js", + "offset": 4733046, + "size": 7086, + "sha256": "359aa4d8d8a2bf75376208a72f739f8f46cb2793073e62724b814815953210a5" + }, + { + "path": "wordpress/wp-admin/js/plugin-install.min.js", + "offset": 4740132, + "size": 2403, + "sha256": "083e52b7454ef9a3568425fa0229104c6f559492cc053eace444b716ff72d399" + }, + { + "path": "wordpress/wp-admin/js/post.js", + "offset": 4742535, + "size": 40486, + "sha256": "855e630364d5efc7f106926facc3e214324b731234014b477a2bac60b525e39e" + }, + { + "path": "wordpress/wp-admin/js/post.min.js", + "offset": 4783021, + "size": 19407, + "sha256": "1cf7ee5c9b9addb379f051791678f27d62cb204834576a4ed6f952ae340bb7a2" + }, + { + "path": "wordpress/wp-admin/js/postbox.js", + "offset": 4802428, + "size": 18937, + "sha256": "9a76b7cb4d29c5748e0267bbbde2d07b8f98ccce9bf0f51fc91034feb03d8e01" + }, + { + "path": "wordpress/wp-admin/js/postbox.min.js", + "offset": 4821365, + "size": 6761, + "sha256": "c24dc5395b68297e01b712c26eb56b1664d11172b8490f4628e6237a50a885d4" + }, + { + "path": "wordpress/wp-admin/js/privacy-tools.js", + "offset": 4828126, + "size": 10923, + "sha256": "3c7aaab276cae505b188ad5b07f39c081c2f0815d5d891feb82588b456a3b3c4" + }, + { + "path": "wordpress/wp-admin/js/privacy-tools.min.js", + "offset": 4839049, + "size": 5154, + "sha256": "78c249523051d927b8e3da8c39a9b6fed6fb0bf45085ca6795af07cfe0de092d" + }, + { + "path": "wordpress/wp-admin/js/revisions.js", + "offset": 4844203, + "size": 34729, + "sha256": "1bbbfba6808d6d77d3738ed94a911984dea511203518e8548e0c5cda9c86a01d" + }, + { + "path": "wordpress/wp-admin/js/revisions.min.js", + "offset": 4878932, + "size": 18401, + "sha256": "a5982bba101b970022fb643e17f2a14cce55a443f03433f8981a353d4656557f" + }, + { + "path": "wordpress/wp-admin/js/set-post-thumbnail.js", + "offset": 4897333, + "size": 876, + "sha256": "45ed6c0473f932521499fa2f8481ad2664820d4d8683729b47d571846751f480" + }, + { + "path": "wordpress/wp-admin/js/set-post-thumbnail.min.js", + "offset": 4898209, + "size": 620, + "sha256": "3d4c86520283172c8e3d257b722aeb41cb698e5b0c120c8f02f150a05c9c14aa" + }, + { + "path": "wordpress/wp-admin/js/site-health.js", + "offset": 4898829, + "size": 13880, + "sha256": "6f0974ebc4aafce679a5a5dfe7ac96d00120657022232e2dacc19cb3ddb4457e" + }, + { + "path": "wordpress/wp-admin/js/site-health.min.js", + "offset": 4912709, + "size": 6474, + "sha256": "de9b6eb0c37106635ff124ceddcecad2e7990b72c96b2e3c311ddbf26c5ed9cd" + }, + { + "path": "wordpress/wp-admin/js/site-icon.js", + "offset": 4919183, + "size": 6243, + "sha256": "10e84b80f75d5aac990350c058cf783613d05ea21e9ae56b495c7c7d4bad7367" + }, + { + "path": "wordpress/wp-admin/js/site-icon.min.js", + "offset": 4925426, + "size": 2254, + "sha256": "898cf4161128cad5df0a5a80467d826f7d4f56ed329cdecc28cb657fc64b74c8" + }, + { + "path": "wordpress/wp-admin/js/svg-painter.js", + "offset": 4927680, + "size": 3280, + "sha256": "3e3335d2499ed745d587392cd5c8ef4d3c4f1076d5cbe5b37e972904d6f27cea" + }, + { + "path": "wordpress/wp-admin/js/svg-painter.min.js", + "offset": 4930960, + "size": 1567, + "sha256": "4f24d19a0f9f092e3427da839e52f0d5e952ee9f9ff9ac8d7d1d8c36db1616c9" + }, + { + "path": "wordpress/wp-admin/js/tags-box.js", + "offset": 4932527, + "size": 11140, + "sha256": "e70afce0cb43e5c7c80a4a5f7eaaf6a9543c850755306c50552e8076db00de17" + }, + { + "path": "wordpress/wp-admin/js/tags-box.min.js", + "offset": 4943667, + "size": 3077, + "sha256": "61146f3d98d80489ec71ee490f96f72c21c367b256b4cebf2536dfa9c8059a2d" + }, + { + "path": "wordpress/wp-admin/js/tags-suggest.js", + "offset": 4946744, + "size": 5771, + "sha256": "8c6eedbcc690fe1513b44b04a6839003c8f9cccbcdaa31093e666e3320b185b6" + }, + { + "path": "wordpress/wp-admin/js/tags-suggest.min.js", + "offset": 4952515, + "size": 2269, + "sha256": "a311334c062c19e78d44c19dfe212e363c51145bfee24732f7d6e7baf3816690" + }, + { + "path": "wordpress/wp-admin/js/tags.js", + "offset": 4954784, + "size": 6098, + "sha256": "66a34b7faab76dbb6e32ed4492dfe9a88243f4af83d4d53bed51fe407ee9c0b5" + }, + { + "path": "wordpress/wp-admin/js/tags.min.js", + "offset": 4960882, + "size": 2467, + "sha256": "419af705bf22cce6ade7261b977407eb6b4ade7a6132776c6bbb3da843b17955" + }, + { + "path": "wordpress/wp-admin/js/theme-plugin-editor.js", + "offset": 4963349, + "size": 26176, + "sha256": "52a2c048c651efb307f6c51566699fe4978c0e51253057da8461cecaff1439a2" + }, + { + "path": "wordpress/wp-admin/js/theme-plugin-editor.min.js", + "offset": 4989525, + "size": 12104, + "sha256": "f7061dfa487ec2d02f177d005cef4cd30145360fb9f8d2da9021c4d70bc6ebd7" + }, + { + "path": "wordpress/wp-admin/js/theme.js", + "offset": 5001629, + "size": 56263, + "sha256": "8a807453f3f5ed7cfbee55a46264148e35373b97917a34c91b82202cf7d54ad3" + }, + { + "path": "wordpress/wp-admin/js/theme.min.js", + "offset": 5057892, + "size": 27142, + "sha256": "9ecb8b3622f68fd0d49193ad6fb0793984334b4aca48f6bd2a86bec235d29c2d" + }, + { + "path": "wordpress/wp-admin/js/updates.js", + "offset": 5085034, + "size": 111999, + "sha256": "c908a8ea6f2bce6f8bd405aff9cdfcda4e60f0f689cbbd9c0a494aa931d8949c" + }, + { + "path": "wordpress/wp-admin/js/updates.min.js", + "offset": 5197033, + "size": 48443, + "sha256": "080fda8ff179bf8248c2b02ed7ba9944b07d73ad17223da75f84741e0a4d9b50" + }, + { + "path": "wordpress/wp-admin/js/user-profile.js", + "offset": 5245476, + "size": 18343, + "sha256": "c6c1d800b1bbb622b840c45fc80df3e044c471d70a82d3e677342d2e598bfea0" + }, + { + "path": "wordpress/wp-admin/js/user-profile.min.js", + "offset": 5263819, + "size": 7997, + "sha256": "f34c5278ba34eec8010c631605a159ff12376367e386f7548fe5b1fb3508226c" + }, + { + "path": "wordpress/wp-admin/js/user-suggest.js", + "offset": 5271816, + "size": 2301, + "sha256": "a175576874b8c14fc86cb0af9557d82e0a20ac6ed0a02a2bd819c1a578191c71" + }, + { + "path": "wordpress/wp-admin/js/user-suggest.min.js", + "offset": 5274117, + "size": 676, + "sha256": "4c5d996982ddca6e732e0c1f5fc4c03893e10c32b85c15c14c55b52e55aae708" + }, + { + "path": "wordpress/wp-admin/js/widgets.js", + "offset": 5274793, + "size": 23098, + "sha256": "4849d9a168e3bdd264cdd6928cc7cf2aa7478a1cdf0676ea30d05cbcdbaac2f1" + }, + { + "path": "wordpress/wp-admin/js/widgets.min.js", + "offset": 5297891, + "size": 12609, + "sha256": "7dba555874f879bcfae895b62d0e339c6cb51d071ea816d8a7e215e986be72d9" + }, + { + "path": "wordpress/wp-admin/js/widgets/custom-html-widgets.js", + "offset": 5310500, + "size": 15759, + "sha256": "608f635fe3a61588a4960dae3147805a56fef6911c43af568047c680bac40c84" + }, + { + "path": "wordpress/wp-admin/js/widgets/custom-html-widgets.min.js", + "offset": 5326259, + "size": 5582, + "sha256": "4abbbe9a6c0dc2395190bc873031cef8a0a5d90cb60f9972292229de0ea44f15" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-audio-widget.js", + "offset": 5331841, + "size": 4284, + "sha256": "216364638a8c62e4afb056f4a066f4d7d3414f12a95da84f48b35644a0c3c40d" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-audio-widget.min.js", + "offset": 5336125, + "size": 1447, + "sha256": "35164740a9d4598e69be8b3615951d29d33f344a2e6c7be891bffc7ec54bdb81" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-gallery-widget.js", + "offset": 5337572, + "size": 10354, + "sha256": "98523ed2b52b399c5a97ccff2f31f589fbdf4d76f18f747d2380be050b90b22e" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-gallery-widget.min.js", + "offset": 5347926, + "size": 3766, + "sha256": "67688f4cb6b2c5634cc2410c80f708961af530c71941dd2387d5835e6d7fa7ec" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-image-widget.js", + "offset": 5351692, + "size": 5468, + "sha256": "827ab852a45708f7f6c2d6c2491f669d7b1e9faa43df231cf4e6b0e15e1841f5" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-image-widget.min.js", + "offset": 5357160, + "size": 2024, + "sha256": "e4b764bdcb3b83f96e4c4762081d107df57a54d7e53d5cee596c6c4d2234dcdb" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-video-widget.js", + "offset": 5359184, + "size": 7020, + "sha256": "1c4405c8a918ebeb5bda9e55a30f12ddbce225d4cc88b067d4b48cd457bf79c9" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-video-widget.min.js", + "offset": 5366204, + "size": 2702, + "sha256": "f750d2d72e568ea9f8f5c3958b65e8332731956954383a0c25bf83ad2bf1c921" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-widgets.js", + "offset": 5368906, + "size": 42864, + "sha256": "b4bdf1c24f144e7823710b7fd6f92626c7857115f33558251cb62f6839cb0b55" + }, + { + "path": "wordpress/wp-admin/js/widgets/media-widgets.min.js", + "offset": 5411770, + "size": 14241, + "sha256": "9bbd5c521373f6f3747cb977c64ee6d178762426c157b9b0a69a871f2f523021" + }, + { + "path": "wordpress/wp-admin/js/widgets/text-widgets.js", + "offset": 5426011, + "size": 18049, + "sha256": "047e9e7a5bef52e640c6c6c4de25217bd634a3654a72e15f44c29386e8ae84ea" + }, + { + "path": "wordpress/wp-admin/js/widgets/text-widgets.min.js", + "offset": 5444060, + "size": 5853, + "sha256": "25dc01e82dc413f0368e04a62798c8a466d601169aace2806a0f9139d16a9af3" + }, + { + "path": "wordpress/wp-admin/js/word-count.js", + "offset": 5449913, + "size": 7696, + "sha256": "109529f3093904cff067c32d9e7a8e8de12c84ccb9b52459faf2e4fa07bc18b2" + }, + { + "path": "wordpress/wp-admin/js/word-count.min.js", + "offset": 5457609, + "size": 1530, + "sha256": "8aac336dbcba85411bc289bcf8d74e38bd91f76e85eb3cf67091a848e72e58a2" + }, + { + "path": "wordpress/wp-admin/js/xfn.js", + "offset": 5459139, + "size": 740, + "sha256": "39723ba0db0cadb2ff15b3429031a11d5dcf805064f890a709c77e0aec1a993d" + }, + { + "path": "wordpress/wp-admin/js/xfn.min.js", + "offset": 5459879, + "size": 458, + "sha256": "c0fd9ca8928a25cb85a53700b9308faa500175fe20eed6cc1599d641ddead761" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/css/editor-style.css", + "offset": 5460337, + "size": 157, + "sha256": "88d55cbf818b1f096163378de3da66db215c9eb19f18a5463fe58e46b4efd240" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/beiruti/Beiruti-VariableFont_wght.woff2", + "offset": 5460494, + "size": 176048, + "sha256": "8e90b74450f015ce63dd5e0b4b05cda71ca342df0f73eb5f17092d7e704a0936" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-code/FiraCode-VariableFont_wght.woff2", + "offset": 5636542, + "size": 106112, + "sha256": "b6e7adca884c51549c66384fec54747fa2967ac6ad5655ed02c5da5b682a9ba2" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-Black.woff2", + "offset": 5742654, + "size": 153368, + "sha256": "642c8e8c7eb14a22d9bfda19cc8131e66a5bd8594646b000e83dd5b593907a3a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-BlackItalic.woff2", + "offset": 5896022, + "size": 165936, + "sha256": "95e6ec6a4014fdd0e9aea3d84bd8220ce094554f19f588725e8a6b6a2e9b532f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-Bold.woff2", + "offset": 6061958, + "size": 167008, + "sha256": "7dff4e2351ca54fae96180e79ab1d1fdeb74ebb6e6cba53a7d9d7b4941600ada" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-BoldItalic.woff2", + "offset": 6228966, + "size": 174164, + "sha256": "96a63974b93a1cbc4e5eafbc0cdda533ec72dac650c5f97b8f8e3ed56a1c6816" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-ExtraBold.woff2", + "offset": 6403130, + "size": 168372, + "sha256": "133482a0c7aa051cd89a9b6bf5ee990bad1bf1a4b37918b96a0d626fdb529bec" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-ExtraBoldItalic.woff2", + "offset": 6571502, + "size": 180240, + "sha256": "7018e9e81ba2f8cfb38a902a5c0aa82990f58b561ae3b67c8b307654be2d000f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-ExtraLight.woff2", + "offset": 6751742, + "size": 155152, + "sha256": "2705525a8505203fd9bcdeadcccfd7f341999fa6f3003d1e0b598c23d1b8427f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-ExtraLightItalic.woff2", + "offset": 6906894, + "size": 161880, + "sha256": "0315d7ad7bdec5fc809a36075cd4ebfceaf3156a46b2a5fd66aacfc4390ef623" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-Italic.woff2", + "offset": 7068774, + "size": 162676, + "sha256": "1d70c09acc08f385ff6874c81ba8fe296d9e393d8d64615f2724113735d127d8" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-Light.woff2", + "offset": 7231450, + "size": 155788, + "sha256": "2315d21be4c62def3fa08de87f29c327187affcd7759a46596d5bacfeb2ff221" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-LightItalic.woff2", + "offset": 7387238, + "size": 161428, + "sha256": "e60a5ffd8eb9bcb24c75e4468b7ed5c022fd304b745bbba3f96cd79ecdc9d613" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-Medium.woff2", + "offset": 7548666, + "size": 159500, + "sha256": "14d421d4fb35d56b40e958cc9b64eb1528d1822bb75251623955c873a9a175d3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-MediumItalic.woff2", + "offset": 7708166, + "size": 166916, + "sha256": "a2eaf8dd1a852211cb45b8d717bc5c244aad339e4be7d011e1e1e31df4cebf0e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-Regular.woff2", + "offset": 7875082, + "size": 155928, + "sha256": "51000d3cc8a601427bdb88625275e0eefc00570f4f2ab7a926fa336abee7098f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-SemiBold.woff2", + "offset": 8031010, + "size": 166908, + "sha256": "01ca0c4a1f02dd4324721cf8766bcbb2edca4dee0fb8884a66e58e231ed62d55" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-SemiBoldItalic.woff2", + "offset": 8197918, + "size": 174264, + "sha256": "9b0883275fd15f1bc8df4b5246a1b5bfd84b3124c26d46125b5a611a903d6b22" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-Thin.woff2", + "offset": 8372182, + "size": 150328, + "sha256": "6eb81ea3312b03d0ea3eaa4f7592d71c94d684d43274f91e384aa89a30a074db" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/fira-sans/FiraSans-ThinItalic.woff2", + "offset": 8522510, + "size": 156216, + "sha256": "145ff97440de67d4b28422278c7f6d1b4fb89689b486c48973a17a6e54b84247" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-Black.woff2", + "offset": 8678726, + "size": 106188, + "sha256": "cea0dadaefea7554bad3f09a5b376be8143ace4a5e6e57ae5184a39c47b3a4ad" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-BlackItalic.woff2", + "offset": 8784914, + "size": 108440, + "sha256": "bfedefd7d42a6c362412323b2732f416b206135f4dadc0d4706e1d4898e8dd52" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-Bold.woff2", + "offset": 8893354, + "size": 107992, + "sha256": "957232e2794ef923054fc1bad67f5ed20331b5d48b68add1c2891521dff751dc" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-BoldItalic.woff2", + "offset": 9001346, + "size": 110300, + "sha256": "5b005b58015742dc6c9296950ca9e9fa2a3f369095a56f49ca7407de57d0f298" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-ExtraBold.woff2", + "offset": 9111646, + "size": 110092, + "sha256": "fa3f25af9ec2f2aae4a545693b0f03d81f76717de7249bb15aa7172b6db3a631" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-ExtraBoldItalic.woff2", + "offset": 9221738, + "size": 110412, + "sha256": "dc040b41485e1b7bdf0ef2b71724e187c1076ea782d8633d5cb4c4085e11145a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-ExtraLight.woff2", + "offset": 9332150, + "size": 104936, + "sha256": "6494e44c0c69338a74434c7641050df4a414de86e4dfc697028d24b9a882dd08" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-ExtraLightItalic.woff2", + "offset": 9437086, + "size": 104392, + "sha256": "c3d834b8c5cea89c05ae74344a0edcb6ff16010e438d6bd7fbf1de691389575c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-Light.woff2", + "offset": 9541478, + "size": 108112, + "sha256": "c3f61d20c2ac7bc7575d07a24e28e4c6dac25c76861a85432db9248106607873" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-LightItalic.woff2", + "offset": 9649590, + "size": 105784, + "sha256": "8bb78ec4296844e3e1119ec80e84bf8536d5604f8fae879ce3a78d56ba912a49" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-Medium.woff2", + "offset": 9755374, + "size": 106504, + "sha256": "11f70b5607abf907f4e94010983a65a79fa0b62bab77f2409de9bdf96d3de89c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-MediumItalic.woff2", + "offset": 9861878, + "size": 108808, + "sha256": "3739a6646cbab6c4366974ef35b38943ce7a8dd331761675c1c03a050d5b77c9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-Regular.woff2", + "offset": 9970686, + "size": 94076, + "sha256": "5820db0b112951d51ebc03fe79d2d348aa6e8aa5dc7546419c9f2e81a750a089" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-RegularItalic.woff2", + "offset": 10064762, + "size": 95608, + "sha256": "5908333585e249cc28887a4df8be77dbe1a121bbb6900b1a46129a7be17b2fb5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-SemiBold.woff2", + "offset": 10160370, + "size": 107448, + "sha256": "131c987396dc8c6e8cbc0aeffb559be5526658db3080555b3f36f45f2781abde" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/literata/Literata72pt-SemiBoldItalic.woff2", + "offset": 10267818, + "size": 109864, + "sha256": "a3c203c1422345f40c604f9a9879b90a31687558d1df85c092769e8320633a22" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/manrope/Manrope-VariableFont_wght.woff2", + "offset": 10377682, + "size": 53600, + "sha256": "ce340d48531930f3f2c8b7c47d149f82c9f4413548dd216e0f4d9af94a87c374" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/platypi/Platypi-Italic-VariableFont_wght.woff2", + "offset": 10431282, + "size": 71768, + "sha256": "ad32d2014933024272c1b57754bb83a99b7db4ec6207bf7872a863427f4aa502" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/platypi/Platypi-VariableFont_wght.woff2", + "offset": 10503050, + "size": 71128, + "sha256": "82827e4242ca335ff90b46798f36389e1068a62f93de2b92c50363a1e9bc4720" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/roboto-slab/RobotoSlab-VariableFont_wght.woff2", + "offset": 10574178, + "size": 115804, + "sha256": "e2847993dc4d9d61b54d9d5926d0d8cea31966a61922051701ab925b1a5d82d6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/vollkorn/Vollkorn-Italic-VariableFont_wght.woff2", + "offset": 10689982, + "size": 163352, + "sha256": "c318c94dcd6ccb9a9168852ccd400ef95f82811e1a94271bd4baa1b04eea1065" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/vollkorn/Vollkorn-VariableFont_wght.woff2", + "offset": 10853334, + "size": 193964, + "sha256": "64632000153498efba5bd2d21d48a477ee71cee990309e154eb02850be77f508" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/ysabeau-office/YsabeauOffice-Italic-VariableFont_wght.woff2", + "offset": 11047298, + "size": 132592, + "sha256": "190540b0739b192cf56b490057761276d62d4d671cac449eed45b78bfd99e486" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/fonts/ysabeau-office/YsabeauOffice-VariableFont_wght.woff2", + "offset": 11179890, + "size": 166928, + "sha256": "a4af4da9ee40ce3010b64215c2caaa00a6770b86e28ef41a3eac337a34bbce51" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/404-image.webp", + "offset": 11346818, + "size": 129970, + "sha256": "6873efa2823693885dcb88526d155c461d23d785f817249e5721b92025133059" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/agenda-img-4.webp", + "offset": 11476788, + "size": 14542, + "sha256": "725cd8af715cb5aa4acfc49b6a0e1361bdad5d93300f5fc8dcbfd4131d95c5b7" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/akaka-falls-state-park-flora.webp", + "offset": 11491330, + "size": 6116, + "sha256": "98d01eed7ab7a680a877cf8bbd0942eff46d9391c0f78cd6907208d2516c1474" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/book-image-landing.webp", + "offset": 11497446, + "size": 19734, + "sha256": "687795b64aaec5cf40c39edd43cdd4e775c8edf00912fe6d532e4c7deaa85697" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/book-image.webp", + "offset": 11517180, + "size": 20980, + "sha256": "47a1cb631eb70c7d2523f739b6e733b97dad2d19c1c7301bc8cf45c036013130" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/botany-flowers-closeup.webp", + "offset": 11538160, + "size": 60176, + "sha256": "29d0c0b5ac0c692f00a5ff0bcc7ae7e169448df3ce62f3b81cd9cba09ff31220" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/botany-flowers.webp", + "offset": 11598336, + "size": 99800, + "sha256": "06357094e33c9ff2c0fda9a07b92b97c68cac3501dc642659671796f2f09525d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/campanula-alliariifolia-flower.webp", + "offset": 11698136, + "size": 42036, + "sha256": "8941ac916477843fb8f0fc65294b21f5b951682cd152cbd48509669f96444409" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/category-anthuriums.webp", + "offset": 11740172, + "size": 27472, + "sha256": "0b9ababaf1bfa9dc2b751dbf4d2554c85550c5330e075cb4fa76d9bacecef939" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/category-cactus.webp", + "offset": 11767644, + "size": 51878, + "sha256": "3c15b1aeb1b0f30b1cc8564fc961208f64e54985954b642420b561af56f886b2" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/category-sunflowers.webp", + "offset": 11819522, + "size": 18558, + "sha256": "279dc1ce52c36fd393565fae44d38058d234cd772d6801b340ba1cbbe0ff24dc" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/coming-soon-bg-image.webp", + "offset": 11838080, + "size": 88600, + "sha256": "65d5bf334ea064e89b0defd222c49f8b3016ec68a332f7a3b804399c16f992d5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/coral-square.webp", + "offset": 11926680, + "size": 27554, + "sha256": "f0cdf27f45840b9cbe90832e2905835a5c66f146f8a5da75d107b6c3257fba95" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/dallas-creek-square.webp", + "offset": 11954234, + "size": 7768, + "sha256": "863c2b5c952a667ce5ee956e0aa192e707752fccdf7a8033855c80f09d716d8e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/delphinium-flowers.webp", + "offset": 11962002, + "size": 42214, + "sha256": "5637fc22d8877d28cbc9a328234d433e8390769e79e0f617b7916fd098e1a771" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/flower-meadow-square.webp", + "offset": 12004216, + "size": 29920, + "sha256": "43b38ffa9da1ea016653a0fb85b7552cebccc54ce8f1006825c3bc93cea3303d" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/grid-flower-1.webp", + "offset": 12034136, + "size": 19654, + "sha256": "3b3cd5620955fe7e2ea3623efc6a71d44ac0e31042792168579d247a9f07f2c9" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/grid-flower-2.webp", + "offset": 12053790, + "size": 82646, + "sha256": "2bbc0f98aa92eff12a0df5b0bd6f67fb87287777a625109f3cc9e8276edd395a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/hero-podcast.webp", + "offset": 12136436, + "size": 60794, + "sha256": "e5a6f88a6d7a96402bfc374805f9ecc4eefc8741e46785faecad1f4c03b1d620" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/link-in-bio-background.webp", + "offset": 12197230, + "size": 112516, + "sha256": "a24fed0ba3eeff6424a3194c43a04e43bee7b8c77f78e9161908ff61c5dec907" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/link-in-bio-image.webp", + "offset": 12309746, + "size": 95598, + "sha256": "1ab577ff8808408939ce19017bf14007b4ede6b68878eb67e74eeb2433afa585" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/location.webp", + "offset": 12405344, + "size": 141020, + "sha256": "0f74537edcd808204b55faece3450bb380170ff874a318dae4fdb917737bc257" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/malibu-plantlife.webp", + "offset": 12546364, + "size": 5194, + "sha256": "760ce72f5f054b54adfccbbc1cd82645d9b221bcbccc2e7865349d6de140b64c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/man-in-hat.webp", + "offset": 12551558, + "size": 35386, + "sha256": "820e58cd2097bfc7448278d5935ff66446559811dcf4f7fe681ffe1725c9f354" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/marshland-birds-square.webp", + "offset": 12586944, + "size": 5886, + "sha256": "48ebf1ce4f2b1117d998e2fed70e31c931ca238d7eab6bc95f14445a23ff920e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/northern-buttercups-flowers.webp", + "offset": 12592830, + "size": 35820, + "sha256": "931de59668e822e446369191acd45b16748575c192903996f0472db891a96cf7" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/nurse.webp", + "offset": 12628650, + "size": 21850, + "sha256": "ed2c7b28d6d933a2638119721a2daed36295e202c1e57bed203af37a71fdd357" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/parthenon-square.webp", + "offset": 12650500, + "size": 36124, + "sha256": "6e4b6f0f31dfc2d59a1d96773f6e1a244c8ea6ad312cde5d6c7bc0eb4ab350d8" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/poster-image-background.webp", + "offset": 12686624, + "size": 222864, + "sha256": "fc678b6db26e31172fc389976420583a47504c065cbf0f66c3c19d03a4e907f1" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/red-hibiscus-closeup.webp", + "offset": 12909488, + "size": 56851, + "sha256": "e2bd276ef0f4dd60e89d64d40c0b34caee38b088801c2f4199e1bbccb7a9f940" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/ruins-image.webp", + "offset": 12966339, + "size": 68384, + "sha256": "af84076ef83896e9135f24b9b4b42eb4c13d1bae7afd798a5d9e6bd588038111" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/services-subscriber-photo.webp", + "offset": 13034723, + "size": 52542, + "sha256": "1ba8a2a5be79f8df1a2cee680bff5733998f4d48e10a300a7e600dc1c1cf9fac" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/star-thristle-flower.webp", + "offset": 13087265, + "size": 21338, + "sha256": "e5eb47d9f034e984f270b5e9eb5ae2f0be0c9abb2c806f71c0a2b0f25422d5aa" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/typewriter.webp", + "offset": 13108603, + "size": 97944, + "sha256": "2d6014a7c9be0b64d7f18d2c65e30bf4cedd1bd1d5dcf0e1cb99b35b4670c38a" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/vash-gon-square.webp", + "offset": 13206547, + "size": 28214, + "sha256": "cd002e55371e7992b699db416113475bc4752968b15ac7297a42b1c533649876" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/assets/images/woman-splashing-water.webp", + "offset": 13234761, + "size": 57776, + "sha256": "28d34b8d619a795f88f315dbe60ab45d13053fcb253c17ff727c6cb9165a9b69" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/screenshot.png", + "offset": 13292537, + "size": 216683, + "sha256": "a0285155b82c0d210810be1efc9ea5242fa1088a86fe929467988bfc047a1949" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/style.css", + "offset": 13509220, + "size": 3009, + "sha256": "069ec4541fc7a3ee2e49d69fefc9bb648c05091ba336bb5c6e9ae72998f0af46" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfive/style.min.css", + "offset": 13512229, + "size": 611, + "sha256": "a30b0b32946b2d9800eed84510b635e7330ffa19447fe5688f74b28b1daa6cec" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/css/button-outline.css", + "offset": 13512840, + "size": 357, + "sha256": "d109c780eab486a0d2bc684015cfb5b4a33e754ed6f39f10321da70abe9b2987" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/fonts/cardo/cardo_italic_400.woff2", + "offset": 13513197, + "size": 105184, + "sha256": "c3b2ff62e3ac4219811de0c709bd0d81d962a88dc87a598ac19b20f58f960136" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/fonts/cardo/cardo_normal_400.woff2", + "offset": 13618381, + "size": 146060, + "sha256": "aa8042a77500cfe4a4893e2b7edbd54dded92768e40418fa0665bec8aae9ae18" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/fonts/cardo/cardo_normal_700.woff2", + "offset": 13764441, + "size": 132564, + "sha256": "bca42f8b5a5283b975115421b3b4171cf89b88bf39ef52538c9494860414c292" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/fonts/instrument-sans/InstrumentSans-Italic-VariableFont_wdth,wght.woff2", + "offset": 13897005, + "size": 94644, + "sha256": "fcad85747412f94c07885fb4ab6d517d033052ceb3b6b4b7934abd6eb68c9e94" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/fonts/instrument-sans/InstrumentSans-VariableFont_wdth,wght.woff2", + "offset": 13991649, + "size": 88476, + "sha256": "8d93e0761b10a433a72768b15bcf1bd293b7b20b9c74d8299fda3cec0b6e59fc" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/fonts/inter/Inter-VariableFont_slnt,wght.woff2", + "offset": 14080125, + "size": 326628, + "sha256": "e931823ffd0b6cfd1624e3a7c1c49861ed3420297862e727f07e04c8be1cc89b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/fonts/jost/Jost-Italic-VariableFont_wght.woff2", + "offset": 14406753, + "size": 62072, + "sha256": "bdaa8596805b7f90fc1bc761ba72e4e1e411414ac20b02ffe6cb524c86dd6233" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/fonts/jost/Jost-VariableFont_wght.woff2", + "offset": 14468825, + "size": 50396, + "sha256": "22f00b0535a26118fdf0bce85f45c53c87c8ce959f6fe9cb809de921df065fa7" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/abstract-geometric-art.webp", + "offset": 14519221, + "size": 100774, + "sha256": "44c0f31ccba5dc8574436e541c502e04be66b2118fc7758be6ce43e97184ee02" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/angular-roof.webp", + "offset": 14619995, + "size": 83964, + "sha256": "ca00f40190463960617430dd503e18ac4181fa58ea7173e60bd3d6b12fa15182" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/art-gallery.webp", + "offset": 14703959, + "size": 118230, + "sha256": "7b7ef1992e6ddc642b746e3f1136f971c09420664653b40d122c21ccdeacf82c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/building-exterior.webp", + "offset": 14822189, + "size": 199724, + "sha256": "7ab1e25df9f0e9825e577187a3c73ebda9c0a068d9b35d2a235b8e924b9c58e5" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/green-staircase.webp", + "offset": 15021913, + "size": 249360, + "sha256": "044e72c1ece83999a92fb880e935bde3f272cc0fd5e8d2c0e480662f3de79e17" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/hotel-facade.webp", + "offset": 15271273, + "size": 82374, + "sha256": "99639d08984605a70deefa2178b06cc11f93b5d09c262617a2f8ecf80f5145fe" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/icon-message.webp", + "offset": 15353647, + "size": 1258, + "sha256": "56407a43b35a9c160574d1b96be530586ef4eceba12792e1c7283ffc4e411582" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/museum.webp", + "offset": 15354905, + "size": 123690, + "sha256": "480d989bfa8ed057a5b287bcb02dd6d44603ee0015e497375a97d17e92516c6b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/tourist-and-building.webp", + "offset": 15478595, + "size": 66482, + "sha256": "0fc42e518e17733d6c766ceec3a2154896e332571f130fae27e4f20480e0980c" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/assets/images/windows.webp", + "offset": 15545077, + "size": 126244, + "sha256": "a8b72c3a34a4ac883406005c0fab343d64894c978b3dda1b6bb00eed01e9409e" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/screenshot.png", + "offset": 15671321, + "size": 721059, + "sha256": "03b8a4ebbb57e403f6aed234f408ceef42299491ca13cfd4a91d83b766672d0f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentyfour/style.css", + "offset": 16392380, + "size": 1201, + "sha256": "b425dcb68ff9c572878c2e2ffce1a9382812f3d2cad8124f3ef09bba45283d6f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/dm-sans/DMSans-Bold-Italic.woff2", + "offset": 16393581, + "size": 32784, + "sha256": "67a470d0ad82fd108edb771c9a731b7f2d4924f9827e94b77605b89882daefe6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/dm-sans/DMSans-Bold.woff2", + "offset": 16426365, + "size": 29272, + "sha256": "4e7327e3b5e640259d0370898211c1ff912cd28676c59ba140f32bf8be75351f" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/dm-sans/DMSans-Regular-Italic.woff2", + "offset": 16455637, + "size": 32492, + "sha256": "ce7aa0dbf9c32f6a9921d5cce21beb25dd3102d1b2c29858eb84a8ae6f3a5cdb" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/dm-sans/DMSans-Regular.woff2", + "offset": 16488129, + "size": 29272, + "sha256": "2c7afda5836bf01dd13de132b4e1302dd19fefb131ac7d1565c1c636cd228d19" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/ibm-plex-mono/IBMPlexMono-Bold.woff2", + "offset": 16517401, + "size": 35652, + "sha256": "ea03437f8e50e94b1c3b424167c0c445aeeb988257c2c4e82c50e5dd8a9407d3" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/ibm-plex-mono/IBMPlexMono-Italic.woff2", + "offset": 16553053, + "size": 38924, + "sha256": "668db08b6e90d1a8b1e91d7071c21e686d53f081464f0f175d4e3217a6e41099" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/ibm-plex-mono/IBMPlexMono-Light.woff2", + "offset": 16591977, + "size": 34700, + "sha256": "d781ad5d3d6e25bafcee0769e50f1baca154890275378258e1efcacff75ef5b1" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/ibm-plex-mono/IBMPlexMono-Regular.woff2", + "offset": 16626677, + "size": 34480, + "sha256": "99733e5b7f6707e5e903ff82b84b4e1d707b07e9c1bf10fa1403443010c5bf31" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/inter/Inter-VariableFont_slnt,wght.ttf", + "offset": 16661157, + "size": 803384, + "sha256": "b9a8e5e213977665be2a56db66945a16c686da3d9861af1e7851322cf15495ea" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.otf.woff2", + "offset": 17464541, + "size": 280296, + "sha256": "47fd41a11c4fbbecad3994ec6541cb136432051faa9d8f8cc798df2373e07084" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/source-serif-pro/SourceSerif4Variable-Italic.ttf.woff2", + "offset": 17744837, + "size": 346840, + "sha256": "679552970b131f8ab2f7f5d6202ad8cce0b63b89e110fdb6fa65fb1283fa399b" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.otf.woff2", + "offset": 18091677, + "size": 348988, + "sha256": "2db48f22fb9023578245181d313913cea19a438cf1b54a74639c555719186797" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/assets/fonts/source-serif-pro/SourceSerif4Variable-Roman.ttf.woff2", + "offset": 18440665, + "size": 428448, + "sha256": "bb8c4212414ce2a7887636e1c6eb1fd3ab46e4391226fd39653a2c2abbb17697" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/screenshot.png", + "offset": 18869113, + "size": 81654, + "sha256": "e598c7db3ccb99088442c56ae0fa385ebc8c0d3817e8f268507a958a88091ab6" + }, + { + "path": "wordpress/wp-content/themes/twentytwentythree/style.css", + "offset": 18950767, + "size": 1114, + "sha256": "be56f896f47ac1ed7de596bbe41df522ddea8f19b89a6236a85abe6a69796638" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-heading/style-rtl.css", + "offset": 18951881, + "size": 883, + "sha256": "a7a23769f84f139b5f2b84ee716ff217760a1d816915ec94ae30c6a2813a70dc" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-heading/style-rtl.min.css", + "offset": 18952764, + "size": 753, + "sha256": "8dc20377e42412d142cc15aba7910c060ee2cd952ff7a08a7ed5fd31ec2290cc" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-heading/style.css", + "offset": 18953517, + "size": 883, + "sha256": "a7a23769f84f139b5f2b84ee716ff217760a1d816915ec94ae30c6a2813a70dc" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-heading/style.min.css", + "offset": 18954400, + "size": 753, + "sha256": "8dc20377e42412d142cc15aba7910c060ee2cd952ff7a08a7ed5fd31ec2290cc" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-item/style-rtl.css", + "offset": 18955153, + "size": 519, + "sha256": "4b27fd73f73790a9684708f071082c65a9d3f4aed51f94af940a177316302186" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-item/style-rtl.min.css", + "offset": 18955672, + "size": 373, + "sha256": "bf1b9d24402a73b2f5f7cedf075d6483c0eb954d301866fe8269de0699fa560b" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-item/style.css", + "offset": 18956045, + "size": 518, + "sha256": "9ef016b8b75a286b71f67c00fc5d643442b92b1bc31d86f57ca9cec710030c80" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-item/style.min.css", + "offset": 18956563, + "size": 372, + "sha256": "7cd741776a642088c53b29ac0fe810b2a2c31316fe6ada5a35a8e9c8b50f6ce9" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-panel/style-rtl.css", + "offset": 18956935, + "size": 123, + "sha256": "f037519fb0b84e529070cec8678c6be0b058ed429d2a746d0c3e2cec4be01501" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-panel/style-rtl.min.css", + "offset": 18957058, + "size": 111, + "sha256": "4f932c154716a0972ac0d90e720c028b7a6a315df8e441bf25bb959d07f0ef07" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-panel/style.css", + "offset": 18957169, + "size": 123, + "sha256": "f037519fb0b84e529070cec8678c6be0b058ed429d2a746d0c3e2cec4be01501" + }, + { + "path": "wordpress/wp-includes/blocks/accordion-panel/style.min.css", + "offset": 18957292, + "size": 111, + "sha256": "4f932c154716a0972ac0d90e720c028b7a6a315df8e441bf25bb959d07f0ef07" + }, + { + "path": "wordpress/wp-includes/blocks/accordion/style-rtl.css", + "offset": 18957403, + "size": 49, + "sha256": "7282db043a7881206ee3ff55e440181b39548156287f7bd17d686fce4296b92f" + }, + { + "path": "wordpress/wp-includes/blocks/accordion/style-rtl.min.css", + "offset": 18957452, + "size": 42, + "sha256": "40f0c19e711a931e789c74a99a2dba22b6f38a9aff8391501496b892f198b529" + }, + { + "path": "wordpress/wp-includes/blocks/accordion/style.css", + "offset": 18957494, + "size": 49, + "sha256": "7282db043a7881206ee3ff55e440181b39548156287f7bd17d686fce4296b92f" + }, + { + "path": "wordpress/wp-includes/blocks/accordion/style.min.css", + "offset": 18957543, + "size": 42, + "sha256": "40f0c19e711a931e789c74a99a2dba22b6f38a9aff8391501496b892f198b529" + }, + { + "path": "wordpress/wp-includes/blocks/archives/style-rtl.css", + "offset": 18957585, + "size": 105, + "sha256": "1e10558c6ca95a24bf9f3ee9c544d45a4953ddb4b121d1a10d5a863b0d3062e3" + }, + { + "path": "wordpress/wp-includes/blocks/archives/style-rtl.min.css", + "offset": 18957690, + "size": 89, + "sha256": "0ea955dcec5c9881fd17e07af6e525cf1eb1c5ec47569fa3f1b76d805e4503ba" + }, + { + "path": "wordpress/wp-includes/blocks/archives/style.css", + "offset": 18957779, + "size": 105, + "sha256": "1e10558c6ca95a24bf9f3ee9c544d45a4953ddb4b121d1a10d5a863b0d3062e3" + }, + { + "path": "wordpress/wp-includes/blocks/archives/style.min.css", + "offset": 18957884, + "size": 89, + "sha256": "0ea955dcec5c9881fd17e07af6e525cf1eb1c5ec47569fa3f1b76d805e4503ba" + }, + { + "path": "wordpress/wp-includes/blocks/audio/editor-rtl.css", + "offset": 18957973, + "size": 263, + "sha256": "2ee93145b88a5b68498907c0be27d06d267827ff3ca4f93de843d80396f5adb1" + }, + { + "path": "wordpress/wp-includes/blocks/audio/editor-rtl.min.css", + "offset": 18958236, + "size": 215, + "sha256": "538cbfca5be48be6d1b8b816cb8e2933dd6c4e71a457b7452e2f88a7c7249d69" + }, + { + "path": "wordpress/wp-includes/blocks/audio/editor.css", + "offset": 18958451, + "size": 261, + "sha256": "239e57cd387e9641be21b21e1a8272c0d5a5ce047271e2536ec79700b35d587b" + }, + { + "path": "wordpress/wp-includes/blocks/audio/editor.min.css", + "offset": 18958712, + "size": 213, + "sha256": "b985885fc95df16224a71e6e843a808c4576061d173aa9a1d2c3b60f42a0b88b" + }, + { + "path": "wordpress/wp-includes/blocks/audio/style-rtl.css", + "offset": 18958925, + "size": 1927, + "sha256": "7da2a5ab63b861de257b720f77b4609ddbacfd39a03f4fb6e6544867b1fe1f01" + }, + { + "path": "wordpress/wp-includes/blocks/audio/style-rtl.min.css", + "offset": 18960852, + "size": 156, + "sha256": "0cc2c5ae130d3214b5501fa21931378f73d42376a7afc9d6418335c2e1cca464" + }, + { + "path": "wordpress/wp-includes/blocks/audio/style.css", + "offset": 18961008, + "size": 1927, + "sha256": "7da2a5ab63b861de257b720f77b4609ddbacfd39a03f4fb6e6544867b1fe1f01" + }, + { + "path": "wordpress/wp-includes/blocks/audio/style.min.css", + "offset": 18962935, + "size": 156, + "sha256": "0cc2c5ae130d3214b5501fa21931378f73d42376a7afc9d6418335c2e1cca464" + }, + { + "path": "wordpress/wp-includes/blocks/audio/theme-rtl.css", + "offset": 18963091, + "size": 1965, + "sha256": "c8e0dfb9fc021ce32c5d3bec0e8b92958a79fc27f44952fec87d76a805f82ca1" + }, + { + "path": "wordpress/wp-includes/blocks/audio/theme-rtl.min.css", + "offset": 18965056, + "size": 176, + "sha256": "bdd319b8cf3b5c738590a593e97eae647011b9eb6e82d46ba7138dd3e1d8d218" + }, + { + "path": "wordpress/wp-includes/blocks/audio/theme.css", + "offset": 18965232, + "size": 1965, + "sha256": "c8e0dfb9fc021ce32c5d3bec0e8b92958a79fc27f44952fec87d76a805f82ca1" + }, + { + "path": "wordpress/wp-includes/blocks/audio/theme.min.css", + "offset": 18967197, + "size": 176, + "sha256": "bdd319b8cf3b5c738590a593e97eae647011b9eb6e82d46ba7138dd3e1d8d218" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/editor-rtl.css", + "offset": 18967373, + "size": 135, + "sha256": "bffa831740594f9496860af755d69ad1647e9ab37610ac122fdc608ddd8cb28e" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/editor-rtl.min.css", + "offset": 18967508, + "size": 119, + "sha256": "aa68aa13f74991eccd28d1684fc6201b26d44afb7551ecd3640a14de1596d5bb" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/editor.css", + "offset": 18967627, + "size": 135, + "sha256": "bffa831740594f9496860af755d69ad1647e9ab37610ac122fdc608ddd8cb28e" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/editor.min.css", + "offset": 18967762, + "size": 119, + "sha256": "aa68aa13f74991eccd28d1684fc6201b26d44afb7551ecd3640a14de1596d5bb" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/style-rtl.css", + "offset": 18967881, + "size": 170, + "sha256": "55788a5518dee794a80ca8f0af7ad7de0e45973312536823905b6425a1e2f64f" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/style-rtl.min.css", + "offset": 18968051, + "size": 138, + "sha256": "3b4015272fe6385c2de8decabf50fcabdbde41a7dc471827b09e01c0df954270" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/style.css", + "offset": 18968189, + "size": 170, + "sha256": "55788a5518dee794a80ca8f0af7ad7de0e45973312536823905b6425a1e2f64f" + }, + { + "path": "wordpress/wp-includes/blocks/avatar/style.min.css", + "offset": 18968359, + "size": 138, + "sha256": "3b4015272fe6385c2de8decabf50fcabdbde41a7dc471827b09e01c0df954270" + }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs/style-rtl.css", + "offset": 18968497, + "size": 453, + "sha256": "641a6aab4c35378f103949a709e62295adca302aa911f953ff84525a2f071345" + }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs/style-rtl.min.css", + "offset": 18968950, + "size": 344, + "sha256": "5b9cd66b0266d65e954ed26edea4c787b2450b7183673fd68d7fdc2d262ff4d3" + }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs/style.css", + "offset": 18969294, + "size": 453, + "sha256": "641a6aab4c35378f103949a709e62295adca302aa911f953ff84525a2f071345" + }, + { + "path": "wordpress/wp-includes/blocks/breadcrumbs/style.min.css", + "offset": 18969747, + "size": 344, + "sha256": "5b9cd66b0266d65e954ed26edea4c787b2450b7183673fd68d7fdc2d262ff4d3" + }, + { + "path": "wordpress/wp-includes/blocks/button/editor-rtl.css", + "offset": 18970091, + "size": 545, + "sha256": "8672b03764fe751de9a83f1e01ce68d6640daa7ebc373a892a4e613fec0d3c7f" + }, + { + "path": "wordpress/wp-includes/blocks/button/editor-rtl.min.css", + "offset": 18970636, + "size": 444, + "sha256": "16052e63bd543d16865efd3d68e904f943bbad650ebdb1b41ed4084fe827edef" + }, + { + "path": "wordpress/wp-includes/blocks/button/editor.css", + "offset": 18971080, + "size": 563, + "sha256": "a0dbe550b964481c5a65388989fb129bb1bb85040e842538e421eabd5981601e" + }, + { + "path": "wordpress/wp-includes/blocks/button/editor.min.css", + "offset": 18971643, + "size": 444, + "sha256": "16052e63bd543d16865efd3d68e904f943bbad650ebdb1b41ed4084fe827edef" + }, + { + "path": "wordpress/wp-includes/blocks/button/style-rtl.css", + "offset": 18972087, + "size": 2519, + "sha256": "07e5bdd24313a4f69a3621a856a1e1f220f79a766ba74e135577246cb1490db0" + }, + { + "path": "wordpress/wp-includes/blocks/button/style-rtl.min.css", + "offset": 18974606, + "size": 2242, + "sha256": "851177307d166983e920351863ef364b2a016becb0dc103dac18da1da5d31565" + }, + { + "path": "wordpress/wp-includes/blocks/button/style.css", + "offset": 18976848, + "size": 2536, + "sha256": "c7e252900f3f4fbeb86fe61a3dbc353a4ab822d8d12f778e8abbaa9e4b77f4b7" + }, + { + "path": "wordpress/wp-includes/blocks/button/style.min.css", + "offset": 18979384, + "size": 2242, + "sha256": "851177307d166983e920351863ef364b2a016becb0dc103dac18da1da5d31565" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/editor-rtl.css", + "offset": 18981626, + "size": 1311, + "sha256": "abf4e55c90c025d3d306b1f4eba19a6920702432c3d545403775797286932e72" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/editor-rtl.min.css", + "offset": 18982937, + "size": 1006, + "sha256": "9d81e12f90e13ab3b0b21e4525c2f2c826ac62b4ec38f2612309a4336ec66aaf" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/editor.css", + "offset": 18983943, + "size": 1311, + "sha256": "880e93c788674d3379e615b65b825f70efe268fff781646527ce54cbabc67d86" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/editor.min.css", + "offset": 18985254, + "size": 1006, + "sha256": "9d81e12f90e13ab3b0b21e4525c2f2c826ac62b4ec38f2612309a4336ec66aaf" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/style-rtl.css", + "offset": 18986260, + "size": 1734, + "sha256": "81d1deceb0ead1cbbf7c1205e03365fac5684d416eb73f7afa23a596ca08ea49" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/style-rtl.min.css", + "offset": 18987994, + "size": 1395, + "sha256": "4bd8c4991f6811a65be67cffb8b0eb0240bc95c19d8ac0fdd11b9c5f02ade768" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/style.css", + "offset": 18989389, + "size": 1734, + "sha256": "4125178351ab6d5d5fa8c8c2aefb8852542cf9b37684aa8c0a11d039d58ab7d2" + }, + { + "path": "wordpress/wp-includes/blocks/buttons/style.min.css", + "offset": 18991123, + "size": 1395, + "sha256": "4bd8c4991f6811a65be67cffb8b0eb0240bc95c19d8ac0fdd11b9c5f02ade768" + }, + { + "path": "wordpress/wp-includes/blocks/calendar/style-rtl.css", + "offset": 18992518, + "size": 770, + "sha256": "b6b70363351519ccf5b7a50f0f2114d23118930a1d0e7d75bc58327cb0bfa33a" + }, + { + "path": "wordpress/wp-includes/blocks/calendar/style-rtl.min.css", + "offset": 18993288, + "size": 661, + "sha256": "54bd056335a05d96008f31c456a3d97bac8d1fa66fc94045c5f965e606ce2460" + }, + { + "path": "wordpress/wp-includes/blocks/calendar/style.css", + "offset": 18993949, + "size": 770, + "sha256": "b6b70363351519ccf5b7a50f0f2114d23118930a1d0e7d75bc58327cb0bfa33a" + }, + { + "path": "wordpress/wp-includes/blocks/calendar/style.min.css", + "offset": 18994719, + "size": 661, + "sha256": "54bd056335a05d96008f31c456a3d97bac8d1fa66fc94045c5f965e606ce2460" + }, + { + "path": "wordpress/wp-includes/blocks/categories/editor-rtl.css", + "offset": 18995380, + "size": 274, + "sha256": "c9f4a832adf9acb5c9be0fc40741b76aa58bdf5080b39f97e5b356ad691d2b17" + }, + { + "path": "wordpress/wp-includes/blocks/categories/editor-rtl.min.css", + "offset": 18995654, + "size": 198, + "sha256": "1caef102647e07a852918ae297e5d13f8ffe8655d77fb719b8e7b7cbe5c745c7" + }, + { + "path": "wordpress/wp-includes/blocks/categories/editor.css", + "offset": 18995852, + "size": 272, + "sha256": "1d4c5a9aff132093ef827deec816729eab8b3236af1f0cb33a79ad3a6eb152da" + }, + { + "path": "wordpress/wp-includes/blocks/categories/editor.min.css", + "offset": 18996124, + "size": 196, + "sha256": "f7bc4be9d5e967607fccc8d7cf7156235ebb1a0ae7eb1bb71eb7ac68c9282602" + }, + { + "path": "wordpress/wp-includes/blocks/categories/style-rtl.css", + "offset": 18996320, + "size": 445, + "sha256": "832e1cf42acb2529a3ecf8c9de541e0d5f4e819c90cba74c54d99c56476d0d02" + }, + { + "path": "wordpress/wp-includes/blocks/categories/style-rtl.min.css", + "offset": 18996765, + "size": 318, + "sha256": "e2411202ed63373e74598d29441eafefdc96956814ac8bf926983a0959a05920" + }, + { + "path": "wordpress/wp-includes/blocks/categories/style.css", + "offset": 18997083, + "size": 479, + "sha256": "89c1882329fdd85ed5bc4b7d918fa71603e0c68b9790ab6904310405cb5fda34" + }, + { + "path": "wordpress/wp-includes/blocks/categories/style.min.css", + "offset": 18997562, + "size": 318, + "sha256": "e2411202ed63373e74598d29441eafefdc96956814ac8bf926983a0959a05920" + }, + { + "path": "wordpress/wp-includes/blocks/code/editor-rtl.css", + "offset": 18997880, + "size": 43, + "sha256": "34b4b651d165b34c9ea7bc80759c96c399c15ac74ec5d8eb26db96c87b411962" + }, + { + "path": "wordpress/wp-includes/blocks/code/editor-rtl.min.css", + "offset": 18997923, + "size": 36, + "sha256": "c6d993d8b4c142cd67b56e173650e6f856f8c578aa0c3e175715bc20a8b60418" + }, + { + "path": "wordpress/wp-includes/blocks/code/editor.css", + "offset": 18997959, + "size": 43, + "sha256": "34b4b651d165b34c9ea7bc80759c96c399c15ac74ec5d8eb26db96c87b411962" + }, + { + "path": "wordpress/wp-includes/blocks/code/editor.min.css", + "offset": 18998002, + "size": 36, + "sha256": "c6d993d8b4c142cd67b56e173650e6f856f8c578aa0c3e175715bc20a8b60418" + }, + { + "path": "wordpress/wp-includes/blocks/code/style-rtl.css", + "offset": 18998038, + "size": 205, + "sha256": "af6ba9b13576654b08c3c171cb348ebeba14bf383f2446265606763ca40100b5" + }, + { + "path": "wordpress/wp-includes/blocks/code/style-rtl.min.css", + "offset": 18998243, + "size": 170, + "sha256": "48b8ce46aee1f7c02cf9e8382207efc1bd425a5f39ff0624d8cb7cac54152bc9" + }, + { + "path": "wordpress/wp-includes/blocks/code/style.css", + "offset": 18998413, + "size": 251, + "sha256": "956a519062fac48120fbbc2aa64481f28ea9129d32fc094761ebcf182cf20a10" + }, + { + "path": "wordpress/wp-includes/blocks/code/style.min.css", + "offset": 18998664, + "size": 170, + "sha256": "48b8ce46aee1f7c02cf9e8382207efc1bd425a5f39ff0624d8cb7cac54152bc9" + }, + { + "path": "wordpress/wp-includes/blocks/code/theme-rtl.css", + "offset": 18998834, + "size": 139, + "sha256": "c6682a14799198e03f6c05948baaa85902846a76ec9098f8589db9c73439155e" + }, + { + "path": "wordpress/wp-includes/blocks/code/theme-rtl.min.css", + "offset": 18998973, + "size": 116, + "sha256": "d4b2806986884389f20aa1fb54ab75d306051c9d0ad4b15ff4ab080972062765" + }, + { + "path": "wordpress/wp-includes/blocks/code/theme.css", + "offset": 18999089, + "size": 139, + "sha256": "c6682a14799198e03f6c05948baaa85902846a76ec9098f8589db9c73439155e" + }, + { + "path": "wordpress/wp-includes/blocks/code/theme.min.css", + "offset": 18999228, + "size": 116, + "sha256": "d4b2806986884389f20aa1fb54ab75d306051c9d0ad4b15ff4ab080972062765" + }, + { + "path": "wordpress/wp-includes/blocks/columns/editor-rtl.css", + "offset": 18999344, + "size": 167, + "sha256": "4281b8071004314eb512e1296b204642f11dafb1c439ae854f56d45e6cc89c74" + }, + { + "path": "wordpress/wp-includes/blocks/columns/editor-rtl.min.css", + "offset": 18999511, + "size": 139, + "sha256": "d234973f2af012144ddd1374a534e71d8789c130b6c4db26ed24b4fdfe0697b3" + }, + { + "path": "wordpress/wp-includes/blocks/columns/editor.css", + "offset": 18999650, + "size": 167, + "sha256": "90270c2ef30887b6c17d3d227b82b78904f2da51ad95fd40159ef43fc8f0c199" + }, + { + "path": "wordpress/wp-includes/blocks/columns/editor.min.css", + "offset": 18999817, + "size": 139, + "sha256": "d234973f2af012144ddd1374a534e71d8789c130b6c4db26ed24b4fdfe0697b3" + }, + { + "path": "wordpress/wp-includes/blocks/columns/style-rtl.css", + "offset": 18999956, + "size": 3649, + "sha256": "a8aa7eeeea6aa93b4e93dc27dbd834692851fa5487d6e48d60ca567bf316f4e4" + }, + { + "path": "wordpress/wp-includes/blocks/columns/style-rtl.min.css", + "offset": 19003605, + "size": 1584, + "sha256": "a86fcda3eac552f9b75e4fdf66424076b51dc4822ae9a7c124cbb515f7c30d96" + }, + { + "path": "wordpress/wp-includes/blocks/columns/style.css", + "offset": 19005189, + "size": 3649, + "sha256": "a8aa7eeeea6aa93b4e93dc27dbd834692851fa5487d6e48d60ca567bf316f4e4" + }, + { + "path": "wordpress/wp-includes/blocks/columns/style.min.css", + "offset": 19008838, + "size": 1584, + "sha256": "a86fcda3eac552f9b75e4fdf66424076b51dc4822ae9a7c124cbb515f7c30d96" + }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name/style-rtl.css", + "offset": 19010422, + "size": 59, + "sha256": "3f21ab638df66e871cd971f3c79519bb44a106d14d12d74cbb7a9bb3099e0aeb" + }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name/style-rtl.min.css", + "offset": 19010481, + "size": 52, + "sha256": "b2e8ec3d074effca499a39b187003a0efa23c8d4f624791594ff6e7114febc98" + }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name/style.css", + "offset": 19010533, + "size": 59, + "sha256": "3f21ab638df66e871cd971f3c79519bb44a106d14d12d74cbb7a9bb3099e0aeb" + }, + { + "path": "wordpress/wp-includes/blocks/comment-author-name/style.min.css", + "offset": 19010592, + "size": 52, + "sha256": "b2e8ec3d074effca499a39b187003a0efa23c8d4f624791594ff6e7114febc98" + }, + { + "path": "wordpress/wp-includes/blocks/comment-content/style-rtl.css", + "offset": 19010644, + "size": 149, + "sha256": "59c3178a1a5263525975714af4886e8318d955bf4612a611ea75a35d11dde473" + }, + { + "path": "wordpress/wp-includes/blocks/comment-content/style-rtl.min.css", + "offset": 19010793, + "size": 124, + "sha256": "c655d691efff7740069f2c6d31c5b48f432cc92217b9e0018f46ae16b4596a6b" + }, + { + "path": "wordpress/wp-includes/blocks/comment-content/style.css", + "offset": 19010917, + "size": 149, + "sha256": "59c3178a1a5263525975714af4886e8318d955bf4612a611ea75a35d11dde473" + }, + { + "path": "wordpress/wp-includes/blocks/comment-content/style.min.css", + "offset": 19011066, + "size": 124, + "sha256": "c655d691efff7740069f2c6d31c5b48f432cc92217b9e0018f46ae16b4596a6b" + }, + { + "path": "wordpress/wp-includes/blocks/comment-date/style-rtl.css", + "offset": 19011190, + "size": 52, + "sha256": "bb31f3c69fa418fe892f0039d90fae36e6046d0346cca3908ac0c13ae6882b2d" + }, + { + "path": "wordpress/wp-includes/blocks/comment-date/style-rtl.min.css", + "offset": 19011242, + "size": 45, + "sha256": "2324457499a12755a7413350e8b87373b9f12a33dd5c8ac612d13f560d9b8dfe" + }, + { + "path": "wordpress/wp-includes/blocks/comment-date/style.css", + "offset": 19011287, + "size": 52, + "sha256": "bb31f3c69fa418fe892f0039d90fae36e6046d0346cca3908ac0c13ae6882b2d" + }, + { + "path": "wordpress/wp-includes/blocks/comment-date/style.min.css", + "offset": 19011339, + "size": 45, + "sha256": "2324457499a12755a7413350e8b87373b9f12a33dd5c8ac612d13f560d9b8dfe" + }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link/style-rtl.css", + "offset": 19011384, + "size": 57, + "sha256": "fa5b8b36e43feb48da7e58ed1d9ccb447bc6d0934c5f95ec6e6b90b4e61cbe98" + }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link/style-rtl.min.css", + "offset": 19011441, + "size": 50, + "sha256": "e41b4d737ec37677937903e9047d4adcac0bde3908369f51ad2414dbba906fa3" + }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link/style.css", + "offset": 19011491, + "size": 57, + "sha256": "fa5b8b36e43feb48da7e58ed1d9ccb447bc6d0934c5f95ec6e6b90b4e61cbe98" + }, + { + "path": "wordpress/wp-includes/blocks/comment-edit-link/style.min.css", + "offset": 19011548, + "size": 50, + "sha256": "e41b4d737ec37677937903e9047d4adcac0bde3908369f51ad2414dbba906fa3" + }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link/style-rtl.css", + "offset": 19011598, + "size": 58, + "sha256": "a4b0205f164113f04bcbbd283f738b2f3a7613dd8543b6fed9c2c51b359ae4b4" + }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link/style-rtl.min.css", + "offset": 19011656, + "size": 51, + "sha256": "df5545d0b2d61333a2a3fc8c546092320d817d75762e758d8f85782b24272949" + }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link/style.css", + "offset": 19011707, + "size": 58, + "sha256": "a4b0205f164113f04bcbbd283f738b2f3a7613dd8543b6fed9c2c51b359ae4b4" + }, + { + "path": "wordpress/wp-includes/blocks/comment-reply-link/style.min.css", + "offset": 19011765, + "size": 51, + "sha256": "df5545d0b2d61333a2a3fc8c546092320d817d75762e758d8f85782b24272949" + }, + { + "path": "wordpress/wp-includes/blocks/comment-template/style-rtl.css", + "offset": 19011816, + "size": 516, + "sha256": "370cca44ed8b8dcbf88cd41c7852cad868223e895d2bd412a962dbc96df762ae" + }, + { + "path": "wordpress/wp-includes/blocks/comment-template/style-rtl.min.css", + "offset": 19012332, + "size": 433, + "sha256": "9bff8f9155297e0c4ad4733d0b126b0bbf035a48b7023e7d5f323a5e53dd768d" + }, + { + "path": "wordpress/wp-includes/blocks/comment-template/style.css", + "offset": 19012765, + "size": 515, + "sha256": "94087a95e0e1d116a9927da11fef35c99fa0a07292071d8c35a73db8228e7377" + }, + { + "path": "wordpress/wp-includes/blocks/comment-template/style.min.css", + "offset": 19013280, + "size": 432, + "sha256": "b984e8d29d91f445f104545fbc69e3a16a7c5730582c0d898140f5558bf8781e" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-numbers/editor-rtl.css", + "offset": 19013712, + "size": 235, + "sha256": "ab3e4c40b7ea2aad26f0402876622e151241f88f5bafd2c9bb78674315cccdc7" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-numbers/editor-rtl.min.css", + "offset": 19013947, + "size": 212, + "sha256": "ed869b3a186fc914b0154b297f42bee71ea1ca090aef06c9989713f9d4d6e5f6" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-numbers/editor.css", + "offset": 19014159, + "size": 253, + "sha256": "3e2bcf76386a6d95bd7a40800ecf079f678f2957d03aed03a8893a820fc706b0" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination-numbers/editor.min.css", + "offset": 19014412, + "size": 213, + "sha256": "203b9f9cf3d3c73aee8dab335fff52f23be669397a097fafe6ca3daacbf4e3f6" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/editor-rtl.css", + "offset": 19014625, + "size": 413, + "sha256": "b64926040249845f6fa0058313a2f626a463b3c34dd002d7f0705162cfc955e4" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/editor-rtl.min.css", + "offset": 19015038, + "size": 378, + "sha256": "5a1bd458cdff1e8badf095262c5b0d2ed1330b4d7fc4437c72c41832a85f36e4" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/editor.css", + "offset": 19015416, + "size": 413, + "sha256": "b64926040249845f6fa0058313a2f626a463b3c34dd002d7f0705162cfc955e4" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/editor.min.css", + "offset": 19015829, + "size": 378, + "sha256": "5a1bd458cdff1e8badf095262c5b0d2ed1330b4d7fc4437c72c41832a85f36e4" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/style-rtl.css", + "offset": 19016207, + "size": 805, + "sha256": "263c517cafc37911f8c46d549875db96e777f7355fcbd163d7bae628966fe66d" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/style-rtl.min.css", + "offset": 19017012, + "size": 740, + "sha256": "a65667e2b164bf124e74981eacb1bcda18f9ce7200d01e0805499239c7ccd83f" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/style.css", + "offset": 19017752, + "size": 841, + "sha256": "99240f8ea0cdf3ab264dbf0c23970a40cb1b4c96ad46ec3febe15a2e0b9e39a8" + }, + { + "path": "wordpress/wp-includes/blocks/comments-pagination/style.min.css", + "offset": 19018593, + "size": 738, + "sha256": "4f7b0e412d37040de997fff280435436600131585db5231841d66ef4c56f83f7" + }, + { + "path": "wordpress/wp-includes/blocks/comments-title/editor-rtl.css", + "offset": 19019331, + "size": 63, + "sha256": "d530c131f5d444320379b9f46c386c8c77dabe9cea985099a7026dc235efedd3" + }, + { + "path": "wordpress/wp-includes/blocks/comments-title/editor-rtl.min.css", + "offset": 19019394, + "size": 56, + "sha256": "f89e6620299843dabe1c8a4b47c01aee439e03b0c7f4c5a5204c99b90770e4b5" + }, + { + "path": "wordpress/wp-includes/blocks/comments-title/editor.css", + "offset": 19019450, + "size": 63, + "sha256": "d530c131f5d444320379b9f46c386c8c77dabe9cea985099a7026dc235efedd3" + }, + { + "path": "wordpress/wp-includes/blocks/comments-title/editor.min.css", + "offset": 19019513, + "size": 56, + "sha256": "f89e6620299843dabe1c8a4b47c01aee439e03b0c7f4c5a5204c99b90770e4b5" + }, + { + "path": "wordpress/wp-includes/blocks/comments/editor-rtl.css", + "offset": 19019569, + "size": 5065, + "sha256": "aba450d113ae9319d1203dac1f678ccd92210c88e2bf71ac10950536de7bb9e8" + }, + { + "path": "wordpress/wp-includes/blocks/comments/editor-rtl.min.css", + "offset": 19024634, + "size": 4460, + "sha256": "eda911c8c9703820a47a9fcc19a1ff82bbc71008df5be9a9260c2cefdf59a4aa" + }, + { + "path": "wordpress/wp-includes/blocks/comments/editor.css", + "offset": 19029094, + "size": 5063, + "sha256": "56597358536cc2dc0d504394cab0c4c560c26d7cb428b0c264c45e3f67a7685a" + }, + { + "path": "wordpress/wp-includes/blocks/comments/editor.min.css", + "offset": 19034157, + "size": 4458, + "sha256": "5eafd2c141a9d40f9e44feca7ef2861befa1332be01abeaae9008c48d3738f71" + }, + { + "path": "wordpress/wp-includes/blocks/comments/style-rtl.css", + "offset": 19038615, + "size": 2884, + "sha256": "43d936341683979f84c091cff05ba0554e886e0618465ba717980c5705b42525" + }, + { + "path": "wordpress/wp-includes/blocks/comments/style-rtl.min.css", + "offset": 19041499, + "size": 2369, + "sha256": "72b4aa452eed908d0c42249ea52dcda2800aac8eecd3488a311b017b68c4e992" + }, + { + "path": "wordpress/wp-includes/blocks/comments/style.css", + "offset": 19043868, + "size": 2882, + "sha256": "1dae5a6ac721ee55340aa269746185ca245984395026ec47c5956ef428474656" + }, + { + "path": "wordpress/wp-includes/blocks/comments/style.min.css", + "offset": 19046750, + "size": 2367, + "sha256": "aeeb7827bb05ec150dc08b2350914dc1428805a70f9e1d46fafb5115bccfe235" + }, + { + "path": "wordpress/wp-includes/blocks/cover/editor-rtl.css", + "offset": 19049117, + "size": 2773, + "sha256": "c76e53a3631e45b02f54b238c883a99e84ea2ea021fae1e57bbfaab641301047" + }, + { + "path": "wordpress/wp-includes/blocks/cover/editor-rtl.min.css", + "offset": 19051890, + "size": 1728, + "sha256": "212b5744001035d1b9eb3ea70cf26b94711c5289a36bcbbbc13eae1fba44ab54" + }, + { + "path": "wordpress/wp-includes/blocks/cover/editor.css", + "offset": 19053618, + "size": 2771, + "sha256": "1014d1448237ee08a4798028323cb8bfbd5e93ad4f707b27aa4a512d5e0a8d93" + }, + { + "path": "wordpress/wp-includes/blocks/cover/editor.min.css", + "offset": 19056389, + "size": 1726, + "sha256": "6a6088f3608085a59599b9adc173bf1b7f20ccb72eba1e3337cac7a4b8ecae35" + }, + { + "path": "wordpress/wp-includes/blocks/cover/style-rtl.css", + "offset": 19058115, + "size": 22759, + "sha256": "243d9a5a5dea3994a0212d5ef3939531f4bcf87d455d4c30b2c0512e03894060" + }, + { + "path": "wordpress/wp-includes/blocks/cover/style-rtl.min.css", + "offset": 19080874, + "size": 20290, + "sha256": "f4b3506d47167e8b6adb9166df612ee070218be75f81ca983695c1c821ae9005" + }, + { + "path": "wordpress/wp-includes/blocks/cover/style.css", + "offset": 19101164, + "size": 22791, + "sha256": "83076b1c83d5a153b7e78f0b09de8a0814870c0422fee85ee50b118637bde467" + }, + { + "path": "wordpress/wp-includes/blocks/cover/style.min.css", + "offset": 19123955, + "size": 20262, + "sha256": "bd56a479237f7212eae3a46a3f0e9aeba7dff44dab300249a18fa24587fb63d0" + }, + { + "path": "wordpress/wp-includes/blocks/details/editor-rtl.css", + "offset": 19144217, + "size": 52, + "sha256": "b9eeaec69799fbaca038f353874a023ac8742c03684d6752ea06b4929493b5bd" + }, + { + "path": "wordpress/wp-includes/blocks/details/editor-rtl.min.css", + "offset": 19144269, + "size": 45, + "sha256": "115c18071c4c04d3b8189118324df7f23fe7ca72411706aa39a3943495348f34" + }, + { + "path": "wordpress/wp-includes/blocks/details/editor.css", + "offset": 19144314, + "size": 52, + "sha256": "b9eeaec69799fbaca038f353874a023ac8742c03684d6752ea06b4929493b5bd" + }, + { + "path": "wordpress/wp-includes/blocks/details/editor.min.css", + "offset": 19144366, + "size": 45, + "sha256": "115c18071c4c04d3b8189118324df7f23fe7ca72411706aa39a3943495348f34" + }, + { + "path": "wordpress/wp-includes/blocks/details/style-rtl.css", + "offset": 19144411, + "size": 97, + "sha256": "b6de946e5946f6c0b65a3fd2fbf0253cd8b58b713cbf3000cd630b48c39e9ff1" + }, + { + "path": "wordpress/wp-includes/blocks/details/style-rtl.min.css", + "offset": 19144508, + "size": 81, + "sha256": "b725e3774697bba8856599dd041eac8332c1caf46b5496cb403bd99acc875685" + }, + { + "path": "wordpress/wp-includes/blocks/details/style.css", + "offset": 19144589, + "size": 97, + "sha256": "b6de946e5946f6c0b65a3fd2fbf0253cd8b58b713cbf3000cd630b48c39e9ff1" + }, + { + "path": "wordpress/wp-includes/blocks/details/style.min.css", + "offset": 19144686, + "size": 81, + "sha256": "b725e3774697bba8856599dd041eac8332c1caf46b5496cb403bd99acc875685" + }, + { + "path": "wordpress/wp-includes/blocks/embed/editor-rtl.css", + "offset": 19144767, + "size": 858, + "sha256": "856686b725c6498728e5e33b8d26dbb58f884aac47437878f5db09b40129ab64" + }, + { + "path": "wordpress/wp-includes/blocks/embed/editor-rtl.min.css", + "offset": 19145625, + "size": 707, + "sha256": "62ed1030f361f99cbe7a54551439e0b809519f52af4839cd415b18727d5335fa" + }, + { + "path": "wordpress/wp-includes/blocks/embed/editor.css", + "offset": 19146332, + "size": 858, + "sha256": "93d7565906d02cd9dae364ccf006f986d49fb49b5c9de15c62e9a1f3ffd999af" + }, + { + "path": "wordpress/wp-includes/blocks/embed/editor.min.css", + "offset": 19147190, + "size": 707, + "sha256": "62ed1030f361f99cbe7a54551439e0b809519f52af4839cd415b18727d5335fa" + }, + { + "path": "wordpress/wp-includes/blocks/embed/style-rtl.css", + "offset": 19147897, + "size": 3618, + "sha256": "1f326d6868ef2f99a223385fabbfd97dc0ad087e9e3375acde17e54ac09d3f90" + }, + { + "path": "wordpress/wp-includes/blocks/embed/style-rtl.min.css", + "offset": 19151515, + "size": 1667, + "sha256": "2329e4c9102a51ac33e48ee6ce4087c4f8367de2d3f8400d9825d5296b6aaa8a" + }, + { + "path": "wordpress/wp-includes/blocks/embed/style.css", + "offset": 19153182, + "size": 3618, + "sha256": "10aac666cfe9be0692e7cb14e1178b86902d93350c66dce6092eb24755a6c8f6" + }, + { + "path": "wordpress/wp-includes/blocks/embed/style.min.css", + "offset": 19156800, + "size": 1667, + "sha256": "2329e4c9102a51ac33e48ee6ce4087c4f8367de2d3f8400d9825d5296b6aaa8a" + }, + { + "path": "wordpress/wp-includes/blocks/embed/theme-rtl.css", + "offset": 19158467, + "size": 1965, + "sha256": "0bc7cde8baf48ca02960972ccb70fd8a3fde0e127bfc82d4cd297b4c0ae57ef5" + }, + { + "path": "wordpress/wp-includes/blocks/embed/theme-rtl.min.css", + "offset": 19160432, + "size": 176, + "sha256": "61feb06a0b8e5b72d231389dbe8f99e05f19998d85541ff126e71ae0c0b9ef14" + }, + { + "path": "wordpress/wp-includes/blocks/embed/theme.css", + "offset": 19160608, + "size": 1965, + "sha256": "0bc7cde8baf48ca02960972ccb70fd8a3fde0e127bfc82d4cd297b4c0ae57ef5" + }, + { + "path": "wordpress/wp-includes/blocks/embed/theme.min.css", + "offset": 19162573, + "size": 176, + "sha256": "61feb06a0b8e5b72d231389dbe8f99e05f19998d85541ff126e71ae0c0b9ef14" + }, + { + "path": "wordpress/wp-includes/blocks/file/editor-rtl.css", + "offset": 19162749, + "size": 894, + "sha256": "37a6cb8f873dc3b323664b0a373100481ceac6cd7b507be482ef5f009a28465b" + }, + { + "path": "wordpress/wp-includes/blocks/file/editor-rtl.min.css", + "offset": 19163643, + "size": 763, + "sha256": "01cbb9b3830822a2828c733667a04644dc64b15fa1b5cac319ed143b7d51b60b" + }, + { + "path": "wordpress/wp-includes/blocks/file/editor.css", + "offset": 19164406, + "size": 893, + "sha256": "18c2f1481074780bcd602a8b784cd598b32b1423d050cf775a0f4d11ad9f2807" + }, + { + "path": "wordpress/wp-includes/blocks/file/editor.min.css", + "offset": 19165299, + "size": 762, + "sha256": "652620fa19f7942df4d74bca87d51e6fd7fc75f124d434c4d19946dfdedb728d" + }, + { + "path": "wordpress/wp-includes/blocks/file/style-rtl.css", + "offset": 19166061, + "size": 776, + "sha256": "ca9453da2ed917895a6f6627ddb3c3952dc219095a68a2ff06343f7699b1d869" + }, + { + "path": "wordpress/wp-includes/blocks/file/style-rtl.min.css", + "offset": 19166837, + "size": 655, + "sha256": "2e4bb54290332bcd9af8aa33c00a0b3b8994ecdebcc6aa00bc09bfdc3270e0a1" + }, + { + "path": "wordpress/wp-includes/blocks/file/style.css", + "offset": 19167492, + "size": 792, + "sha256": "c73d18ee55e8bcbb3984fe4df039f2bf1dd4fdadaaa4bab36bb71cc19506648b" + }, + { + "path": "wordpress/wp-includes/blocks/file/style.min.css", + "offset": 19168284, + "size": 654, + "sha256": "06138a428bab20e366c5257157fde8555463a00744a451c67650efc663fb51dd" + }, + { + "path": "wordpress/wp-includes/blocks/footnotes/style-rtl.css", + "offset": 19168938, + "size": 343, + "sha256": "90dccbcdbd184763ec97363d02ff43f1b29a50eed2423e94a1a89d8d8291df20" + }, + { + "path": "wordpress/wp-includes/blocks/footnotes/style-rtl.min.css", + "offset": 19169281, + "size": 288, + "sha256": "d0d52c680f80debec7cf118999e1d57f3a0cccae97828b17cf887ff0d35320a1" + }, + { + "path": "wordpress/wp-includes/blocks/footnotes/style.css", + "offset": 19169569, + "size": 342, + "sha256": "352c94210682bf52d3630c4165fec4c589e9a5425374139eb7a8a6519e327f5f" + }, + { + "path": "wordpress/wp-includes/blocks/footnotes/style.min.css", + "offset": 19169911, + "size": 287, + "sha256": "543eade00916013b2bbc1728e8a3899a4e50d5265f283a571475e2b8c6552065" + }, + { + "path": "wordpress/wp-includes/blocks/freeform/editor-rtl.css", + "offset": 19170198, + "size": 2798, + "sha256": "bce07c9079b8af7f244a6e07250c6a02ee995e97ef4d826b8e16ae80803d2469" + }, + { + "path": "wordpress/wp-includes/blocks/freeform/editor-rtl.min.css", + "offset": 19172996, + "size": 909, + "sha256": "2fefe051bc4f775b64cfe82b7f3a1dd3cf76b10d56839478430a30a678bca6a7" + }, + { + "path": "wordpress/wp-includes/blocks/freeform/editor.css", + "offset": 19173905, + "size": 2798, + "sha256": "bce07c9079b8af7f244a6e07250c6a02ee995e97ef4d826b8e16ae80803d2469" + }, + { + "path": "wordpress/wp-includes/blocks/freeform/editor.min.css", + "offset": 19176703, + "size": 909, + "sha256": "2fefe051bc4f775b64cfe82b7f3a1dd3cf76b10d56839478430a30a678bca6a7" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/editor-rtl.css", + "offset": 19177612, + "size": 4221, + "sha256": "81f1a97f6007762351b73399f6d5362c62cb1c2cc283e91d9034730c557b3a32" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/editor-rtl.min.css", + "offset": 19181833, + "size": 2057, + "sha256": "09954d1796bd0fbe0e9dce3ad7b3da8fb14e42c20d48d2e70182e0dd662623b5" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/editor.css", + "offset": 19183890, + "size": 4221, + "sha256": "34a5afa6c11550ae64436599378a611cf1ec2070cb994e4119771671ecb51642" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/editor.min.css", + "offset": 19188111, + "size": 2057, + "sha256": "add74c8cd51fb141e9ddf0707160d8b4ae5636ea302354edbfc8fce8ee9823c8" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/style-rtl.css", + "offset": 19190168, + "size": 19490, + "sha256": "9c3c5f8e5e67554c65277bbdb20aa8a11ecf9be8ce5c7993c8e6228284a315d7" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/style-rtl.min.css", + "offset": 19209658, + "size": 16110, + "sha256": "2db7d891a18aef42f5678ef80ad161f358231f8b0e590afae3cd2b6bd3b88bae" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/style.css", + "offset": 19225768, + "size": 19507, + "sha256": "41b3026ece5754f41a87ce6e70f5548cf818453ac9159ab81c28a2eb05ba6b51" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/style.min.css", + "offset": 19245275, + "size": 16120, + "sha256": "58f503a45492e0720e4e4ca1f579603e29181989da3f72e256017f93a1217eb6" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/theme-rtl.css", + "offset": 19261395, + "size": 1901, + "sha256": "9a64faf0885f9063a07078caedc4952a60951fa7676cf0c5e26e8288b7e57285" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/theme-rtl.min.css", + "offset": 19263296, + "size": 123, + "sha256": "0d92ddb05513c897042d202033d79d33463ed3201a4f15a494979bd8517a3ec9" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/theme.css", + "offset": 19263419, + "size": 1901, + "sha256": "9a64faf0885f9063a07078caedc4952a60951fa7676cf0c5e26e8288b7e57285" + }, + { + "path": "wordpress/wp-includes/blocks/gallery/theme.min.css", + "offset": 19265320, + "size": 123, + "sha256": "0d92ddb05513c897042d202033d79d33463ed3201a4f15a494979bd8517a3ec9" + }, + { + "path": "wordpress/wp-includes/blocks/group/editor-rtl.css", + "offset": 19265443, + "size": 2318, + "sha256": "94b38d8c08b99e4e99a19123e34edf683e79966744e9f33ebc271d1ca94137d7" + }, + { + "path": "wordpress/wp-includes/blocks/group/editor-rtl.min.css", + "offset": 19267761, + "size": 1355, + "sha256": "c1602b8a3954159609bbbb5a3a0a6a590085d5ba74b6e6fe0f5a6463d3ac671a" + }, + { + "path": "wordpress/wp-includes/blocks/group/editor.css", + "offset": 19269116, + "size": 2318, + "sha256": "39884cad5fab22026660062288f95e922351feda4224864b2d2449157247c60a" + }, + { + "path": "wordpress/wp-includes/blocks/group/editor.min.css", + "offset": 19271434, + "size": 1355, + "sha256": "c1602b8a3954159609bbbb5a3a0a6a590085d5ba74b6e6fe0f5a6463d3ac671a" + }, + { + "path": "wordpress/wp-includes/blocks/group/style-rtl.css", + "offset": 19272789, + "size": 133, + "sha256": "16464a7cf2a3e7dbc446e5b48f4a555c66f3b27022fcaf6f5f147efe383236d0" + }, + { + "path": "wordpress/wp-includes/blocks/group/style-rtl.min.css", + "offset": 19272922, + "size": 117, + "sha256": "628253f7b803c3e507e1189320a8c925ac718d5237c1f2c346a183ae02bbd6bb" + }, + { + "path": "wordpress/wp-includes/blocks/group/style.css", + "offset": 19273039, + "size": 133, + "sha256": "16464a7cf2a3e7dbc446e5b48f4a555c66f3b27022fcaf6f5f147efe383236d0" + }, + { + "path": "wordpress/wp-includes/blocks/group/style.min.css", + "offset": 19273172, + "size": 117, + "sha256": "628253f7b803c3e507e1189320a8c925ac718d5237c1f2c346a183ae02bbd6bb" + }, + { + "path": "wordpress/wp-includes/blocks/group/theme-rtl.css", + "offset": 19273289, + "size": 849, + "sha256": "889e05db36342aa896cd4510e2d7dc1ccb4b4970de2647f5d17e7d481557e45b" + }, + { + "path": "wordpress/wp-includes/blocks/group/theme-rtl.min.css", + "offset": 19274138, + "size": 62, + "sha256": "7cdc95241dae1a12350e177473d07acbcce3f01149572f4698cf2e84ac80da1b" + }, + { + "path": "wordpress/wp-includes/blocks/group/theme.css", + "offset": 19274200, + "size": 849, + "sha256": "889e05db36342aa896cd4510e2d7dc1ccb4b4970de2647f5d17e7d481557e45b" + }, + { + "path": "wordpress/wp-includes/blocks/group/theme.min.css", + "offset": 19275049, + "size": 62, + "sha256": "7cdc95241dae1a12350e177473d07acbcce3f01149572f4698cf2e84ac80da1b" + }, + { + "path": "wordpress/wp-includes/blocks/heading/style-rtl.css", + "offset": 19275111, + "size": 1976, + "sha256": "2a1ad02383103c53d74e2cadd9d1f273b87eef0424ae9b8398481254a6400501" + }, + { + "path": "wordpress/wp-includes/blocks/heading/style-rtl.min.css", + "offset": 19277087, + "size": 1165, + "sha256": "187c2a0a016f532b73a680d2ec1095537ee7c6a545833c6c244571fdf3c9f7c9" + }, + { + "path": "wordpress/wp-includes/blocks/heading/style.css", + "offset": 19278252, + "size": 1976, + "sha256": "2a1ad02383103c53d74e2cadd9d1f273b87eef0424ae9b8398481254a6400501" + }, + { + "path": "wordpress/wp-includes/blocks/heading/style.min.css", + "offset": 19280228, + "size": 1165, + "sha256": "187c2a0a016f532b73a680d2ec1095537ee7c6a545833c6c244571fdf3c9f7c9" + }, + { + "path": "wordpress/wp-includes/blocks/html/editor-rtl.css", + "offset": 19281393, + "size": 3017, + "sha256": "16d668de17ec42af428586ba981a938e9692a8c1b6e56bc990ff49c78ed35d3e" + }, + { + "path": "wordpress/wp-includes/blocks/html/editor-rtl.min.css", + "offset": 19284410, + "size": 1043, + "sha256": "2ee7c42eca6728e1c399ac476ca230e171a5526dc4c503982f3a8895b4c6df14" + }, + { + "path": "wordpress/wp-includes/blocks/html/editor.css", + "offset": 19285453, + "size": 3033, + "sha256": "444e8bff360b9a06ab2fa7912f07585a7434a4412276c85cf5492dbc8a0b055c" + }, + { + "path": "wordpress/wp-includes/blocks/html/editor.min.css", + "offset": 19288486, + "size": 1042, + "sha256": "5936f51d0cbd9eda090ee73a8b36f7ad7b3f7aaa0ebea61198200c9d6ee591d4" + }, + { + "path": "wordpress/wp-includes/blocks/icon/editor-rtl.css", + "offset": 19289528, + "size": 1774, + "sha256": "fb05aaa5c99d6842bbfb9c86348ca9ec6b10f5f4d87d3e0ca9c7df188185d2d8" + }, + { + "path": "wordpress/wp-includes/blocks/icon/editor-rtl.min.css", + "offset": 19291302, + "size": 849, + "sha256": "5556d04213fce175d4fdee24e11003ff72482db3fc8860b4f87c26bc53e28729" + }, + { + "path": "wordpress/wp-includes/blocks/icon/editor.css", + "offset": 19292151, + "size": 1774, + "sha256": "fb05aaa5c99d6842bbfb9c86348ca9ec6b10f5f4d87d3e0ca9c7df188185d2d8" + }, + { + "path": "wordpress/wp-includes/blocks/icon/editor.min.css", + "offset": 19293925, + "size": 849, + "sha256": "5556d04213fce175d4fdee24e11003ff72482db3fc8860b4f87c26bc53e28729" + }, + { + "path": "wordpress/wp-includes/blocks/icon/style-rtl.css", + "offset": 19294774, + "size": 328, + "sha256": "b49f67ec6e13ade10bb859a1a954723ce0455d58d69d4188e1bf4de982ba0999" + }, + { + "path": "wordpress/wp-includes/blocks/icon/style-rtl.min.css", + "offset": 19295102, + "size": 201, + "sha256": "1e4c6ad9e021546aa1a75c8325532d1238cc2d3183aad5da3781b57362880dcc" + }, + { + "path": "wordpress/wp-includes/blocks/icon/style.css", + "offset": 19295303, + "size": 328, + "sha256": "b49f67ec6e13ade10bb859a1a954723ce0455d58d69d4188e1bf4de982ba0999" + }, + { + "path": "wordpress/wp-includes/blocks/icon/style.min.css", + "offset": 19295631, + "size": 201, + "sha256": "1e4c6ad9e021546aa1a75c8325532d1238cc2d3183aad5da3781b57362880dcc" + }, + { + "path": "wordpress/wp-includes/blocks/image/editor-rtl.css", + "offset": 19295832, + "size": 4523, + "sha256": "1b9b53576cd083ee945cbd084ff13edc42b9c79903b5f71d8d0141fb4416167a" + }, + { + "path": "wordpress/wp-includes/blocks/image/editor-rtl.min.css", + "offset": 19300355, + "size": 2313, + "sha256": "e0a0ad2e06d48a567fd07e7951c00955de4bcc527c0bb7f71500596158af7037" + }, + { + "path": "wordpress/wp-includes/blocks/image/editor.css", + "offset": 19302668, + "size": 4523, + "sha256": "a404904d5ed73637eaf5702e173a5bb85e9d8efc6d4d172488b91f1fd87c4804" + }, + { + "path": "wordpress/wp-includes/blocks/image/editor.min.css", + "offset": 19307191, + "size": 2313, + "sha256": "65b3475ab09bddf99629b48fa085eb3d19c736a818e148bbba47a5a638b64fc4" + }, + { + "path": "wordpress/wp-includes/blocks/image/style-rtl.css", + "offset": 19309504, + "size": 12393, + "sha256": "84cb9e73fa80272797d1607cba24fc5413fc53371e82cf216085d7cad1ce1e17" + }, + { + "path": "wordpress/wp-includes/blocks/image/style-rtl.min.css", + "offset": 19321897, + "size": 8794, + "sha256": "638bde21b34c96c32feee2168ff3abc5c48fbf0adbec7ecacc39a34911b2441e" + }, + { + "path": "wordpress/wp-includes/blocks/image/style.css", + "offset": 19330691, + "size": 12485, + "sha256": "6d3a9dcc001736ed2e447e981ad97f1951e5083a5abb04e11a44f8cff847e109" + }, + { + "path": "wordpress/wp-includes/blocks/image/style.min.css", + "offset": 19343176, + "size": 8784, + "sha256": "2417f96c8d21991a91de4cffd2c10836c5ff65e2ab367c2243cd02a258cb469b" + }, + { + "path": "wordpress/wp-includes/blocks/image/theme-rtl.css", + "offset": 19351960, + "size": 1977, + "sha256": "db0e429699c038d112d308c67bc16f3f527c73e996f23525b1f4395870f6dbf1" + }, + { + "path": "wordpress/wp-includes/blocks/image/theme-rtl.min.css", + "offset": 19353937, + "size": 188, + "sha256": "1bded44fd9ce83d8626fa1e638456791df38df28767be04332a6ae82ef543e94" + }, + { + "path": "wordpress/wp-includes/blocks/image/theme.css", + "offset": 19354125, + "size": 1977, + "sha256": "db0e429699c038d112d308c67bc16f3f527c73e996f23525b1f4395870f6dbf1" + }, + { + "path": "wordpress/wp-includes/blocks/image/theme.min.css", + "offset": 19356102, + "size": 188, + "sha256": "1bded44fd9ce83d8626fa1e638456791df38df28767be04332a6ae82ef543e94" + }, + { + "path": "wordpress/wp-includes/blocks/latest-comments/style-rtl.css", + "offset": 19356290, + "size": 1457, + "sha256": "77cea3fad9135cb436779f92ee2c88acf815111cc112dd4f12fb64a1ae72856a" + }, + { + "path": "wordpress/wp-includes/blocks/latest-comments/style-rtl.min.css", + "offset": 19357747, + "size": 1305, + "sha256": "3c9c8327f73d802b78f7d8f134b0a84bd308fdfc70aab99e8a9750ecbce88276" + }, + { + "path": "wordpress/wp-includes/blocks/latest-comments/style.css", + "offset": 19359052, + "size": 1454, + "sha256": "ccacc36732e7a5b896f5e8408a857fb4171dfba86271cf3ecc8d8fe087fd4dc2" + }, + { + "path": "wordpress/wp-includes/blocks/latest-comments/style.min.css", + "offset": 19360506, + "size": 1302, + "sha256": "3a5a541b5946a8d213d206e3d7920ce6e293ebb336b8adaf563b35b82ebe234d" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/editor-rtl.css", + "offset": 19361808, + "size": 289, + "sha256": "91ecc9a12fc84c619d6596ee95afea4c6591b1b9766440b09eaabd8c45825670" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/editor-rtl.min.css", + "offset": 19362097, + "size": 250, + "sha256": "0f80eaf97b93314675765e37c69efe1c06f4e7477173aea3b49cc53d954931cb" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/editor.css", + "offset": 19362347, + "size": 287, + "sha256": "7d9c12f38dd7361b2035329009546c589f7e028a5a5c43ef998abc8c65823a1b" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/editor.min.css", + "offset": 19362634, + "size": 248, + "sha256": "e029e81cbc9f19294cd208e11f9a57785318882bb2e6598d607cfbefc434e985" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/style-rtl.css", + "offset": 19362882, + "size": 3966, + "sha256": "6a2f9198f1a072a73e253e667701b1784f57087839324f5eb24489c57571db07" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/style-rtl.min.css", + "offset": 19366848, + "size": 1833, + "sha256": "0197009f6a71dc3e40a66309dff56de867c4f711d2b2057980bffaeaa7b418f5" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/style.css", + "offset": 19368681, + "size": 4072, + "sha256": "44b5cd8579c4c24ddbcb1a6830a9c3d4366f4099b4574235304c9bc1850dd1bc" + }, + { + "path": "wordpress/wp-includes/blocks/latest-posts/style.min.css", + "offset": 19372753, + "size": 1837, + "sha256": "6b43473e0898e1d0809ead87010e7f8b5d032f8d3257b8205c61e342b7f63fa0" + }, + { + "path": "wordpress/wp-includes/blocks/list/style-rtl.css", + "offset": 19374590, + "size": 892, + "sha256": "d989fe25818d80ba8ade0c57d1fde3598db66204f62785cbcf2ba45c0862700f" + }, + { + "path": "wordpress/wp-includes/blocks/list/style-rtl.min.css", + "offset": 19375482, + "size": 95, + "sha256": "942b2302016e4ef821e6ce7f339addb7a7b021f151c2132d1d6de54c849b3a34" + }, + { + "path": "wordpress/wp-includes/blocks/list/style.css", + "offset": 19375577, + "size": 892, + "sha256": "d989fe25818d80ba8ade0c57d1fde3598db66204f62785cbcf2ba45c0862700f" + }, + { + "path": "wordpress/wp-includes/blocks/list/style.min.css", + "offset": 19376469, + "size": 95, + "sha256": "942b2302016e4ef821e6ce7f339addb7a7b021f151c2132d1d6de54c849b3a34" + }, + { + "path": "wordpress/wp-includes/blocks/loginout/style-rtl.css", + "offset": 19376564, + "size": 48, + "sha256": "40e1b3c2a16921b461cc51fa77c58a00e824903744073ce9a1077420b6ae9633" + }, + { + "path": "wordpress/wp-includes/blocks/loginout/style-rtl.min.css", + "offset": 19376612, + "size": 41, + "sha256": "79fc5403cb35199ba3f96d205abed9049556d12321c93ada5ae6e704a6d91e1b" + }, + { + "path": "wordpress/wp-includes/blocks/loginout/style.css", + "offset": 19376653, + "size": 48, + "sha256": "40e1b3c2a16921b461cc51fa77c58a00e824903744073ce9a1077420b6ae9633" + }, + { + "path": "wordpress/wp-includes/blocks/loginout/style.min.css", + "offset": 19376701, + "size": 41, + "sha256": "79fc5403cb35199ba3f96d205abed9049556d12321c93ada5ae6e704a6d91e1b" + }, + { + "path": "wordpress/wp-includes/blocks/math/editor-rtl.css", + "offset": 19376742, + "size": 894, + "sha256": "194f02219c96cf9dded785183001fbf591c19507a0611c3f203e4aee8093809f" + }, + { + "path": "wordpress/wp-includes/blocks/math/editor-rtl.min.css", + "offset": 19377636, + "size": 100, + "sha256": "7e21acb80fafa314f4efcd6be57fc54eafd3de1fcb642c03b4035ce73fb70387" + }, + { + "path": "wordpress/wp-includes/blocks/math/editor.css", + "offset": 19377736, + "size": 911, + "sha256": "20dacfcec120a8dff6db4c8acb76b53510fa8dc4cdb4054cf413686404824eaa" + }, + { + "path": "wordpress/wp-includes/blocks/math/editor.min.css", + "offset": 19378647, + "size": 100, + "sha256": "7e21acb80fafa314f4efcd6be57fc54eafd3de1fcb642c03b4035ce73fb70387" + }, + { + "path": "wordpress/wp-includes/blocks/math/style-rtl.css", + "offset": 19378747, + "size": 60, + "sha256": "15bcbf060ae81ad9c9a9d6a0acb997fa914c515c98ad885d00101143290f664b" + }, + { + "path": "wordpress/wp-includes/blocks/math/style-rtl.min.css", + "offset": 19378807, + "size": 49, + "sha256": "5aca1354d093f7bd74a9c251e9ba34933ee5ec3ce86d9b0469a4f1cce4f11c3c" + }, + { + "path": "wordpress/wp-includes/blocks/math/style.css", + "offset": 19378856, + "size": 60, + "sha256": "15bcbf060ae81ad9c9a9d6a0acb997fa914c515c98ad885d00101143290f664b" + }, + { + "path": "wordpress/wp-includes/blocks/math/style.min.css", + "offset": 19378916, + "size": 49, + "sha256": "5aca1354d093f7bd74a9c251e9ba34933ee5ec3ce86d9b0469a4f1cce4f11c3c" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/editor-rtl.css", + "offset": 19378965, + "size": 1038, + "sha256": "3f745e0bcb4ac9eab8f75074419a1e64acc1bd80666be86776edfcb24583689e" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/editor-rtl.min.css", + "offset": 19380003, + "size": 853, + "sha256": "525ca0cdc862c1fdae74f6560b5d92ef2f31b4c15796db03697abcc262d6e12e" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/editor.css", + "offset": 19380856, + "size": 1036, + "sha256": "aa8c230d7a05c47caea63bc6c491d9cf02d139941bcc106f65f5431b84d1f8bd" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/editor.min.css", + "offset": 19381892, + "size": 851, + "sha256": "b009b5ae7ee4ece83eebe703831745f197f08f7480bb221dd441f3fc30c1d17b" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/style-rtl.css", + "offset": 19382743, + "size": 3309, + "sha256": "329427c53da5347d46322b91ba24c28135b53179f26b5e84f05dc1c1ff66e926" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/style-rtl.min.css", + "offset": 19386052, + "size": 2415, + "sha256": "f2ebde07d5038707919bdb195e8bb6b80566f8d9e35f2d2e65e1743d84f858c7" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/style.css", + "offset": 19388467, + "size": 3529, + "sha256": "41f2006567e3d1194a50da59a37fd4dabfce2052c1bd26002bf53639ebafa9e3" + }, + { + "path": "wordpress/wp-includes/blocks/media-text/style.min.css", + "offset": 19391996, + "size": 2415, + "sha256": "246a123620ac962df698ae2b266e98edd0828ac1b86c8c2d6b7d72a61794dbfd" + }, + { + "path": "wordpress/wp-includes/blocks/more/editor-rtl.css", + "offset": 19394411, + "size": 1516, + "sha256": "5615993ff70a43f9541eaf2475047a4292b769bfbf61fbd9106cb971a8709d77" + }, + { + "path": "wordpress/wp-includes/blocks/more/editor-rtl.min.css", + "offset": 19395927, + "size": 611, + "sha256": "f27312a487a236efd5a324886005d309944e70c03dcb3edfac38f2c2ab1e102c" + }, + { + "path": "wordpress/wp-includes/blocks/more/editor.css", + "offset": 19396538, + "size": 1516, + "sha256": "522d29f27248b1b39159ebe58d78da598f5ce20d0707428efcd0bd453122b0a3" + }, + { + "path": "wordpress/wp-includes/blocks/more/editor.min.css", + "offset": 19398054, + "size": 611, + "sha256": "f27312a487a236efd5a324886005d309944e70c03dcb3edfac38f2c2ab1e102c" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/editor-rtl.css", + "offset": 19398665, + "size": 3726, + "sha256": "e6bda13eed4a30d0eb6e780a276c9bd64f2ebb236f4cecb15867018f5f53e420" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/editor-rtl.min.css", + "offset": 19402391, + "size": 2314, + "sha256": "a9a2f332da0c1219fb372456b26c98da4385e73a6e8c09fd0d4a1663a0a32ead" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/editor.css", + "offset": 19404705, + "size": 3725, + "sha256": "e76a9f129142af20e19fba6360aeeb2b36b69c1fb0896096d623eef999954273" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/editor.min.css", + "offset": 19408430, + "size": 2313, + "sha256": "6515baef6839ca81c53cd48d8ce16c9ce4cc32e453d9d99583639512af031a53" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/style-rtl.css", + "offset": 19410743, + "size": 1147, + "sha256": "87f7df1de6cf27634d070a3622586bca30d11ae7a0027c9f895bce20ff7ade4c" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/style-rtl.min.css", + "offset": 19411890, + "size": 317, + "sha256": "8790836470e0b7b8f66e6f2e45ff81867b285684a63852e6b007659e1e190ee0" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/style.css", + "offset": 19412207, + "size": 1146, + "sha256": "2f6d2eb23297e0dba1dddd95c96dd64c1887747e76f2a6665a9579910a0b5431" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-link/style.min.css", + "offset": 19413353, + "size": 316, + "sha256": "7dd7835ab92b90455b2e9aabb2fb0e2b55d2713198707588016bb8cecf9d8e17" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close/style-rtl.css", + "offset": 19413669, + "size": 554, + "sha256": "7acd0294355c4196bf0d5f1634044e06400ad112fba41879768cd60d911c6a34" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close/style-rtl.min.css", + "offset": 19414223, + "size": 464, + "sha256": "7428052d984d06efb69e2c42b97fbd4616084df7fdda451d5165bc20d4104967" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close/style.css", + "offset": 19414687, + "size": 554, + "sha256": "7acd0294355c4196bf0d5f1634044e06400ad112fba41879768cd60d911c6a34" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-overlay-close/style.min.css", + "offset": 19415241, + "size": 464, + "sha256": "7428052d984d06efb69e2c42b97fbd4616084df7fdda451d5165bc20d4104967" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-submenu/editor-rtl.css", + "offset": 19415705, + "size": 2992, + "sha256": "bae6f563efee0c12513d81fd07baf0c7335a45cd8726d870e585a22da562b598" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-submenu/editor-rtl.min.css", + "offset": 19418697, + "size": 1099, + "sha256": "e366d6d35919aebbb7add82c0d0e5a64b5124a0fd513fc5096cd108c4ac14c68" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-submenu/editor.css", + "offset": 19419796, + "size": 2991, + "sha256": "bf9701ffedf2ca046e54abdd7f3302603efd95eefbe829d3873da6e51415952a" + }, + { + "path": "wordpress/wp-includes/blocks/navigation-submenu/editor.min.css", + "offset": 19422787, + "size": 1098, + "sha256": "9c8646e5f5b48b470cee0d2cfdea8cfa5120760a98d6d30049a51a5b6429aaad" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/editor-rtl.css", + "offset": 19423885, + "size": 15407, + "sha256": "9d60a0cec471d702c6348b86dea7799bb7bcd0d6274dd9fccb7d4cb3581b3e60" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/editor-rtl.min.css", + "offset": 19439292, + "size": 11673, + "sha256": "224c906ad0f8e9cee80bf61b209353f288f208e5f5c175e72307ab225719e462" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/editor.css", + "offset": 19450965, + "size": 15404, + "sha256": "d3e0d25ef0b037099616898fd360ea1eb1ad96891f1b57526d3802c555a03b0b" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/editor.min.css", + "offset": 19466369, + "size": 11670, + "sha256": "e6cab1f73a57267d692636e158d4d32c0dee092c4e4985fcffe089198785c877" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/style-rtl.css", + "offset": 19478039, + "size": 24630, + "sha256": "a197ef154586afd718f5550ff2c485e7accffa4646497713690dc1c38fcc4b36" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/style-rtl.min.css", + "offset": 19502669, + "size": 20716, + "sha256": "43a7c90c5aba15bf4a9795a6c1cc278e99f24d71535b1a145d46ee3024c3218a" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/style.css", + "offset": 19523385, + "size": 24623, + "sha256": "1fbab5e859243cd1aa473124e829d79b5bfc14e0fead7c67626db9941e94be63" + }, + { + "path": "wordpress/wp-includes/blocks/navigation/style.min.css", + "offset": 19548008, + "size": 20709, + "sha256": "8970a41c9eb07af6f38cde8079663efdec36fe89b9d71033b5e8686567107dea" + }, + { + "path": "wordpress/wp-includes/blocks/nextpage/editor-rtl.css", + "offset": 19568717, + "size": 1495, + "sha256": "5a8b7f373ac34c0cc7598c9b41144b82a3591ec21f484f6c13d4bfce306ec3b9" + }, + { + "path": "wordpress/wp-includes/blocks/nextpage/editor-rtl.min.css", + "offset": 19570212, + "size": 592, + "sha256": "8352c7781e57d92b03339a36983dcd17fec06516c3922dc620f34ba68a4d1fdf" + }, + { + "path": "wordpress/wp-includes/blocks/nextpage/editor.css", + "offset": 19570804, + "size": 1495, + "sha256": "1b76bfe511ae8998dbcd86e11fbbd6fa9f4076507d0b78543004722f39730293" + }, + { + "path": "wordpress/wp-includes/blocks/nextpage/editor.min.css", + "offset": 19572299, + "size": 592, + "sha256": "8352c7781e57d92b03339a36983dcd17fec06516c3922dc620f34ba68a4d1fdf" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/editor-rtl.css", + "offset": 19572891, + "size": 2916, + "sha256": "6681e000c7865b40c0ead2079916f018c8ffd544c02956e90461f0acb239948a" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/editor-rtl.min.css", + "offset": 19575807, + "size": 1056, + "sha256": "1784e6cde1b5750b042ef0ae39511eec4f0eae4e1e8f18163d6dccf2393da258" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/editor.css", + "offset": 19576863, + "size": 2916, + "sha256": "6681e000c7865b40c0ead2079916f018c8ffd544c02956e90461f0acb239948a" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/editor.min.css", + "offset": 19579779, + "size": 1056, + "sha256": "1784e6cde1b5750b042ef0ae39511eec4f0eae4e1e8f18163d6dccf2393da258" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/style-rtl.css", + "offset": 19580835, + "size": 452, + "sha256": "983455af96b98b594c470eb940247da2a5f6fad60d284ab994590cc762f6562f" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/style-rtl.min.css", + "offset": 19581287, + "size": 404, + "sha256": "6f1226725cfa6ddc35ef66475019751df23f156f1672e59b2a39eee4a7c300d8" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/style.css", + "offset": 19581691, + "size": 452, + "sha256": "983455af96b98b594c470eb940247da2a5f6fad60d284ab994590cc762f6562f" + }, + { + "path": "wordpress/wp-includes/blocks/page-list/style.min.css", + "offset": 19582143, + "size": 404, + "sha256": "6f1226725cfa6ddc35ef66475019751df23f156f1672e59b2a39eee4a7c300d8" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/editor-rtl.css", + "offset": 19582547, + "size": 863, + "sha256": "52e66795be2c9f0fdcb6bea34f8f8eee91617888c1f5fdcdd1cbff51a58d2890" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/editor-rtl.min.css", + "offset": 19583410, + "size": 798, + "sha256": "29500bcd5922c0718c22156c78540f680815521f3ac8c63740b656959f76de32" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/editor.css", + "offset": 19584208, + "size": 862, + "sha256": "d676225d234cacaa73cfe2844d3909cbf5022b59bacdd296a3efd02759cfdceb" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/editor.min.css", + "offset": 19585070, + "size": 797, + "sha256": "aa9c6ca6a67fdbe4f25b49e7972f76f88375c04f9532b16bdfd9631520293b6e" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/style-rtl.css", + "offset": 19585867, + "size": 1564, + "sha256": "f547515ec44a55b543fa33c17be2fcb4714c0831de31923ae02afcfdad9696cd" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/style-rtl.min.css", + "offset": 19587431, + "size": 657, + "sha256": "10a3b473645eec27e17c37ff79e41fb055cfe42008719d687af53bf67e4b7d2d" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/style.css", + "offset": 19588088, + "size": 1562, + "sha256": "f37a4910749fd26b4bdeae81449d6b5074078ac339fb18f7ced5a952d9b00cea" + }, + { + "path": "wordpress/wp-includes/blocks/paragraph/style.min.css", + "offset": 19589650, + "size": 655, + "sha256": "622f970d122913e7a6707d8be35940b9417f061eb64b21c3bf1b01b76bfccd62" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography/style-rtl.css", + "offset": 19590305, + "size": 61, + "sha256": "ab347926fa395caeaefd27a172edae49cdc647c0509fa423bc060476fd055a1f" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography/style-rtl.min.css", + "offset": 19590366, + "size": 54, + "sha256": "c3a97e8df90c0af1ad6d8678089bfb7944b3dcc977aab2d863ee160543eca527" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography/style.css", + "offset": 19590420, + "size": 61, + "sha256": "ab347926fa395caeaefd27a172edae49cdc647c0509fa423bc060476fd055a1f" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-biography/style.min.css", + "offset": 19590481, + "size": 54, + "sha256": "c3a97e8df90c0af1ad6d8678089bfb7944b3dcc977aab2d863ee160543eca527" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-name/style-rtl.css", + "offset": 19590535, + "size": 56, + "sha256": "bd38a1cb30550b25db4bc5cecdc715836bc3a80651966e3e0268e90e91d26dc6" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-name/style-rtl.min.css", + "offset": 19590591, + "size": 49, + "sha256": "99d6d57e920928f90fdafecf5a90d93c3dca24dea4d323ae12b61bd2ddb977e9" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-name/style.css", + "offset": 19590640, + "size": 56, + "sha256": "bd38a1cb30550b25db4bc5cecdc715836bc3a80651966e3e0268e90e91d26dc6" + }, + { + "path": "wordpress/wp-includes/blocks/post-author-name/style.min.css", + "offset": 19590696, + "size": 49, + "sha256": "99d6d57e920928f90fdafecf5a90d93c3dca24dea4d323ae12b61bd2ddb977e9" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/editor-rtl.css", + "offset": 19590745, + "size": 894, + "sha256": "59470df4510c5fbdd26174b94d7752f2e90d1913ee01da983306d75babce9b09" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/editor-rtl.min.css", + "offset": 19591639, + "size": 95, + "sha256": "31e8f5519b06c8e27633cae49d392be2618e9d1e8fc30c8a9901d770f42499ef" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/editor.css", + "offset": 19591734, + "size": 894, + "sha256": "59470df4510c5fbdd26174b94d7752f2e90d1913ee01da983306d75babce9b09" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/editor.min.css", + "offset": 19592628, + "size": 95, + "sha256": "31e8f5519b06c8e27633cae49d392be2618e9d1e8fc30c8a9901d770f42499ef" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/style-rtl.css", + "offset": 19592723, + "size": 435, + "sha256": "af39ca5e12d30947eab1315143142d0b6429d0d631ee7fa2f256591f164eb37b" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/style-rtl.min.css", + "offset": 19593158, + "size": 357, + "sha256": "e36ab3540cb80f33c4f50fb6dd0534541f532c2fe3f2dc83c2ac5e310c3df652" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/style.css", + "offset": 19593515, + "size": 436, + "sha256": "29c743c6d57a11ba64d6b347bd8b4d696c29f38eaa1548d5410663a99716aac9" + }, + { + "path": "wordpress/wp-includes/blocks/post-author/style.min.css", + "offset": 19593951, + "size": 358, + "sha256": "4b47d7b7f17933fcb057d0f09a00c98a836f1a7727c091d0f30d8626f58eb4ac" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count/style-rtl.css", + "offset": 19594309, + "size": 59, + "sha256": "ee2dc72a10a48bc6ccc1d7d4bb2e2d449c01cd082ffee6ab95eb9e6ef6011678" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count/style-rtl.min.css", + "offset": 19594368, + "size": 52, + "sha256": "473d5542e8302768c7affb3e4fa767fb2666a5ad64d8a2a19aceae033ea42dac" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count/style.css", + "offset": 19594420, + "size": 59, + "sha256": "ee2dc72a10a48bc6ccc1d7d4bb2e2d449c01cd082ffee6ab95eb9e6ef6011678" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-count/style.min.css", + "offset": 19594479, + "size": 52, + "sha256": "473d5542e8302768c7affb3e4fa767fb2666a5ad64d8a2a19aceae033ea42dac" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/editor-rtl.css", + "offset": 19594531, + "size": 140, + "sha256": "365defd477567c533cd62559df4b8c402639a0a0a8e2f13b8ef363536f0b97f9" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/editor-rtl.min.css", + "offset": 19594671, + "size": 124, + "sha256": "5c020575e1009b3bafe477f47c60d0bc523569ccf73a8fe114e9ef2730063859" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/editor.css", + "offset": 19594795, + "size": 140, + "sha256": "365defd477567c533cd62559df4b8c402639a0a0a8e2f13b8ef363536f0b97f9" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/editor.min.css", + "offset": 19594935, + "size": 124, + "sha256": "5c020575e1009b3bafe477f47c60d0bc523569ccf73a8fe114e9ef2730063859" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/style-rtl.css", + "offset": 19595059, + "size": 2235, + "sha256": "0147e625c82ab2bf532285c24aa5ed30856e82f9b0578bc5b4828bf69e33ef55" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/style-rtl.min.css", + "offset": 19597294, + "size": 1992, + "sha256": "d2d6339687455805e3d5b66b9f5e5fa98c0637fcc1dfc6fb650e5e099a0a9142" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/style.css", + "offset": 19599286, + "size": 2234, + "sha256": "127b185692bb444a67029469ea8d517b9b8c7f81d2d54d634a4e1788d39352ac" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-form/style.min.css", + "offset": 19601520, + "size": 1991, + "sha256": "c83105fde6fa20f285ec2acdb14bf21062e78ac6b8b520864c79ca65909ab410" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link/style-rtl.css", + "offset": 19603511, + "size": 58, + "sha256": "e74d4a8ed66f08eefa6f205ea57bd4042a148da12ad6819614d6152e73bbc1a7" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link/style-rtl.min.css", + "offset": 19603569, + "size": 51, + "sha256": "dd15d4270f4ec63259ce1ca86cfd79fd37811078e1b448e7d9d110ec92797cfc" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link/style.css", + "offset": 19603620, + "size": 58, + "sha256": "e74d4a8ed66f08eefa6f205ea57bd4042a148da12ad6819614d6152e73bbc1a7" + }, + { + "path": "wordpress/wp-includes/blocks/post-comments-link/style.min.css", + "offset": 19603678, + "size": 51, + "sha256": "dd15d4270f4ec63259ce1ca86cfd79fd37811078e1b448e7d9d110ec92797cfc" + }, + { + "path": "wordpress/wp-includes/blocks/post-content/style-rtl.css", + "offset": 19603729, + "size": 48, + "sha256": "4a95959fb196e41d1fbdcc7e21bace395eda401e928c2064e3751408fe324ba2" + }, + { + "path": "wordpress/wp-includes/blocks/post-content/style-rtl.min.css", + "offset": 19603777, + "size": 41, + "sha256": "5632fcbd1fa2fa2c20a2c160d11acdaf4d26a45c1478144671d75be2c69fe20c" + }, + { + "path": "wordpress/wp-includes/blocks/post-content/style.css", + "offset": 19603818, + "size": 48, + "sha256": "4a95959fb196e41d1fbdcc7e21bace395eda401e928c2064e3751408fe324ba2" + }, + { + "path": "wordpress/wp-includes/blocks/post-content/style.min.css", + "offset": 19603866, + "size": 41, + "sha256": "5632fcbd1fa2fa2c20a2c160d11acdaf4d26a45c1478144671d75be2c69fe20c" + }, + { + "path": "wordpress/wp-includes/blocks/post-date/style-rtl.css", + "offset": 19603907, + "size": 49, + "sha256": "b242f6f5e2d87611434f2e96edc80421d57d7288b9a591df2380349caf80560a" + }, + { + "path": "wordpress/wp-includes/blocks/post-date/style-rtl.min.css", + "offset": 19603956, + "size": 42, + "sha256": "d2fcacb015df3f08b4d661b068968206bb991fc6966f0176f1acd6d70c356e68" + }, + { + "path": "wordpress/wp-includes/blocks/post-date/style.css", + "offset": 19603998, + "size": 49, + "sha256": "b242f6f5e2d87611434f2e96edc80421d57d7288b9a591df2380349caf80560a" + }, + { + "path": "wordpress/wp-includes/blocks/post-date/style.min.css", + "offset": 19604047, + "size": 42, + "sha256": "d2fcacb015df3f08b4d661b068968206bb991fc6966f0176f1acd6d70c356e68" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/editor-rtl.css", + "offset": 19604089, + "size": 87, + "sha256": "0093dfef948b943d5bc9cd06e6bf2f2989d4d62db9418497641854bb923db5f7" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/editor-rtl.min.css", + "offset": 19604176, + "size": 80, + "sha256": "f41e820e6860ec82ade4742af5842d07a6d959e362042deac9407ab35bc025a4" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/editor.css", + "offset": 19604256, + "size": 87, + "sha256": "0093dfef948b943d5bc9cd06e6bf2f2989d4d62db9418497641854bb923db5f7" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/editor.min.css", + "offset": 19604343, + "size": 80, + "sha256": "f41e820e6860ec82ade4742af5842d07a6d959e362042deac9407ab35bc025a4" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/style-rtl.css", + "offset": 19604423, + "size": 389, + "sha256": "1abd3ef89a38a5048b2ee79ee26a17c538b8906e81efaac73a9d1c10a251fdaa" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/style-rtl.min.css", + "offset": 19604812, + "size": 339, + "sha256": "cd02424f179ba3c421371d8002dadc5290837f3f30b8b47904c48fe4c404d035" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/style.css", + "offset": 19605151, + "size": 389, + "sha256": "1abd3ef89a38a5048b2ee79ee26a17c538b8906e81efaac73a9d1c10a251fdaa" + }, + { + "path": "wordpress/wp-includes/blocks/post-excerpt/style.min.css", + "offset": 19605540, + "size": 339, + "sha256": "cd02424f179ba3c421371d8002dadc5290837f3f30b8b47904c48fe4c404d035" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/editor-rtl.css", + "offset": 19605879, + "size": 6035, + "sha256": "e950e6f5895e761d61b6532c7766ae4c4984c33eb80a006b4ade9936c6556b4b" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/editor-rtl.min.css", + "offset": 19611914, + "size": 4936, + "sha256": "9e0147b8abb48183eaa66a34326c9313c83b2c8ea8e7a8f8673ef44ce1403d96" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/editor.css", + "offset": 19616850, + "size": 6035, + "sha256": "a872fc5be754ae4f3062b5785d15d8c15d75a76dd63b1d3756a76157ebbbdd35" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/editor.min.css", + "offset": 19622885, + "size": 4936, + "sha256": "e1c37d810d0f1112b2909517f01a05ef4f48a2617ce40480d27fb64f772824eb" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/style-rtl.css", + "offset": 19627821, + "size": 2050, + "sha256": "810af75dbdb51eb7d7151fe71b154b36141430df21976cb4b41aec71f343ee2a" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/style-rtl.min.css", + "offset": 19629871, + "size": 1833, + "sha256": "24441594aa1e6b2016a479490c781edb54bd2a631d912b833837b650d2318a20" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/style.css", + "offset": 19631704, + "size": 2050, + "sha256": "f0d6ba32be7201238ebea9fb76c061886db7b78354144dd65fef2361f1f6178b" + }, + { + "path": "wordpress/wp-includes/blocks/post-featured-image/style.min.css", + "offset": 19633754, + "size": 1833, + "sha256": "24441594aa1e6b2016a479490c781edb54bd2a631d912b833837b650d2318a20" + }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link/style-rtl.css", + "offset": 19635587, + "size": 706, + "sha256": "2c004c6315856ec01d81e70d25ba240a2b3c6d1341ff3df51fe8939e91bfc2ef" + }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link/style-rtl.min.css", + "offset": 19636293, + "size": 656, + "sha256": "4deaffe864b7373606fd3267183806f764e69a69d19e3fd5a7e10392ff822ec2" + }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link/style.css", + "offset": 19636949, + "size": 742, + "sha256": "7bf8823266d0c3e5c940c8bf4f8eabdfaf54e0e42e5a2b8a8fa537441d319760" + }, + { + "path": "wordpress/wp-includes/blocks/post-navigation-link/style.min.css", + "offset": 19637691, + "size": 654, + "sha256": "82172503bc289d3b32b80066cf6980d5132fbe48f08212751871994a58244875" + }, + { + "path": "wordpress/wp-includes/blocks/post-template/style-rtl.css", + "offset": 19638345, + "size": 3572, + "sha256": "124461c6d03a892350c9b1853dca01cadb257d0ae49b9f9621899a59e2f5f434" + }, + { + "path": "wordpress/wp-includes/blocks/post-template/style-rtl.min.css", + "offset": 19641917, + "size": 1521, + "sha256": "693c5178584f5add24406f9c2d8e18d91e55a3714b2710b430c86e214446ad54" + }, + { + "path": "wordpress/wp-includes/blocks/post-template/style.css", + "offset": 19643438, + "size": 3572, + "sha256": "c5d42ac6d78909c5bb4ba4c9b49bb07e9242aee38871e16e42b0c85fc7f5a472" + }, + { + "path": "wordpress/wp-includes/blocks/post-template/style.min.css", + "offset": 19647010, + "size": 1521, + "sha256": "e3671ed8ee305a452c2929df91397956b3308f8e12ac35772a29fdbc655bd1bd" + }, + { + "path": "wordpress/wp-includes/blocks/post-terms/style-rtl.css", + "offset": 19648531, + "size": 132, + "sha256": "735f2d2e6b6c081b4fa4d01b10a119bbf5a4b828910cd287c6de9fdded8c6fae" + }, + { + "path": "wordpress/wp-includes/blocks/post-terms/style-rtl.min.css", + "offset": 19648663, + "size": 117, + "sha256": "0a63691280a665a3e6186b64fcdc7388f7650710168b7cb28428c8e51f97de30" + }, + { + "path": "wordpress/wp-includes/blocks/post-terms/style.css", + "offset": 19648780, + "size": 132, + "sha256": "735f2d2e6b6c081b4fa4d01b10a119bbf5a4b828910cd287c6de9fdded8c6fae" + }, + { + "path": "wordpress/wp-includes/blocks/post-terms/style.min.css", + "offset": 19648912, + "size": 117, + "sha256": "0a63691280a665a3e6186b64fcdc7388f7650710168b7cb28428c8e51f97de30" + }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read/style-rtl.css", + "offset": 19649029, + "size": 57, + "sha256": "3ac4f47140117207ea9324d425d4576644e7aa8a7932539e1bb2509023c256cf" + }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read/style-rtl.min.css", + "offset": 19649086, + "size": 50, + "sha256": "20151eee8731829b21007824c6078261d3914ea9fe04a2a4834a47dd9675d3c3" + }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read/style.css", + "offset": 19649136, + "size": 57, + "sha256": "3ac4f47140117207ea9324d425d4576644e7aa8a7932539e1bb2509023c256cf" + }, + { + "path": "wordpress/wp-includes/blocks/post-time-to-read/style.min.css", + "offset": 19649193, + "size": 50, + "sha256": "20151eee8731829b21007824c6078261d3914ea9fe04a2a4834a47dd9675d3c3" + }, + { + "path": "wordpress/wp-includes/blocks/post-title/style-rtl.css", + "offset": 19649243, + "size": 308, + "sha256": "1ab285b72f9f05f4bd6b0fc77ae17359df17b7f75c351d337d052853da737bab" + }, + { + "path": "wordpress/wp-includes/blocks/post-title/style-rtl.min.css", + "offset": 19649551, + "size": 261, + "sha256": "ceb4bb251d54c06a97753b6118da782a43e24dc0f28d6cb0252d42a3aaabde43" + }, + { + "path": "wordpress/wp-includes/blocks/post-title/style.css", + "offset": 19649812, + "size": 308, + "sha256": "1ab285b72f9f05f4bd6b0fc77ae17359df17b7f75c351d337d052853da737bab" + }, + { + "path": "wordpress/wp-includes/blocks/post-title/style.min.css", + "offset": 19650120, + "size": 261, + "sha256": "ceb4bb251d54c06a97753b6118da782a43e24dc0f28d6cb0252d42a3aaabde43" + }, + { + "path": "wordpress/wp-includes/blocks/preformatted/style-rtl.css", + "offset": 19650381, + "size": 935, + "sha256": "ba55d9b5b02e949b2d815b39323b8759bd3406e3ecdf6658ada0d775125edb79" + }, + { + "path": "wordpress/wp-includes/blocks/preformatted/style-rtl.min.css", + "offset": 19651316, + "size": 135, + "sha256": "ac3ebd1b8d17d5e2a4c67cfc4bc1d92857e1c556043f2d09caa295fa989d1023" + }, + { + "path": "wordpress/wp-includes/blocks/preformatted/style.css", + "offset": 19651451, + "size": 935, + "sha256": "ba55d9b5b02e949b2d815b39323b8759bd3406e3ecdf6658ada0d775125edb79" + }, + { + "path": "wordpress/wp-includes/blocks/preformatted/style.min.css", + "offset": 19652386, + "size": 135, + "sha256": "ac3ebd1b8d17d5e2a4c67cfc4bc1d92857e1c556043f2d09caa295fa989d1023" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/editor-rtl.css", + "offset": 19652521, + "size": 250, + "sha256": "33beda9630b5674a8a210ad7fb277459998e73415c04c32ec1afd5e7d6e6aa12" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/editor-rtl.min.css", + "offset": 19652771, + "size": 222, + "sha256": "8146af05ca9bc940471eeff9401c5836cab1eeb122e20ec764dd0c99c55c582c" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/editor.css", + "offset": 19652993, + "size": 250, + "sha256": "33beda9630b5674a8a210ad7fb277459998e73415c04c32ec1afd5e7d6e6aa12" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/editor.min.css", + "offset": 19653243, + "size": 222, + "sha256": "8146af05ca9bc940471eeff9401c5836cab1eeb122e20ec764dd0c99c55c582c" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/style-rtl.css", + "offset": 19653465, + "size": 2075, + "sha256": "ba92369e066da1468e49f815eacc4648e4a5bc9692b33d0bbc3099f030960d2c" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/style-rtl.min.css", + "offset": 19655540, + "size": 1118, + "sha256": "e17f9e872d9482e4a3134da00959f8995ad88e407fada6c17e8252962be9b11d" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/style.css", + "offset": 19656658, + "size": 2075, + "sha256": "2f224de46c66cf5999467afff581a1d7af111467c50120453a3dcaaccae4f9dd" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/style.min.css", + "offset": 19658733, + "size": 1118, + "sha256": "719147e897375906b9dfeee27ee1251f8640e3992666bef5cc7f2034514ba605" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/theme-rtl.css", + "offset": 19659851, + "size": 351, + "sha256": "c54d3644b46c46abff9ef5b78b3c80581b721b682d002fc3da0eec037c046ce4" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/theme-rtl.min.css", + "offset": 19660202, + "size": 283, + "sha256": "1df1aeaafec585b9386f763a6872d4b448480bd714197c278deb729df69c2b7e" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/theme.css", + "offset": 19660485, + "size": 351, + "sha256": "c54d3644b46c46abff9ef5b78b3c80581b721b682d002fc3da0eec037c046ce4" + }, + { + "path": "wordpress/wp-includes/blocks/pullquote/theme.min.css", + "offset": 19660836, + "size": 283, + "sha256": "1df1aeaafec585b9386f763a6872d4b448480bd714197c278deb729df69c2b7e" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-numbers/editor-rtl.css", + "offset": 19661119, + "size": 226, + "sha256": "26d457948416f52bfbf27462adbc3a6e1577d8d78d39f93a26867ee94bfa0bb9" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-numbers/editor-rtl.min.css", + "offset": 19661345, + "size": 203, + "sha256": "27b061f6c2dafa64f3abb05e27453ea040c23db704fdad64d59ff29681a1610c" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-numbers/editor.css", + "offset": 19661548, + "size": 244, + "sha256": "72756bfaa86db02ff9bc23583121d4420e58593beab8a437ae3d9dd31e594205" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination-numbers/editor.min.css", + "offset": 19661792, + "size": 204, + "sha256": "d3652b4f9ce9249f7c205a0180005264a8a6c35ef341e1706323af7b71fa4f12" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/editor-rtl.css", + "offset": 19661996, + "size": 277, + "sha256": "fcb14c31f3c1f12ba0b156063a8f538aeaa897942c37132dd1c2c230b00b3fbf" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/editor-rtl.min.css", + "offset": 19662273, + "size": 251, + "sha256": "b528925306c9ee82d22315d5c6f8fdb1b2bbd350dcc129c9a0f237a984587764" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/editor.css", + "offset": 19662524, + "size": 277, + "sha256": "fcb14c31f3c1f12ba0b156063a8f538aeaa897942c37132dd1c2c230b00b3fbf" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/editor.min.css", + "offset": 19662801, + "size": 251, + "sha256": "b528925306c9ee82d22315d5c6f8fdb1b2bbd350dcc129c9a0f237a984587764" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/style-rtl.css", + "offset": 19663052, + "size": 835, + "sha256": "f289786ebbccbca1b6f5d79e1ffd6f62dbbf664bb697cdba637d707eb23cddb6" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/style-rtl.min.css", + "offset": 19663887, + "size": 766, + "sha256": "962cc6d7e65fb2689088b17057674e6d819c9a5d0d658ca1de12da19974014af" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/style.css", + "offset": 19664653, + "size": 871, + "sha256": "88258068d6814f43bfe37268aebaad94ec5f113c9c093c38216e00c8546bfc11" + }, + { + "path": "wordpress/wp-includes/blocks/query-pagination/style.min.css", + "offset": 19665524, + "size": 764, + "sha256": "7278837aa0d5787fa817bcfac8e84ea0cce6a603f32025de917b57279990e966" + }, + { + "path": "wordpress/wp-includes/blocks/query-title/style-rtl.css", + "offset": 19666288, + "size": 51, + "sha256": "f0e4ae006b910c0adb01575011233dc99897f441834c01825f38fbe8d5d4b3c2" + }, + { + "path": "wordpress/wp-includes/blocks/query-title/style-rtl.min.css", + "offset": 19666339, + "size": 44, + "sha256": "64225111e4d9a55bc90152663fd0b02d5b319d93f5b1952378be722c6d640d5f" + }, + { + "path": "wordpress/wp-includes/blocks/query-title/style.css", + "offset": 19666383, + "size": 51, + "sha256": "f0e4ae006b910c0adb01575011233dc99897f441834c01825f38fbe8d5d4b3c2" + }, + { + "path": "wordpress/wp-includes/blocks/query-title/style.min.css", + "offset": 19666434, + "size": 44, + "sha256": "64225111e4d9a55bc90152663fd0b02d5b319d93f5b1952378be722c6d640d5f" + }, + { + "path": "wordpress/wp-includes/blocks/query-total/style-rtl.css", + "offset": 19666478, + "size": 51, + "sha256": "22ac232d4b704be23ca34a46b4b3f8425c9959981e5125b7d8cd7b4733b540d2" + }, + { + "path": "wordpress/wp-includes/blocks/query-total/style-rtl.min.css", + "offset": 19666529, + "size": 44, + "sha256": "673579e48dc2a0e1f25952657cbad59e1f4927856e245244207bc3a9b2230f15" + }, + { + "path": "wordpress/wp-includes/blocks/query-total/style.css", + "offset": 19666573, + "size": 51, + "sha256": "22ac232d4b704be23ca34a46b4b3f8425c9959981e5125b7d8cd7b4733b540d2" + }, + { + "path": "wordpress/wp-includes/blocks/query-total/style.min.css", + "offset": 19666624, + "size": 44, + "sha256": "673579e48dc2a0e1f25952657cbad59e1f4927856e245244207bc3a9b2230f15" + }, + { + "path": "wordpress/wp-includes/blocks/query/editor-rtl.css", + "offset": 19666668, + "size": 3422, + "sha256": "d8b9e06cf110b031651adcd2b3884ea47222eccf15b21385aa45dcae87b88928" + }, + { + "path": "wordpress/wp-includes/blocks/query/editor-rtl.min.css", + "offset": 19670090, + "size": 1499, + "sha256": "bd56c733e1f12d607b7573b605bc3562542c301ba5f96a0db1d7c772590424d5" + }, + { + "path": "wordpress/wp-includes/blocks/query/editor.css", + "offset": 19671589, + "size": 3422, + "sha256": "d8b9e06cf110b031651adcd2b3884ea47222eccf15b21385aa45dcae87b88928" + }, + { + "path": "wordpress/wp-includes/blocks/query/editor.min.css", + "offset": 19675011, + "size": 1499, + "sha256": "bd56c733e1f12d607b7573b605bc3562542c301ba5f96a0db1d7c772590424d5" + }, + { + "path": "wordpress/wp-includes/blocks/quote/style-rtl.css", + "offset": 19676510, + "size": 764, + "sha256": "45ba110a5aac0ddd870fe7b1010a15f423604c47c9fff42d67043f18118a6a7f" + }, + { + "path": "wordpress/wp-includes/blocks/quote/style-rtl.min.css", + "offset": 19677274, + "size": 698, + "sha256": "a6730c0693cef057f7f1491875c2b467b5742fa3a383cbd68e738d24410bfb57" + }, + { + "path": "wordpress/wp-includes/blocks/quote/style.css", + "offset": 19677972, + "size": 765, + "sha256": "1ef9387c601a42e7670e2d5b8f633d7fd988fa478f984fdf6dc58f3ef1924414" + }, + { + "path": "wordpress/wp-includes/blocks/quote/style.min.css", + "offset": 19678737, + "size": 699, + "sha256": "64b45ce7e07ddda5425b8e14c38b5cde9ded12764b1b13a3aa60fffdc34addbd" + }, + { + "path": "wordpress/wp-includes/blocks/quote/theme-rtl.css", + "offset": 19679436, + "size": 604, + "sha256": "4cc0283c5df4b212016bc81e71a6682506c992324d5d50da0923d4a2acfb2107" + }, + { + "path": "wordpress/wp-includes/blocks/quote/theme-rtl.min.css", + "offset": 19680040, + "size": 495, + "sha256": "31278ff95033fca46d9cfb4c660ae6bdacf5a473bfee4969e64e0163af46a354" + }, + { + "path": "wordpress/wp-includes/blocks/quote/theme.css", + "offset": 19680535, + "size": 601, + "sha256": "479ac382ef67019e08e15b950cb5704fc59785472f7daeee43eb693188935d9c" + }, + { + "path": "wordpress/wp-includes/blocks/quote/theme.min.css", + "offset": 19681136, + "size": 492, + "sha256": "a8297149e2638718ce90a6625f35608c38efba0873709f803e044c93caa68d62" + }, + { + "path": "wordpress/wp-includes/blocks/read-more/style-rtl.css", + "offset": 19681628, + "size": 309, + "sha256": "ab266f741297164cdd48c300dabb8b28ac90ad0aea107d8fd69a80941af6f0f3" + }, + { + "path": "wordpress/wp-includes/blocks/read-more/style-rtl.min.css", + "offset": 19681937, + "size": 281, + "sha256": "0c33217859ff3f32b53de5ec5e26c9ab04463c4ecbc5cf6f05334c8f8e8684d9" + }, + { + "path": "wordpress/wp-includes/blocks/read-more/style.css", + "offset": 19682218, + "size": 309, + "sha256": "ab266f741297164cdd48c300dabb8b28ac90ad0aea107d8fd69a80941af6f0f3" + }, + { + "path": "wordpress/wp-includes/blocks/read-more/style.min.css", + "offset": 19682527, + "size": 281, + "sha256": "0c33217859ff3f32b53de5ec5e26c9ab04463c4ecbc5cf6f05334c8f8e8684d9" + }, + { + "path": "wordpress/wp-includes/blocks/rss/editor-rtl.css", + "offset": 19682808, + "size": 211, + "sha256": "cc53d2db4c981e090b1d247412c4e1905c4c2d4303a762e2effbbfa6a1fbb701" + }, + { + "path": "wordpress/wp-includes/blocks/rss/editor-rtl.min.css", + "offset": 19683019, + "size": 176, + "sha256": "b73516d2d13afb3e6d1211d1ed60bdea5935440a3f8ade6084f99506bfef253b" + }, + { + "path": "wordpress/wp-includes/blocks/rss/editor.css", + "offset": 19683195, + "size": 211, + "sha256": "cc53d2db4c981e090b1d247412c4e1905c4c2d4303a762e2effbbfa6a1fbb701" + }, + { + "path": "wordpress/wp-includes/blocks/rss/editor.min.css", + "offset": 19683406, + "size": 176, + "sha256": "b73516d2d13afb3e6d1211d1ed60bdea5935440a3f8ade6084f99506bfef253b" + }, + { + "path": "wordpress/wp-includes/blocks/rss/style-rtl.css", + "offset": 19683582, + "size": 2553, + "sha256": "69c090b4df7e2e7a56e90f0769059a3357046409971e57f10852f00327065a3b" + }, + { + "path": "wordpress/wp-includes/blocks/rss/style-rtl.min.css", + "offset": 19686135, + "size": 652, + "sha256": "bab0c671cbb9cf43964d243414cd01efa78acdfd9b963d4529deb218798d266d" + }, + { + "path": "wordpress/wp-includes/blocks/rss/style.css", + "offset": 19686787, + "size": 2587, + "sha256": "f7212946b6975af4537d873e0a4254cf68d4a9724edc45c359fd3161514f6917" + }, + { + "path": "wordpress/wp-includes/blocks/rss/style.min.css", + "offset": 19689374, + "size": 652, + "sha256": "81a8ec4131de719fe46f74b0c619c1bfad324baef9eb043b1882e4716ee68684" + }, + { + "path": "wordpress/wp-includes/blocks/search/editor-rtl.css", + "offset": 19690026, + "size": 371, + "sha256": "aaf4709981c7867f93a78edfe65734ed4991f0e4a1191082fc8d5c1320f917d7" + }, + { + "path": "wordpress/wp-includes/blocks/search/editor-rtl.min.css", + "offset": 19690397, + "size": 326, + "sha256": "9e0c4870b5b588b6771fca210f183ae477e4e704b22014875f667d527ba5e2e8" + }, + { + "path": "wordpress/wp-includes/blocks/search/editor.css", + "offset": 19690723, + "size": 371, + "sha256": "aaf4709981c7867f93a78edfe65734ed4991f0e4a1191082fc8d5c1320f917d7" + }, + { + "path": "wordpress/wp-includes/blocks/search/editor.min.css", + "offset": 19691094, + "size": 326, + "sha256": "9e0c4870b5b588b6771fca210f183ae477e4e704b22014875f667d527ba5e2e8" + }, + { + "path": "wordpress/wp-includes/blocks/search/style-rtl.css", + "offset": 19691420, + "size": 3574, + "sha256": "af658b6c42fd8f3cb7bcab895f960c8823c1135eac951f3c0a33afcfc300cf1a" + }, + { + "path": "wordpress/wp-includes/blocks/search/style-rtl.min.css", + "offset": 19694994, + "size": 2390, + "sha256": "168a8df99aacc03b58a07c457e33ab403380d034d31425aa10952ab44956646a" + }, + { + "path": "wordpress/wp-includes/blocks/search/style.css", + "offset": 19697384, + "size": 3573, + "sha256": "cddd940a20ec40a6bc1f1639a1fb987d5d29e32964fa068108560f93a2948531" + }, + { + "path": "wordpress/wp-includes/blocks/search/style.min.css", + "offset": 19700957, + "size": 2389, + "sha256": "df6e38a94626af42752ed12f25de7c55f17ae729893490902c55817c5d550efe" + }, + { + "path": "wordpress/wp-includes/blocks/search/theme-rtl.css", + "offset": 19703346, + "size": 149, + "sha256": "75789f787c2a4448c8da80c96def5e91a7aa9a07ab0a35a95dbbe2b3196672ab" + }, + { + "path": "wordpress/wp-includes/blocks/search/theme-rtl.min.css", + "offset": 19703495, + "size": 126, + "sha256": "65d0b0f4c02d95b7b7a3eea7473b9ce5fa03140fa611e68ed27275c7011b584b" + }, + { + "path": "wordpress/wp-includes/blocks/search/theme.css", + "offset": 19703621, + "size": 149, + "sha256": "75789f787c2a4448c8da80c96def5e91a7aa9a07ab0a35a95dbbe2b3196672ab" + }, + { + "path": "wordpress/wp-includes/blocks/search/theme.min.css", + "offset": 19703770, + "size": 126, + "sha256": "65d0b0f4c02d95b7b7a3eea7473b9ce5fa03140fa611e68ed27275c7011b584b" + }, + { + "path": "wordpress/wp-includes/blocks/separator/editor-rtl.css", + "offset": 19703896, + "size": 110, + "sha256": "75f4d6d00573741e00e60a4c296ad619dfe95e49fdfe3232c15559ab6f8dd5f3" + }, + { + "path": "wordpress/wp-includes/blocks/separator/editor-rtl.min.css", + "offset": 19704006, + "size": 97, + "sha256": "6a3757a9af00c0d33685240ede15011065e7c1df7e5249f5d1e57084257a1d50" + }, + { + "path": "wordpress/wp-includes/blocks/separator/editor.css", + "offset": 19704103, + "size": 110, + "sha256": "75f4d6d00573741e00e60a4c296ad619dfe95e49fdfe3232c15559ab6f8dd5f3" + }, + { + "path": "wordpress/wp-includes/blocks/separator/editor.min.css", + "offset": 19704213, + "size": 97, + "sha256": "6a3757a9af00c0d33685240ede15011065e7c1df7e5249f5d1e57084257a1d50" + }, + { + "path": "wordpress/wp-includes/blocks/separator/style-rtl.css", + "offset": 19704310, + "size": 541, + "sha256": "a5c01b293116189861bc46a92c8544d17f871300d6124e754104dcbd6f0ccdea" + }, + { + "path": "wordpress/wp-includes/blocks/separator/style-rtl.min.css", + "offset": 19704851, + "size": 405, + "sha256": "341ddd8c8560a0e964be5dfbec80f7fce2bf5f360bb5dad886620a901fd480f7" + }, + { + "path": "wordpress/wp-includes/blocks/separator/style.css", + "offset": 19705256, + "size": 558, + "sha256": "f6df036d7b1ecd466096904f74e77c3a4c2ddb3a13db1525e8790036629dde44" + }, + { + "path": "wordpress/wp-includes/blocks/separator/style.min.css", + "offset": 19705814, + "size": 405, + "sha256": "341ddd8c8560a0e964be5dfbec80f7fce2bf5f360bb5dad886620a901fd480f7" + }, + { + "path": "wordpress/wp-includes/blocks/separator/theme-rtl.css", + "offset": 19706219, + "size": 521, + "sha256": "7786b6bbc055c0b0c55883b96fe8d6fb68901685f3fbd52a9feb0e6da2bad884" + }, + { + "path": "wordpress/wp-includes/blocks/separator/theme-rtl.min.css", + "offset": 19706740, + "size": 437, + "sha256": "0c6438dfff9c2ee9b863aaf7ccc21c4f40cebf35c23f6f43bef165f85f62e62a" + }, + { + "path": "wordpress/wp-includes/blocks/separator/theme.css", + "offset": 19707177, + "size": 521, + "sha256": "3125fc27ec4edeaf307a0b30591162f3ed2423f56333fc22822eeffd2cb034ae" + }, + { + "path": "wordpress/wp-includes/blocks/separator/theme.min.css", + "offset": 19707698, + "size": 437, + "sha256": "0c6438dfff9c2ee9b863aaf7ccc21c4f40cebf35c23f6f43bef165f85f62e62a" + }, + { + "path": "wordpress/wp-includes/blocks/shortcode/editor-rtl.css", + "offset": 19708135, + "size": 2418, + "sha256": "40fd83414a4fa380364bd726e720b111f7cc6f8f7418c7017704eb1f7952fe22" + }, + { + "path": "wordpress/wp-includes/blocks/shortcode/editor-rtl.min.css", + "offset": 19710553, + "size": 577, + "sha256": "e32da515edffcf9738a17a658ced98d64f38413cddff71db3bfb923a194560a3" + }, + { + "path": "wordpress/wp-includes/blocks/shortcode/editor.css", + "offset": 19711130, + "size": 2418, + "sha256": "40fd83414a4fa380364bd726e720b111f7cc6f8f7418c7017704eb1f7952fe22" + }, + { + "path": "wordpress/wp-includes/blocks/shortcode/editor.min.css", + "offset": 19713548, + "size": 577, + "sha256": "e32da515edffcf9738a17a658ced98d64f38413cddff71db3bfb923a194560a3" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/editor-rtl.css", + "offset": 19714125, + "size": 3410, + "sha256": "73ae84dccbc1ffa579acb2d252604598180f11c5c4bb697412565cebe88dacbc" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/editor-rtl.min.css", + "offset": 19717535, + "size": 2286, + "sha256": "22bffe8f32d74b334b0a1b7d4ba26bd03bf8ed00683c16f829bd1be0750c2782" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/editor.css", + "offset": 19719821, + "size": 3410, + "sha256": "f0d876e2c0870ca2c3295a221df1bbe69319148d7e86c8063b6b3acc79de9a36" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/editor.min.css", + "offset": 19723231, + "size": 2286, + "sha256": "cf27be4ad6a6377192a2113ca535528abe3dcf94589d0b192fcef020f9cddd21" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/style-rtl.css", + "offset": 19725517, + "size": 534, + "sha256": "4b6bb10b1c0fceb3e945f6b2aa312861e9b4c9b76fbc19531366eb4da4be3fdd" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/style-rtl.min.css", + "offset": 19726051, + "size": 453, + "sha256": "88bfd9470ef9542b8418b116f1f99c732224deeee305a4e7b44ed59bd623ce02" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/style.css", + "offset": 19726504, + "size": 534, + "sha256": "3bad81efcf5dba56449a58602c31624570143bf96e9e126fd60aa60cb73bc2f5" + }, + { + "path": "wordpress/wp-includes/blocks/site-logo/style.min.css", + "offset": 19727038, + "size": 453, + "sha256": "88bfd9470ef9542b8418b116f1f99c732224deeee305a4e7b44ed59bd623ce02" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/editor-rtl.css", + "offset": 19727491, + "size": 79, + "sha256": "536b01e7ddb4ae7510a2b89f65fe8fba70d24c8aa822bb4ff4e7b98462bc1e66" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/editor-rtl.min.css", + "offset": 19727570, + "size": 68, + "sha256": "7e64dc70968924991c1404a753fe55ec082f8e46b119592c1a03b876d8f3b81c" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/editor.css", + "offset": 19727638, + "size": 79, + "sha256": "536b01e7ddb4ae7510a2b89f65fe8fba70d24c8aa822bb4ff4e7b98462bc1e66" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/editor.min.css", + "offset": 19727717, + "size": 68, + "sha256": "7e64dc70968924991c1404a753fe55ec082f8e46b119592c1a03b876d8f3b81c" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/style-rtl.css", + "offset": 19727785, + "size": 52, + "sha256": "0231f5f37c6f7d0c8cfc3e41352a0e37ce568082529aa9952a938f628da8f8dd" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/style-rtl.min.css", + "offset": 19727837, + "size": 45, + "sha256": "84f0671a6687c88461d6c4c293fbb50978b91ff054e6b104f14410263fd73cfa" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/style.css", + "offset": 19727882, + "size": 52, + "sha256": "0231f5f37c6f7d0c8cfc3e41352a0e37ce568082529aa9952a938f628da8f8dd" + }, + { + "path": "wordpress/wp-includes/blocks/site-tagline/style.min.css", + "offset": 19727934, + "size": 45, + "sha256": "84f0671a6687c88461d6c4c293fbb50978b91ff054e6b104f14410263fd73cfa" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/editor-rtl.css", + "offset": 19727979, + "size": 77, + "sha256": "2c2f780001aa789d569fba339aef64e536bb7e4905e2740cc384532e87062ba0" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/editor-rtl.min.css", + "offset": 19728056, + "size": 66, + "sha256": "8c94f3a5649721272d9437f79a2415e1e0c879e02100c4e90d9ff12cc59c53ae" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/editor.css", + "offset": 19728122, + "size": 77, + "sha256": "2c2f780001aa789d569fba339aef64e536bb7e4905e2740cc384532e87062ba0" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/editor.min.css", + "offset": 19728199, + "size": 66, + "sha256": "8c94f3a5649721272d9437f79a2415e1e0c879e02100c4e90d9ff12cc59c53ae" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/style-rtl.css", + "offset": 19728265, + "size": 275, + "sha256": "9d899520dea0df0c3f1dfd86a13ddda45f010c7a00a82f5596effd35983e4083" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/style-rtl.min.css", + "offset": 19728540, + "size": 232, + "sha256": "eebbf4533110d92d4d5dbb60284b6476db0ac9689191885cad2c90ce2eba01e2" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/style.css", + "offset": 19728772, + "size": 275, + "sha256": "9d899520dea0df0c3f1dfd86a13ddda45f010c7a00a82f5596effd35983e4083" + }, + { + "path": "wordpress/wp-includes/blocks/site-title/style.min.css", + "offset": 19729047, + "size": 232, + "sha256": "eebbf4533110d92d4d5dbb60284b6476db0ac9689191885cad2c90ce2eba01e2" + }, + { + "path": "wordpress/wp-includes/blocks/social-link/editor-rtl.css", + "offset": 19729279, + "size": 746, + "sha256": "7d9c04ccc1156666af216d43270f9bf97e1c4daeee2cb9b96dd7a16a3bdbe879" + }, + { + "path": "wordpress/wp-includes/blocks/social-link/editor-rtl.min.css", + "offset": 19730025, + "size": 636, + "sha256": "cbfddb6f1e32670c0b88ede84761ab41182f7f8be8a3993ad754829c8cbfa0e2" + }, + { + "path": "wordpress/wp-includes/blocks/social-link/editor.css", + "offset": 19730661, + "size": 746, + "sha256": "2c764c8d44d0f885a34fd3a583713ea70a9f0fe0e581abbc93e768c0a781a583" + }, + { + "path": "wordpress/wp-includes/blocks/social-link/editor.min.css", + "offset": 19731407, + "size": 636, + "sha256": "cbfddb6f1e32670c0b88ede84761ab41182f7f8be8a3993ad754829c8cbfa0e2" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/editor-rtl.css", + "offset": 19732043, + "size": 1770, + "sha256": "aec216a2b927bd24118fd662857387b2bff34097d57d238841599be707f4dd49" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/editor-rtl.min.css", + "offset": 19733813, + "size": 882, + "sha256": "31ff5706ecddd7dbc40adb19e791d8675283873c871a97b805838c271480427f" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/editor.css", + "offset": 19734695, + "size": 1769, + "sha256": "75feb16693bda7e5d97e739d9ef8d710ab41c8b066a1b66f5cb333d74c318ec6" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/editor.min.css", + "offset": 19736464, + "size": 881, + "sha256": "16c0b0b7b2de843afa915229547868e33ea9d63d143d35111ac631a8ff58c08e" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/style-rtl.css", + "offset": 19737345, + "size": 12983, + "sha256": "50d1adefd622ebae725acb0cc10e89fa7b071c843cf8d037bdacc04407c80172" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/style-rtl.min.css", + "offset": 19750328, + "size": 11763, + "sha256": "23f2631f3c37db5bda4844da898a31caf818b418fca050ddd76316b98dbaffdb" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/style.css", + "offset": 19762091, + "size": 12982, + "sha256": "f0a9b59e1800da133b39ba933df08417364762cc29f5bb916453b2573584b1fb" + }, + { + "path": "wordpress/wp-includes/blocks/social-links/style.min.css", + "offset": 19775073, + "size": 11762, + "sha256": "553d6c5803709a741e04e02dba67a54a614ebc63539c488823baf92cd7b4fefd" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/editor-rtl.css", + "offset": 19786835, + "size": 1855, + "sha256": "11d45501dfab06a3c1a1798ffcf4ff03c4caaacfccad4c9ce10b71f0d4d5e4d6" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/editor-rtl.min.css", + "offset": 19788690, + "size": 948, + "sha256": "86f2883d0e756b68373bfd6f9df4e48e71c0f09de7c1923f61b50313833817b7" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/editor.css", + "offset": 19789638, + "size": 1855, + "sha256": "11d45501dfab06a3c1a1798ffcf4ff03c4caaacfccad4c9ce10b71f0d4d5e4d6" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/editor.min.css", + "offset": 19791493, + "size": 948, + "sha256": "86f2883d0e756b68373bfd6f9df4e48e71c0f09de7c1923f61b50313833817b7" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/style-rtl.css", + "offset": 19792441, + "size": 35, + "sha256": "d622c5eb5ee6345a9c554a9cc91fa56bc543839f2a48e40f6f0b814bea428f8b" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/style-rtl.min.css", + "offset": 19792476, + "size": 28, + "sha256": "7532ff54d8da7a22bb84edffcf2d67be89b6265dcc56db136ab295244f3e1f8b" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/style.css", + "offset": 19792504, + "size": 35, + "sha256": "d622c5eb5ee6345a9c554a9cc91fa56bc543839f2a48e40f6f0b814bea428f8b" + }, + { + "path": "wordpress/wp-includes/blocks/spacer/style.min.css", + "offset": 19792539, + "size": 28, + "sha256": "7532ff54d8da7a22bb84edffcf2d67be89b6265dcc56db136ab295244f3e1f8b" + }, + { + "path": "wordpress/wp-includes/blocks/table/editor-rtl.css", + "offset": 19792567, + "size": 3461, + "sha256": "4611114f916e47c8d7e094df799e20ac19ddd7390942361f37353e83c571b38a" + }, + { + "path": "wordpress/wp-includes/blocks/table/editor-rtl.min.css", + "offset": 19796028, + "size": 1495, + "sha256": "945b73bfafa8389394025893ac8c1162a9e8fb5fcb2c0d14a70dad15b982c3ff" + }, + { + "path": "wordpress/wp-includes/blocks/table/editor.css", + "offset": 19797523, + "size": 3461, + "sha256": "4611114f916e47c8d7e094df799e20ac19ddd7390942361f37353e83c571b38a" + }, + { + "path": "wordpress/wp-includes/blocks/table/editor.min.css", + "offset": 19800984, + "size": 1495, + "sha256": "945b73bfafa8389394025893ac8c1162a9e8fb5fcb2c0d14a70dad15b982c3ff" + }, + { + "path": "wordpress/wp-includes/blocks/table/style-rtl.css", + "offset": 19802479, + "size": 5080, + "sha256": "81d85158ebb07335401174c845bd0cec6a6ef868e0542f550a9fa136542cb49d" + }, + { + "path": "wordpress/wp-includes/blocks/table/style-rtl.min.css", + "offset": 19807559, + "size": 3888, + "sha256": "51674c258f5b613b6d7d80ee2966ec330557ab33467327738668dffa645bcdbe" + }, + { + "path": "wordpress/wp-includes/blocks/table/style.css", + "offset": 19811447, + "size": 5080, + "sha256": "a386d9d775c2c00b8ea7208ec2df1d4de91aa5a658be975da0770af7bce006b4" + }, + { + "path": "wordpress/wp-includes/blocks/table/style.min.css", + "offset": 19816527, + "size": 3888, + "sha256": "bb5bd8340a93b27f6075624a94b977ef43cc360859c8b8d30d26a6a73ca37613" + }, + { + "path": "wordpress/wp-includes/blocks/table/theme-rtl.css", + "offset": 19820415, + "size": 2029, + "sha256": "6ee6bfe9fb5dd0aad8367e26f7fbade0884abad96347e918bcc796a8cf73052b" + }, + { + "path": "wordpress/wp-includes/blocks/table/theme-rtl.min.css", + "offset": 19822444, + "size": 232, + "sha256": "0a161230525a2d7528eac14c1e5253b8d631d7e787a183733f01fa6670945e57" + }, + { + "path": "wordpress/wp-includes/blocks/table/theme.css", + "offset": 19822676, + "size": 2029, + "sha256": "6ee6bfe9fb5dd0aad8367e26f7fbade0884abad96347e918bcc796a8cf73052b" + }, + { + "path": "wordpress/wp-includes/blocks/table/theme.min.css", + "offset": 19824705, + "size": 232, + "sha256": "0a161230525a2d7528eac14c1e5253b8d631d7e787a183733f01fa6670945e57" + }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud/style-rtl.css", + "offset": 19824937, + "size": 606, + "sha256": "8704d4413b1d2f69dfb6111062391376a07188b20a2a28845713ee1bc9658acf" + }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud/style-rtl.min.css", + "offset": 19825543, + "size": 502, + "sha256": "828211200038113cf4f587100c24628656ad3eabfc35f9fa9c0c0421610c7f60" + }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud/style.css", + "offset": 19826045, + "size": 607, + "sha256": "b67fce56649938d490d5f8df5884e4a4d6114d954469b74d08cc5f105a30f3a7" + }, + { + "path": "wordpress/wp-includes/blocks/tag-cloud/style.min.css", + "offset": 19826652, + "size": 503, + "sha256": "14593e359e5efdf71c634319f30796b5a69ff4c4922dd19c1a9453ea12052087" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/editor-rtl.css", + "offset": 19827155, + "size": 3249, + "sha256": "aa93cbe40788836cec4924785df38af04347300a6199b4302d58a790517db67e" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/editor-rtl.min.css", + "offset": 19830404, + "size": 1360, + "sha256": "afb251f1761ff388c937ce4b1c2ed969d527ddac7867cab98dce051e3e7f0ac8" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/editor.css", + "offset": 19831764, + "size": 3249, + "sha256": "aa93cbe40788836cec4924785df38af04347300a6199b4302d58a790517db67e" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/editor.min.css", + "offset": 19835013, + "size": 1360, + "sha256": "afb251f1761ff388c937ce4b1c2ed969d527ddac7867cab98dce051e3e7f0ac8" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/theme-rtl.css", + "offset": 19836373, + "size": 900, + "sha256": "03ecb2bc6ae7aa230a4861efaa6473ce1708ec7cebe96003f8df53e1cfd470f9" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/theme-rtl.min.css", + "offset": 19837273, + "size": 105, + "sha256": "85b366f1200d3a58cf06d32173c73c7704da73562134c789656c58f0a7e81a17" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/theme.css", + "offset": 19837378, + "size": 900, + "sha256": "03ecb2bc6ae7aa230a4861efaa6473ce1708ec7cebe96003f8df53e1cfd470f9" + }, + { + "path": "wordpress/wp-includes/blocks/template-part/theme.min.css", + "offset": 19838278, + "size": 105, + "sha256": "85b366f1200d3a58cf06d32173c73c7704da73562134c789656c58f0a7e81a17" + }, + { + "path": "wordpress/wp-includes/blocks/term-count/style-rtl.css", + "offset": 19838383, + "size": 50, + "sha256": "cd61e48f387bdf1c4e76beb5017b6386c9d3389c9c9059c344a86285f56fd590" + }, + { + "path": "wordpress/wp-includes/blocks/term-count/style-rtl.min.css", + "offset": 19838433, + "size": 43, + "sha256": "db8513c7798a8fbfc6119fb5be7a90c418ddab94b51a188859bc4d1743134ad4" + }, + { + "path": "wordpress/wp-includes/blocks/term-count/style.css", + "offset": 19838476, + "size": 50, + "sha256": "cd61e48f387bdf1c4e76beb5017b6386c9d3389c9c9059c344a86285f56fd590" + }, + { + "path": "wordpress/wp-includes/blocks/term-count/style.min.css", + "offset": 19838526, + "size": 43, + "sha256": "db8513c7798a8fbfc6119fb5be7a90c418ddab94b51a188859bc4d1743134ad4" + }, + { + "path": "wordpress/wp-includes/blocks/term-description/style-rtl.css", + "offset": 19838569, + "size": 224, + "sha256": "f23691c6b81750187694528332d2fa626d3e98a8126eae658f81ca715d12a966" + }, + { + "path": "wordpress/wp-includes/blocks/term-description/style-rtl.min.css", + "offset": 19838793, + "size": 196, + "sha256": "e31d1c947396c1c9ab41cbc079af8664e673c83970bdfeb50cb4223e09620128" + }, + { + "path": "wordpress/wp-includes/blocks/term-description/style.css", + "offset": 19838989, + "size": 224, + "sha256": "f23691c6b81750187694528332d2fa626d3e98a8126eae658f81ca715d12a966" + }, + { + "path": "wordpress/wp-includes/blocks/term-description/style.min.css", + "offset": 19839213, + "size": 196, + "sha256": "e31d1c947396c1c9ab41cbc079af8664e673c83970bdfeb50cb4223e09620128" + }, + { + "path": "wordpress/wp-includes/blocks/term-name/style-rtl.css", + "offset": 19839409, + "size": 49, + "sha256": "87a73237e41b708e51c70f7f6149331f184a055677f53d1d45c369d0dd17a226" + }, + { + "path": "wordpress/wp-includes/blocks/term-name/style-rtl.min.css", + "offset": 19839458, + "size": 42, + "sha256": "2ae714bef79076276fc834ed507b2b6034424cbb9255d3bb9abe3b5bf653b99d" + }, + { + "path": "wordpress/wp-includes/blocks/term-name/style.css", + "offset": 19839500, + "size": 49, + "sha256": "87a73237e41b708e51c70f7f6149331f184a055677f53d1d45c369d0dd17a226" + }, + { + "path": "wordpress/wp-includes/blocks/term-name/style.min.css", + "offset": 19839549, + "size": 42, + "sha256": "2ae714bef79076276fc834ed507b2b6034424cbb9255d3bb9abe3b5bf653b99d" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/editor-rtl.css", + "offset": 19839591, + "size": 475, + "sha256": "e7c96bf284bd624ccb39b9f36efc64957bbf3a5ed3c342580931869c4ce61fda" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/editor-rtl.min.css", + "offset": 19840066, + "size": 367, + "sha256": "0b774f57c5f4064056e360c21ad1f2c936c77c51ae8cfed16f6ed241f5fbac99" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/editor.css", + "offset": 19840433, + "size": 475, + "sha256": "e7c96bf284bd624ccb39b9f36efc64957bbf3a5ed3c342580931869c4ce61fda" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/editor.min.css", + "offset": 19840908, + "size": 367, + "sha256": "0b774f57c5f4064056e360c21ad1f2c936c77c51ae8cfed16f6ed241f5fbac99" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/style-rtl.css", + "offset": 19841275, + "size": 143, + "sha256": "416f2fcdbcd6f85aae3dae35aa56d52526e0109bf6d42047ab21c98d45ab4b34" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/style-rtl.min.css", + "offset": 19841418, + "size": 116, + "sha256": "6a2e3afdc9e2cac94f9128283ecf4981972dc703dd40a0c747cbd374a8a40184" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/style.css", + "offset": 19841534, + "size": 143, + "sha256": "416f2fcdbcd6f85aae3dae35aa56d52526e0109bf6d42047ab21c98d45ab4b34" + }, + { + "path": "wordpress/wp-includes/blocks/term-template/style.min.css", + "offset": 19841677, + "size": 116, + "sha256": "6a2e3afdc9e2cac94f9128283ecf4981972dc703dd40a0c747cbd374a8a40184" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/editor-rtl.css", + "offset": 19841793, + "size": 873, + "sha256": "4ff3bbe3e229cb5b1e052a739ef0b77536e1850dfcb17b6a0a9bbafef780e8a9" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/editor-rtl.min.css", + "offset": 19842666, + "size": 86, + "sha256": "9bbe4433e6b7a99b205e25e6b463337fbdaf22470e64716f501a535b201a29e4" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/editor.css", + "offset": 19842752, + "size": 873, + "sha256": "4ff3bbe3e229cb5b1e052a739ef0b77536e1850dfcb17b6a0a9bbafef780e8a9" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/editor.min.css", + "offset": 19843625, + "size": 86, + "sha256": "9bbe4433e6b7a99b205e25e6b463337fbdaf22470e64716f501a535b201a29e4" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/style-rtl.css", + "offset": 19843711, + "size": 537, + "sha256": "8e6cca29acd0e79974546e56f8a32ec0172c7123ec8e910420a1503e24a08f26" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/style-rtl.min.css", + "offset": 19844248, + "size": 457, + "sha256": "d675326833c1abcda9ea106e1760d84ba2fafe0a2878beddbfc3c482129cc484" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/style.css", + "offset": 19844705, + "size": 537, + "sha256": "8502e85cf12eae0cdb5b9521f342eff9d254b61890f4ea063230ef27ff74431c" + }, + { + "path": "wordpress/wp-includes/blocks/text-columns/style.min.css", + "offset": 19845242, + "size": 457, + "sha256": "cc892c13b7f850fb6e7a14eaa727e96172c4d65924b13aadb240f63bfb09abb4" + }, + { + "path": "wordpress/wp-includes/blocks/verse/style-rtl.css", + "offset": 19845699, + "size": 191, + "sha256": "716c59cff6b649296364afef015f917aa99832aa0b1d3d4d6f58540174af0155" + }, + { + "path": "wordpress/wp-includes/blocks/verse/style-rtl.min.css", + "offset": 19845890, + "size": 159, + "sha256": "ecc3099a1ebafbe1e6456998e8be7cb95d1cf576bc20ebab0c08f66e3d1cddf9" + }, + { + "path": "wordpress/wp-includes/blocks/verse/style.css", + "offset": 19846049, + "size": 191, + "sha256": "716c59cff6b649296364afef015f917aa99832aa0b1d3d4d6f58540174af0155" + }, + { + "path": "wordpress/wp-includes/blocks/verse/style.min.css", + "offset": 19846240, + "size": 159, + "sha256": "ecc3099a1ebafbe1e6456998e8be7cb95d1cf576bc20ebab0c08f66e3d1cddf9" + }, + { + "path": "wordpress/wp-includes/blocks/video/editor-rtl.css", + "offset": 19846399, + "size": 2038, + "sha256": "e9be1d35aeb5bcd2506a8a3348ecbadcd8a4d871ced3e367ec33cd3d19c42946" + }, + { + "path": "wordpress/wp-includes/blocks/video/editor-rtl.min.css", + "offset": 19848437, + "size": 1112, + "sha256": "ae3521e00582c0853feecebabba3b9872a5844fe8ccd23be4a1f24b5bd2411a6" + }, + { + "path": "wordpress/wp-includes/blocks/video/editor.css", + "offset": 19849549, + "size": 2035, + "sha256": "b911121751d5c5c65e1fd8a0f4af1c84966d7ed5fbb8ef3d05da8cf16f5b0e03" + }, + { + "path": "wordpress/wp-includes/blocks/video/editor.min.css", + "offset": 19851584, + "size": 1109, + "sha256": "c4e0f6c5ccdc991a2a8a826596932c451e09eef3b184f6d5913d76dc347f98a3" + }, + { + "path": "wordpress/wp-includes/blocks/video/style-rtl.css", + "offset": 19852693, + "size": 2092, + "sha256": "b673f8c6c9b931f78e5221bc297309f2be1c5745f82ee4c9acd4600cac326b93" + }, + { + "path": "wordpress/wp-includes/blocks/video/style-rtl.min.css", + "offset": 19854785, + "size": 291, + "sha256": "5d02b68b7c929c457ff633daa86ab577581bf35504adab59fa31c58aabdfd719" + }, + { + "path": "wordpress/wp-includes/blocks/video/style.css", + "offset": 19855076, + "size": 2092, + "sha256": "b673f8c6c9b931f78e5221bc297309f2be1c5745f82ee4c9acd4600cac326b93" + }, + { + "path": "wordpress/wp-includes/blocks/video/style.min.css", + "offset": 19857168, + "size": 291, + "sha256": "5d02b68b7c929c457ff633daa86ab577581bf35504adab59fa31c58aabdfd719" + }, + { + "path": "wordpress/wp-includes/blocks/video/theme-rtl.css", + "offset": 19857459, + "size": 1965, + "sha256": "3345fc2a118e2a0cdc643e527f923cad683a1ce221905431480876b2af4d1323" + }, + { + "path": "wordpress/wp-includes/blocks/video/theme-rtl.min.css", + "offset": 19859424, + "size": 176, + "sha256": "8effbc651a2a28bb0ac10daffae3347ff2eac1d352d4c0a85aed235e9a5037a5" + }, + { + "path": "wordpress/wp-includes/blocks/video/theme.css", + "offset": 19859600, + "size": 1965, + "sha256": "3345fc2a118e2a0cdc643e527f923cad683a1ce221905431480876b2af4d1323" + }, + { + "path": "wordpress/wp-includes/blocks/video/theme.min.css", + "offset": 19861565, + "size": 176, + "sha256": "8effbc651a2a28bb0ac10daffae3347ff2eac1d352d4c0a85aed235e9a5037a5" + }, + { + "path": "wordpress/wp-includes/build/pages/font-library/loader.js", + "offset": 19861741, + "size": 45, + "sha256": "a2ef03a1a68cf9cef2439760e50991389c0038903f3a20a1653164afb9f1fdf6" + }, + { + "path": "wordpress/wp-includes/build/pages/options-connectors/loader.js", + "offset": 19861786, + "size": 45, + "sha256": "a2ef03a1a68cf9cef2439760e50991389c0038903f3a20a1653164afb9f1fdf6" + }, + { + "path": "wordpress/wp-includes/build/routes/connectors-home/content.js", + "offset": 19861831, + "size": 97865, + "sha256": "1d421ac0b966ed365d122a6db45454a6f4f6ad67ca083c6e7345f64f96ab11ba" + }, + { + "path": "wordpress/wp-includes/build/routes/connectors-home/content.min.js", + "offset": 19959696, + "size": 64475, + "sha256": "b614fcaf0d408b3eff9d20ec4c0085851cf1e044f7d0d40643b1546a0fab47a3" + }, + { + "path": "wordpress/wp-includes/build/routes/connectors-home/route.js", + "offset": 20024171, + "size": 2273, + "sha256": "1b3006d660b2d6fe495448f9495cb4d86f59ce8917aeb548702555ed9a3540ab" + }, + { + "path": "wordpress/wp-includes/build/routes/connectors-home/route.min.js", + "offset": 20026444, + "size": 840, + "sha256": "f3b33cd01621a8692a0f176902c24f829b259c5ebf3b1c9c6c2d7ea2a55eba7d" + }, + { + "path": "wordpress/wp-includes/build/routes/font-list/content.js", + "offset": 20027284, + "size": 574953, + "sha256": "7fcfebaa9bf7d54685352e84662ccae2f774c327b4189a36ab125d939dec0830" + }, + { + "path": "wordpress/wp-includes/build/routes/font-list/content.min.js", + "offset": 20602237, + "size": 273686, + "sha256": "f4a76b3cfc58409a8d9c147c0f9ef99258f5917b2fbc196bafd3045ddc2ef0e8" + }, + { + "path": "wordpress/wp-includes/build/routes/font-list/route.js", + "offset": 20875923, + "size": 1644, + "sha256": "40e8a79274432840edc224829ecd3e7a4673d58c6601e0bc9f32f5dfb35d8487" + }, + { + "path": "wordpress/wp-includes/build/routes/font-list/route.min.js", + "offset": 20877567, + "size": 629, + "sha256": "5bf837c01b0b1bb1bc6f72841410bef093d6f6350efe23d2fa2d11a6b81977d5" + }, + { + "path": "wordpress/wp-includes/build/routes/fonts-home/route.js", + "offset": 20878196, + "size": 209, + "sha256": "5c26508f17fe8fa3e91195e146164dff2467d7d3c72d22da864c6ea1e49d6085" + }, + { + "path": "wordpress/wp-includes/build/routes/fonts-home/route.min.js", + "offset": 20878405, + "size": 125, + "sha256": "63fba8ad1ac5f2b9aba875c1efa275e440138cd0d452e15c183b1ab6b7867981" + }, + { + "path": "wordpress/wp-includes/css/admin-bar-rtl.css", + "offset": 20878530, + "size": 25623, + "sha256": "a32e92e4e732bc2607c4fadbab36dfa13cd567174d5600aaa7a5a7eb615b4489" + }, + { + "path": "wordpress/wp-includes/css/admin-bar-rtl.min.css", + "offset": 20904153, + "size": 20782, + "sha256": "0ceaa1a0663facf27739d463e7f3cc17bf6bc40887b39a38d4bfe06871961ce9" + }, + { + "path": "wordpress/wp-includes/css/admin-bar.css", + "offset": 20924935, + "size": 25587, + "sha256": "0597bf7446b57a3a6d79896f63b3bb199049534878c7fc73fd0d8d9d77c5efbd" + }, + { + "path": "wordpress/wp-includes/css/admin-bar.min.css", + "offset": 20950522, + "size": 20781, + "sha256": "96fd40fad8d03bf7b1ffb0ebdcb6e8053ba8ccf9dd8918706b06c210f663e564" + }, + { + "path": "wordpress/wp-includes/css/buttons-rtl.css", + "offset": 20971303, + "size": 12991, + "sha256": "5144c537c51e1dfd46936fc09e32117477ba9c4b4314bbbd072e434b30d26206" + }, + { + "path": "wordpress/wp-includes/css/buttons-rtl.min.css", + "offset": 20984294, + "size": 8154, + "sha256": "874f5d7dbeeddda7dd2a410801c8e3897ad3d884c2e8879f1fb9fb393f85e9fc" + }, + { + "path": "wordpress/wp-includes/css/buttons.css", + "offset": 20992448, + "size": 12954, + "sha256": "5029d390ee1f6236d6b8bb48d56c5e29d6e73d06bb674a2c21e58fac70b630ec" + }, + { + "path": "wordpress/wp-includes/css/buttons.min.css", + "offset": 21005402, + "size": 8152, + "sha256": "dd8f0d50298e6c2498535cc891a7b5459971471eab331700af766c2f5ead2e7f" + }, + { + "path": "wordpress/wp-includes/css/classic-themes.css", + "offset": 21013554, + "size": 729, + "sha256": "5c0c65630b301eea0eed759a5584f8bc1917b2fbc36d7f43b7fafb9fadf0dfb3" + }, + { + "path": "wordpress/wp-includes/css/classic-themes.min.css", + "offset": 21014283, + "size": 291, + "sha256": "dcd9f488bd62ba0ee403b07a97e40b9ffd63a0eff61091588c913b16d5153d48" + }, + { + "path": "wordpress/wp-includes/css/customize-preview-rtl.css", + "offset": 21014574, + "size": 3798, + "sha256": "1447a5e39f02878152edaf5968ba7bf1985a787abdb1953e2b9001e0445c9714" + }, + { + "path": "wordpress/wp-includes/css/customize-preview-rtl.min.css", + "offset": 21018372, + "size": 2996, + "sha256": "c6de4d74043923f72f0856c0f0359b8289f7cebed83ad59cbc44cbf98c7e7de9" + }, + { + "path": "wordpress/wp-includes/css/customize-preview.css", + "offset": 21021368, + "size": 3758, + "sha256": "f7a258d61320860af33e96d0194cc028aa938e7d30ef6fff1cc82721240cb3ba" + }, + { + "path": "wordpress/wp-includes/css/customize-preview.min.css", + "offset": 21025126, + "size": 2991, + "sha256": "933f182c3c05653fb8888ffa8da7f5754732b4ce87bb302c6cf4e495a8d66173" + }, + { + "path": "wordpress/wp-includes/css/dashicons.css", + "offset": 21028117, + "size": 62346, + "sha256": "195221d266b36a8136f71e7fd7b241b80ff2080efeb139db9968b65100b4bbca" + }, + { + "path": "wordpress/wp-includes/css/dashicons.min.css", + "offset": 21090463, + "size": 59004, + "sha256": "26a7e7b9d4bb9948771c126085b99fba6546af4399e6263e8a69a3e33f6271cb" + }, + { + "path": "wordpress/wp-includes/css/dist/base-styles/admin-schemes-rtl.css", + "offset": 21149467, + "size": 6468, + "sha256": "0d9c277a241f5dafa9f4d9eb31912ccb869023a0400cb69f1d3bdbe182eba0ce" + }, + { + "path": "wordpress/wp-includes/css/dist/base-styles/admin-schemes-rtl.min.css", + "offset": 21155935, + "size": 3656, + "sha256": "932033b8cfc5596f639dbd9fe39a3600e3c18b6020e9151aac76336f28e9f376" + }, + { + "path": "wordpress/wp-includes/css/dist/base-styles/admin-schemes.css", + "offset": 21159591, + "size": 6468, + "sha256": "0d9c277a241f5dafa9f4d9eb31912ccb869023a0400cb69f1d3bdbe182eba0ce" + }, + { + "path": "wordpress/wp-includes/css/dist/base-styles/admin-schemes.min.css", + "offset": 21166059, + "size": 3656, + "sha256": "932033b8cfc5596f639dbd9fe39a3600e3c18b6020e9151aac76336f28e9f376" + }, + { + "path": "wordpress/wp-includes/css/dist/block-directory/style-rtl.css", + "offset": 21169715, + "size": 6213, + "sha256": "58618df2233d26cc2a75f439d72376d87b14872a3e5821696f22b46169c5c914" + }, + { + "path": "wordpress/wp-includes/css/dist/block-directory/style-rtl.min.css", + "offset": 21175928, + "size": 3743, + "sha256": "7e26313b6826235c935b3bdeadaa685632a16196f5b524a6722048d1fb28db02" + }, + { + "path": "wordpress/wp-includes/css/dist/block-directory/style.css", + "offset": 21179671, + "size": 6213, + "sha256": "c0a3e729efc64aa5afd07fbe4b5d57abf6326584d4eea17d7df363c627fb0711" + }, + { + "path": "wordpress/wp-includes/css/dist/block-directory/style.min.css", + "offset": 21185884, + "size": 3743, + "sha256": "e939e22ccf500715dc3aa9ae87aba32ec90b6b4750ffa0da5062980a4ee177e3" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/content-rtl.css", + "offset": 21189627, + "size": 32621, + "sha256": "89c2245559415a651e5792625ef5b6e821da9ef85afc940ba9893351b71f1274" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/content-rtl.min.css", + "offset": 21222248, + "size": 26927, + "sha256": "cd142cc1814180cb0942aa9645513d54b49337361e0c65ae5adf2a705ae3ebbb" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/content.css", + "offset": 21249175, + "size": 32619, + "sha256": "b29685a36e64e0294655cea3b2919490d5992421c25c10f9ab246ad3086bd915" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/content.min.css", + "offset": 21281794, + "size": 26925, + "sha256": "39c6c623fc65da7060d31946daba21b75a7d0c017c428a54d072a9f8482fe7cb" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/default-editor-styles-rtl.css", + "offset": 21308719, + "size": 1319, + "sha256": "70402028e4cd90de3dc4f8aa6a506b0d2041d6dc62906ddd5726ed3a0c1b1454" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/default-editor-styles-rtl.min.css", + "offset": 21310038, + "size": 288, + "sha256": "1eab34d0ffc481cb0a145fc58be31e6f598aaad7150ce5f405d4aa57a91e17fc" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/default-editor-styles.css", + "offset": 21310326, + "size": 1319, + "sha256": "70402028e4cd90de3dc4f8aa6a506b0d2041d6dc62906ddd5726ed3a0c1b1454" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/default-editor-styles.min.css", + "offset": 21311645, + "size": 288, + "sha256": "1eab34d0ffc481cb0a145fc58be31e6f598aaad7150ce5f405d4aa57a91e17fc" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/style-rtl.css", + "offset": 21311933, + "size": 133621, + "sha256": "277e205072f3486d72b5dde374b1cceee845b9b8ed695730706459942f00e6ae" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/style-rtl.min.css", + "offset": 21445554, + "size": 116073, + "sha256": "61a2ca84115f6c51ecafdfc6fec07854db145078f2b180eb9d406216f738d916" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/style.css", + "offset": 21561627, + "size": 133659, + "sha256": "4ccb0abb73d8b09a1655304febc60ec032f0910221cab9d898d6320fd61e2c3a" + }, + { + "path": "wordpress/wp-includes/css/dist/block-editor/style.min.css", + "offset": 21695286, + "size": 116057, + "sha256": "b942a796ad35563d03151e8c67af0d3867bacb0be6b0cfe296dfdefbf5fa5619" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/classic-rtl.css", + "offset": 21811343, + "size": 881, + "sha256": "6669c569f7d1c3eb248472c88fe3c73bfdd5903c00b819898e99324bcbd3335b" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/classic-rtl.min.css", + "offset": 21812224, + "size": 743, + "sha256": "e83b9337fca8622060dd6d5de23d932185298113ff726c63868abc75f0085c70" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/classic.css", + "offset": 21812967, + "size": 881, + "sha256": "6669c569f7d1c3eb248472c88fe3c73bfdd5903c00b819898e99324bcbd3335b" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/classic.min.css", + "offset": 21813848, + "size": 743, + "sha256": "e83b9337fca8622060dd6d5de23d932185298113ff726c63868abc75f0085c70" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/common-rtl.css", + "offset": 21814591, + "size": 7250, + "sha256": "225a3c8e38badddaa331fbc4550f969e87bf5081025d7403bf159cf5e96d06c4" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/common-rtl.min.css", + "offset": 21821841, + "size": 3585, + "sha256": "7434f3755833ba14a55a00f0ec63170177bdcf8535680e2dc48fec4276df2e0f" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/common.css", + "offset": 21825426, + "size": 7344, + "sha256": "cf20c8dfde1b1f1e7091faf437855be0dfb4b7fa999e0d0cf72ff0eb72a8d34b" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/common.min.css", + "offset": 21832770, + "size": 3577, + "sha256": "02ca56cc8acd6f891a1224e60a08a592cedf20d731c6645c24473c7666941471" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/editor-elements-rtl.css", + "offset": 21836347, + "size": 134, + "sha256": "80444efe8539c19572c084650ffbdf6ab777448362b76c54be585f052f84b3cd" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/editor-elements-rtl.min.css", + "offset": 21836481, + "size": 78, + "sha256": "f202a7930118c856084c6298f5fbd834648e6f70f9b0f377ffbeaf58834bf9c9" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/editor-elements.css", + "offset": 21836559, + "size": 134, + "sha256": "80444efe8539c19572c084650ffbdf6ab777448362b76c54be585f052f84b3cd" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/editor-elements.min.css", + "offset": 21836693, + "size": 78, + "sha256": "f202a7930118c856084c6298f5fbd834648e6f70f9b0f377ffbeaf58834bf9c9" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/editor-rtl.css", + "offset": 21836771, + "size": 74838, + "sha256": "cd29d5610e1bbce3881e3b284a4a03bc7da3df057836d9920515cd044a8c0010" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/editor-rtl.min.css", + "offset": 21911609, + "size": 62406, + "sha256": "85e0e2ac384c87bf8f9c024f47561325c0736785830dbb01838100437c116c19" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/editor.css", + "offset": 21974015, + "size": 74890, + "sha256": "adb5636ca84e389e2d39fda6365c8fd2ecbcc47da3e31c29790daa10f6653dae" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/editor.min.css", + "offset": 22048905, + "size": 62372, + "sha256": "5c19e79f4acab8291e021bce75561ac69cfedeb562974aaf0b953c52f36c5a82" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/elements-rtl.css", + "offset": 22111277, + "size": 68, + "sha256": "bd167f49fa61ddc0864c5cc4576337d3662d0076d9a91f9551e737e04db64c20" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/elements-rtl.min.css", + "offset": 22111345, + "size": 34, + "sha256": "1d2ff0af684c4ccaadef536848aebc8db2c24aa4d4ba3420ab2022b63fcd4140" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/elements.css", + "offset": 22111379, + "size": 68, + "sha256": "bd167f49fa61ddc0864c5cc4576337d3662d0076d9a91f9551e737e04db64c20" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/elements.min.css", + "offset": 22111447, + "size": 34, + "sha256": "1d2ff0af684c4ccaadef536848aebc8db2c24aa4d4ba3420ab2022b63fcd4140" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/reset-rtl.css", + "offset": 22111481, + "size": 3327, + "sha256": "eb50c026d33a6aa137a78e79fea4deab3737f7fa88b6fea3381d91c4824bfaff" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/reset-rtl.min.css", + "offset": 22114808, + "size": 2065, + "sha256": "219182f25559f12e073774fcd831ae3fcf31aed73d9134add7a0aef051d2c87a" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/reset.css", + "offset": 22116873, + "size": 3327, + "sha256": "eb50c026d33a6aa137a78e79fea4deab3737f7fa88b6fea3381d91c4824bfaff" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/reset.min.css", + "offset": 22120200, + "size": 2065, + "sha256": "219182f25559f12e073774fcd831ae3fcf31aed73d9134add7a0aef051d2c87a" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/style-rtl.css", + "offset": 22122265, + "size": 152469, + "sha256": "4b7afaa84d9eeaf52905b66bd7b67d7707009da8590077aa66a68b1710945029" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/style-rtl.min.css", + "offset": 22274734, + "size": 131248, + "sha256": "6029538db77e0230d9cea8ec414d957caeede035537c17126b51165c66bf19e1" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/style.css", + "offset": 22405982, + "size": 153282, + "sha256": "c7b754541e5b0a410f0ad6566516db8a08b897fd08117f158d53a7f18404a060" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/style.min.css", + "offset": 22559264, + "size": 131189, + "sha256": "dd1a8bd21728868dea879c3fcdb6e4e2d23ae377a86ee4866366cffd17900d9f" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/theme-rtl.css", + "offset": 22690453, + "size": 5084, + "sha256": "86ca879a8ef78c2e59ed62686ce060db3d9249da772c330de61975d7e56b79a4" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/theme-rtl.min.css", + "offset": 22695537, + "size": 2695, + "sha256": "07b3691c25e29505081a72082f636831b9289705c799b309f33f4891059eb1e6" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/theme.css", + "offset": 22698232, + "size": 5081, + "sha256": "0d6aad0e7327685f51169e790732c4602eabc8815ef84ff73191d651b40b9522" + }, + { + "path": "wordpress/wp-includes/css/dist/block-library/theme.min.css", + "offset": 22703313, + "size": 2692, + "sha256": "9ba73c8923d006f9e7f967f93ce54d8bdbd6084b01d6e8dde84e41085e25bb38" + }, + { + "path": "wordpress/wp-includes/css/dist/commands/style-rtl.css", + "offset": 22706005, + "size": 6360, + "sha256": "251f2895f561316c255309a324970279c244daf2936bc7ac3a5deddce06bcc6c" + }, + { + "path": "wordpress/wp-includes/css/dist/commands/style-rtl.min.css", + "offset": 22712365, + "size": 3973, + "sha256": "f651de988989e728822cb24e86b54543c4a9c5ef3e5dab47403bdfad73fc3954" + }, + { + "path": "wordpress/wp-includes/css/dist/commands/style.css", + "offset": 22716338, + "size": 6357, + "sha256": "881687c799b5140228e3cbab215b66cd0274e34e91e285c18e48db94acaddad0" + }, + { + "path": "wordpress/wp-includes/css/dist/commands/style.min.css", + "offset": 22722695, + "size": 3970, + "sha256": "b0a78cf68e21418df429e11dff26671110edcd5719c9ec2f6104351b883909e8" + }, + { + "path": "wordpress/wp-includes/css/dist/components/style-rtl.css", + "offset": 22726665, + "size": 117325, + "sha256": "15137fb1c6fc4cca8f5491c1fc9e0aa7b836699ba84ff8de829fde51c496f0fe" + }, + { + "path": "wordpress/wp-includes/css/dist/components/style-rtl.min.css", + "offset": 22843990, + "size": 97442, + "sha256": "08364097840c1534598f76355c4e6de658226e821bae4e55064088f41cf1a8fe" + }, + { + "path": "wordpress/wp-includes/css/dist/components/style.css", + "offset": 22941432, + "size": 117568, + "sha256": "e7fd1949b0bc808891e9ad540caa58cb61b8050718f6d1272d470ba679281880" + }, + { + "path": "wordpress/wp-includes/css/dist/components/style.min.css", + "offset": 23059000, + "size": 97425, + "sha256": "bc5a89aa0f9b629ff98b89e263cd55dffae103f4fec83b6c75a946ba49a761ca" + }, + { + "path": "wordpress/wp-includes/css/dist/customize-widgets/style-rtl.css", + "offset": 23156425, + "size": 8581, + "sha256": "25c636a59e2ca497a6dfb567a6ffe9aa4270a383cddb0cc108be3145d435b7fd" + }, + { + "path": "wordpress/wp-includes/css/dist/customize-widgets/style-rtl.min.css", + "offset": 23165006, + "size": 6100, + "sha256": "f90ad63f68e488c09dd2ed6cb9167105a17e78e7a55e08a2d2e9f6ebba588677" + }, + { + "path": "wordpress/wp-includes/css/dist/customize-widgets/style.css", + "offset": 23171106, + "size": 8581, + "sha256": "453b46b535b95241a846bcf57eb1146999c75c662b9952eb733e0042a8ad1f80" + }, + { + "path": "wordpress/wp-includes/css/dist/customize-widgets/style.min.css", + "offset": 23179687, + "size": 6100, + "sha256": "3e19b3d6aa237193590c57084e105ebe8d6c5761838c654d43f476f116058a04" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/classic-rtl.css", + "offset": 23185787, + "size": 3491, + "sha256": "e8edaede2d18e6df13872815345fb937a3f61fc57236cd76656fb3016fb0fe25" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/classic-rtl.min.css", + "offset": 23189278, + "size": 1476, + "sha256": "acaf81320bc0770db9914c0355de2e4060150e56c8506a5d527539f755e9a815" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/classic.css", + "offset": 23190754, + "size": 3581, + "sha256": "b9e5ea9e21a9ec7bf6a5ec409b7e771aeffc212c95303db86ab5785e2f07be2f" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/classic.min.css", + "offset": 23194335, + "size": 1474, + "sha256": "5eb580231a6ef287a8a83c9b98d81b3a55d670271a90392df2f637e3c38cfe2d" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/style-rtl.css", + "offset": 23195809, + "size": 15703, + "sha256": "ef6cdf575f6a49c0736045b39bd2aaf1bb96421b830c4e195399948aa4a55f20" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/style-rtl.min.css", + "offset": 23211512, + "size": 10574, + "sha256": "f4a6f3d64b9f2f54b5ccf2445fd34b351844dad7946f4f8a03267726bd9f6382" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/style.css", + "offset": 23222086, + "size": 15702, + "sha256": "617716501008f893bb49309859ffdccba1360ee4d72e80e3121c5ea7525c2fd4" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-post/style.min.css", + "offset": 23237788, + "size": 10573, + "sha256": "c4a3b467a9af0e6b81fa2722d976e1d6c26a00a766e35e731731bcb643507a3d" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-site/style-rtl.css", + "offset": 23248361, + "size": 153486, + "sha256": "ce2e501409f7d86238bc8d6192191100a1fa395f3f87fc4ccc35962cba7fb932" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-site/style-rtl.min.css", + "offset": 23401847, + "size": 125874, + "sha256": "2d157b0479e3fe94d61267e1062aa7911186ebe8d489e1404bd6f67f12b13221" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-site/style.css", + "offset": 23527721, + "size": 153528, + "sha256": "1ee7783958c45849823f1b293682da34a95131e0737f3e097e606d2b4dcbef2f" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-site/style.min.css", + "offset": 23681249, + "size": 125855, + "sha256": "cb022019cded5ec686bf36f698e91ecb317afe072590b330b8835fd7e5482189" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-widgets/style-rtl.css", + "offset": 23807104, + "size": 27728, + "sha256": "b5de3192dba0282b0b8ba6137de5523f0e6fa53a1a710300ef469dac2538da2a" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-widgets/style-rtl.min.css", + "offset": 23834832, + "size": 19901, + "sha256": "6186d1da064b641b3608b1d3822cff080ef09671c10c4a736a3435d9be5b49c3" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-widgets/style.css", + "offset": 23854733, + "size": 27721, + "sha256": "0f827afd1731e93659f7e2b23e4e0fd89c2eeb618db4f0cfdb1dccf6cd6b85b4" + }, + { + "path": "wordpress/wp-includes/css/dist/edit-widgets/style.min.css", + "offset": 23882454, + "size": 19895, + "sha256": "1e6ad7ca9f883a30cd7c74ca7f62e35d453d7727cf3ccee33c419d7ed8d2fc4f" + }, + { + "path": "wordpress/wp-includes/css/dist/editor/style-rtl.css", + "offset": 23902349, + "size": 177620, + "sha256": "8037bb16287ac3f23b9c1d5691ecae79e4f87c52bbd69b9519d996ffa170d3d1" + }, + { + "path": "wordpress/wp-includes/css/dist/editor/style-rtl.min.css", + "offset": 24079969, + "size": 146862, + "sha256": "f34edfc6224f06f4eb576870fffb5a1bd23cc1aff25a30f0bc96f553a1a42ad3" + }, + { + "path": "wordpress/wp-includes/css/dist/editor/style.css", + "offset": 24226831, + "size": 177745, + "sha256": "27335f13b0dc36ecb157503ba083f607af1f121f5f00ddd50ec9f365897443f6" + }, + { + "path": "wordpress/wp-includes/css/dist/editor/style.min.css", + "offset": 24404576, + "size": 146838, + "sha256": "0e1b257cab13c0b865b11168c8715a93652a217e878e5959b9751533a8d6d43e" + }, + { + "path": "wordpress/wp-includes/css/dist/format-library/style-rtl.css", + "offset": 24551414, + "size": 1653, + "sha256": "346f04732656b71efd46b2f022f1c2f21e6a4186faf185968b3aadf0a3524402" + }, + { + "path": "wordpress/wp-includes/css/dist/format-library/style-rtl.min.css", + "offset": 24553067, + "size": 761, + "sha256": "33f61308f615689afd65707f913e337dea3e7f3a19e0ff75477817f9de17b3ef" + }, + { + "path": "wordpress/wp-includes/css/dist/format-library/style.css", + "offset": 24553828, + "size": 1670, + "sha256": "3750c59ad3a6f1f6d4527187f97ddcd33e493fed3592695b3144c51ba347d210" + }, + { + "path": "wordpress/wp-includes/css/dist/format-library/style.min.css", + "offset": 24555498, + "size": 761, + "sha256": "33f61308f615689afd65707f913e337dea3e7f3a19e0ff75477817f9de17b3ef" + }, + { + "path": "wordpress/wp-includes/css/dist/list-reusable-blocks/style-rtl.css", + "offset": 24556259, + "size": 2386, + "sha256": "1bf543ff536ff7c115571c054e64e49608dc61d3bc8c2adcee58a098a9ad6b10" + }, + { + "path": "wordpress/wp-includes/css/dist/list-reusable-blocks/style-rtl.min.css", + "offset": 24558645, + "size": 563, + "sha256": "44bc6b17219133cc78ded3773dd46f0e4156922f1c25e3d766f9dc254271b285" + }, + { + "path": "wordpress/wp-includes/css/dist/list-reusable-blocks/style.css", + "offset": 24559208, + "size": 2388, + "sha256": "0ad8372107a4225d6191493ba3c7901e3516dab0e31b616c246ad559a5fbac1e" + }, + { + "path": "wordpress/wp-includes/css/dist/list-reusable-blocks/style.min.css", + "offset": 24561596, + "size": 565, + "sha256": "50fb62beef23b4d06c93f19b3425751946ace1df8628e501358c1bd64623b948" + }, + { + "path": "wordpress/wp-includes/css/dist/media-utils/style-rtl.css", + "offset": 24562161, + "size": 6091, + "sha256": "7adb7294b48ee7d3382c13b11340a8830de200492d10f534539c42a9173ddadc" + }, + { + "path": "wordpress/wp-includes/css/dist/media-utils/style-rtl.min.css", + "offset": 24568252, + "size": 2226, + "sha256": "9c24ac124c29d03e2b22471206ac268cdfa85f36a9396fa7b029e45272e6b40e" + }, + { + "path": "wordpress/wp-includes/css/dist/media-utils/style.css", + "offset": 24570478, + "size": 6089, + "sha256": "592f73b607df347c9e23fcf565c34bcc9902b2e1e9fa1d55ba0ad09b39622695" + }, + { + "path": "wordpress/wp-includes/css/dist/media-utils/style.min.css", + "offset": 24576567, + "size": 2224, + "sha256": "f57022ee9fa4d2a4576be753481b5e24987bd07454f05b8764809bec896e5bb5" + }, + { + "path": "wordpress/wp-includes/css/dist/nux/style-rtl.css", + "offset": 24578791, + "size": 4422, + "sha256": "0a943a8815a48b7b803eb016f5a7fc8444a453ad530ac5a317c2ca0404bcc16d" + }, + { + "path": "wordpress/wp-includes/css/dist/nux/style-rtl.min.css", + "offset": 24583213, + "size": 2316, + "sha256": "c6991c9380402fbd962f8d5d30e27fc800741b617a116699fc1daa9376df740c" + }, + { + "path": "wordpress/wp-includes/css/dist/nux/style.css", + "offset": 24585529, + "size": 4491, + "sha256": "18fd382c0a9b23137fae87f2f36cf4a762884f16ef448ab8d5e18f5dccf503e9" + }, + { + "path": "wordpress/wp-includes/css/dist/nux/style.min.css", + "offset": 24590020, + "size": 2313, + "sha256": "ff8fe10d1555b51943e0c87ea41f4c58ef348b95cfdcd5c20124518b2f62818d" + }, + { + "path": "wordpress/wp-includes/css/dist/patterns/style-rtl.css", + "offset": 24592333, + "size": 3416, + "sha256": "de2d49fe067331d74df66509bc7856bf74e0f18d66420144a9363c3d390e0089" + }, + { + "path": "wordpress/wp-includes/css/dist/patterns/style-rtl.min.css", + "offset": 24595749, + "size": 1460, + "sha256": "6b6f6e69111002810d0e3efe8e4fe867a809e0e33d2ec96775bdba315bb165bb" + }, + { + "path": "wordpress/wp-includes/css/dist/patterns/style.css", + "offset": 24597209, + "size": 3415, + "sha256": "5267e0f84580acb1ea27cf7a39a20c426799b26642f11bd7b68c4e4a2cad9b52" + }, + { + "path": "wordpress/wp-includes/css/dist/patterns/style.min.css", + "offset": 24600624, + "size": 1459, + "sha256": "5b301ad4d81a468e29413fc8ff9d496faf61d1f427cd44194f53c3b1f24a4cad" + }, + { + "path": "wordpress/wp-includes/css/dist/preferences/style-rtl.css", + "offset": 24602083, + "size": 2972, + "sha256": "c1258c85f53765df49eb3297cde8514a01e1f25c55a1529d35ba2ba38a372bd5" + }, + { + "path": "wordpress/wp-includes/css/dist/preferences/style-rtl.min.css", + "offset": 24605055, + "size": 1006, + "sha256": "92f9dad4d9ccce679affb5942bf6ddae53a4be5cf242501f4dbdcc0ddb2e8acb" + }, + { + "path": "wordpress/wp-includes/css/dist/preferences/style.css", + "offset": 24606061, + "size": 2969, + "sha256": "a70cca260c27d6010047b32bfb1d36da1c2873b1b06773e069c5d00369c2c4b4" + }, + { + "path": "wordpress/wp-includes/css/dist/preferences/style.min.css", + "offset": 24609030, + "size": 1003, + "sha256": "6d7f1873151523933c43ddd6324ccf685feaf07dafd5caae98f1d4d1eb153a2b" + }, + { + "path": "wordpress/wp-includes/css/dist/reusable-blocks/style-rtl.css", + "offset": 24610033, + "size": 2411, + "sha256": "5370b38fe9984a1fa0cda86fbbad828a20f700c19da937a600f3e5514f293297" + }, + { + "path": "wordpress/wp-includes/css/dist/reusable-blocks/style-rtl.min.css", + "offset": 24612444, + "size": 579, + "sha256": "2a0f3229165640926e3d5fa8da79f2767e36fedd34e29261ad24f3cae5535799" + }, + { + "path": "wordpress/wp-includes/css/dist/reusable-blocks/style.css", + "offset": 24613023, + "size": 2411, + "sha256": "5370b38fe9984a1fa0cda86fbbad828a20f700c19da937a600f3e5514f293297" + }, + { + "path": "wordpress/wp-includes/css/dist/reusable-blocks/style.min.css", + "offset": 24615434, + "size": 579, + "sha256": "2a0f3229165640926e3d5fa8da79f2767e36fedd34e29261ad24f3cae5535799" + }, + { + "path": "wordpress/wp-includes/css/dist/widgets/style-rtl.css", + "offset": 24616013, + "size": 7939, + "sha256": "60dcdb879c613ceace9c06ed02e16dc61dae3c72c33ff0c968eae15965d135cf" + }, + { + "path": "wordpress/wp-includes/css/dist/widgets/style-rtl.min.css", + "offset": 24623952, + "size": 5602, + "sha256": "ae7c037b499426e1ad7eb4b8ae5fce35f723bae61c8724d58d5118fb3edc5e39" + }, + { + "path": "wordpress/wp-includes/css/dist/widgets/style.css", + "offset": 24629554, + "size": 7936, + "sha256": "883823a9dd8c1d4e2197e161455a4257033d7d39c56722905d5e437df2784da1" + }, + { + "path": "wordpress/wp-includes/css/dist/widgets/style.min.css", + "offset": 24637490, + "size": 5599, + "sha256": "15691b34827e7c5cff4ce2d7818b636d8e5988bc3117dbb2cefc766d19e4aa1a" + }, + { + "path": "wordpress/wp-includes/css/editor-rtl.css", + "offset": 24643089, + "size": 34209, + "sha256": "4e2fd97e2d2a0c9f0f5669ee7febe7bb4c75ed13e2da263b1cc3450c353618e2" + }, + { + "path": "wordpress/wp-includes/css/editor-rtl.min.css", + "offset": 24677298, + "size": 28022, + "sha256": "93c1a8448aa27565c401f5b2b59510a46b214185f14623f2eb88064353269ed9" + }, + { + "path": "wordpress/wp-includes/css/editor.css", + "offset": 24705320, + "size": 34171, + "sha256": "da5db2990dfb83963d2463f2fa4e2b2074f79c2323a89fa1794239a2c15fb56f" + }, + { + "path": "wordpress/wp-includes/css/editor.min.css", + "offset": 24739491, + "size": 28019, + "sha256": "eb7b3c33dbe6cf12696561b6b31112172386336dcebbfee90ad91f56f15a02a2" + }, + { + "path": "wordpress/wp-includes/css/jquery-ui-dialog-rtl.css", + "offset": 24767510, + "size": 6002, + "sha256": "72ff1b033037fd6acc61cc5a234b2cb07871a4a10f6d2009e3af0cb3a52e031e" + }, + { + "path": "wordpress/wp-includes/css/jquery-ui-dialog-rtl.min.css", + "offset": 24773512, + "size": 4592, + "sha256": "12f74d1d16163f181f922d096e35ca13335eea10bfe0d3aff65c4aca5d8cf673" + }, + { + "path": "wordpress/wp-includes/css/jquery-ui-dialog.css", + "offset": 24778104, + "size": 5963, + "sha256": "a497d71823d4db4402e085efa6839d7d9a3f57f24198294fde6ef03bce17f2e8" + }, + { + "path": "wordpress/wp-includes/css/jquery-ui-dialog.min.css", + "offset": 24784067, + "size": 4588, + "sha256": "966d7526c6b0761698386c4a981daf7cbd1263b1ad732625878ec6a7f33428f1" + }, + { + "path": "wordpress/wp-includes/css/media-views-rtl.css", + "offset": 24788655, + "size": 61021, + "sha256": "3a0cf6cabe14f7ccb2f55a539aba33a39f78f622ce8a2a73fdcc799fb0101441" + }, + { + "path": "wordpress/wp-includes/css/media-views-rtl.min.css", + "offset": 24849676, + "size": 49464, + "sha256": "a1f02fe91797078249e63cbfa3924433af4211d07b1756da0a69efe5172f9c52" + }, + { + "path": "wordpress/wp-includes/css/media-views.css", + "offset": 24899140, + "size": 60978, + "sha256": "41eee0b496127aca8e01ba98bca8d4802bf2924f73620015403067d661b62608" + }, + { + "path": "wordpress/wp-includes/css/media-views.min.css", + "offset": 24960118, + "size": 49456, + "sha256": "4b7789642b1e7998cbb75869b15b8072f64f813fcea88174e636f4152cbc7d1a" + }, + { + "path": "wordpress/wp-includes/css/wp-auth-check-rtl.css", + "offset": 25009574, + "size": 2512, + "sha256": "af96cb2750de76625dee6b968bd1a70421be9c56515c35b425437d0ce71ef747" + }, + { + "path": "wordpress/wp-includes/css/wp-auth-check-rtl.min.css", + "offset": 25012086, + "size": 1887, + "sha256": "f5d283ff1cdcb186a11401d0119afecf4ad2312e7c0a21034dd402dafe8a8aec" + }, + { + "path": "wordpress/wp-includes/css/wp-auth-check.css", + "offset": 25013973, + "size": 2475, + "sha256": "516c0333b9b03ca5bc4ca15650a1721181c941bbedcbe492be2dab61b185c482" + }, + { + "path": "wordpress/wp-includes/css/wp-auth-check.min.css", + "offset": 25016448, + "size": 1885, + "sha256": "b3ec2a2792c64d95c234259265b88d93b5dd5ed07893d79058b7aa0abe186f52" + }, + { + "path": "wordpress/wp-includes/css/wp-block-template-skip-link-rtl.css", + "offset": 25018333, + "size": 653, + "sha256": "13d0e64443afe08c3026f6893464f8606ff9e456bf0f3e55f9114ca75431b300" + }, + { + "path": "wordpress/wp-includes/css/wp-block-template-skip-link-rtl.min.css", + "offset": 25018986, + "size": 468, + "sha256": "185d8c91f897175ddb39b560bff9ed9adf96154e09b593c017001ca1ad9cd222" + }, + { + "path": "wordpress/wp-includes/css/wp-block-template-skip-link.css", + "offset": 25019454, + "size": 617, + "sha256": "9c2b254bd4aadfa0feeabc41067df41c91cc9e7bcb515fb90d2d7d962171c0a3" + }, + { + "path": "wordpress/wp-includes/css/wp-block-template-skip-link.min.css", + "offset": 25020071, + "size": 467, + "sha256": "0a4e769501116063e4b64e134b206a74e492986aea803694656bf06bfda968be" + }, + { + "path": "wordpress/wp-includes/css/wp-embed-template-ie.css", + "offset": 25020538, + "size": 33, + "sha256": "e8a74bb82547cfd27307ccee76c071e49762a6574fc9ecdfa8f6159a51d2ca7e" + }, + { + "path": "wordpress/wp-includes/css/wp-embed-template-ie.min.css", + "offset": 25020571, + "size": 35, + "sha256": "c892b9c0828a176db25a693cb5709049f8a4eed8bdc7bf6595f0c7e02692892d" + }, + { + "path": "wordpress/wp-includes/css/wp-embed-template.css", + "offset": 25020606, + "size": 8109, + "sha256": "cad14a58ffb866ad4421d693acffce40c19c0e2af311ec854ffae1c7a4332ebd" + }, + { + "path": "wordpress/wp-includes/css/wp-embed-template.min.css", + "offset": 25028715, + "size": 7026, + "sha256": "8f20cee682918aa92243a442e2c359a9281f1c00cb2f93de64982ddaa8a8079b" + }, + { + "path": "wordpress/wp-includes/css/wp-empty-template-alert.css", + "offset": 25035741, + "size": 465, + "sha256": "d2748033d5a3cf1c2b92691cc3f47a551a0177f156dfa39f779d4ac677a2a973" + }, + { + "path": "wordpress/wp-includes/css/wp-empty-template-alert.min.css", + "offset": 25036206, + "size": 409, + "sha256": "df14209b083d3087915fc9f35958ca9e823bff5ca0196b0f8f729ba984c16e4b" + }, + { + "path": "wordpress/wp-includes/css/wp-pointer-rtl.css", + "offset": 25036615, + "size": 4084, + "sha256": "568624ac08a86943a0e41af611ac2b69fb03df508f51f7d7463d7bc67e19d1ff" + }, + { + "path": "wordpress/wp-includes/css/wp-pointer-rtl.min.css", + "offset": 25040699, + "size": 3257, + "sha256": "48e95c1c0fd73d7b5bf9c928ae3e849294fe6dc767a3034d4268499382dc262e" + }, + { + "path": "wordpress/wp-includes/css/wp-pointer.css", + "offset": 25043956, + "size": 4044, + "sha256": "a6305b39e0c0077e075f03f0ccb260a81cf758c6da5d32bb843f33e4917d0f6f" + }, + { + "path": "wordpress/wp-includes/css/wp-pointer.min.css", + "offset": 25048000, + "size": 3252, + "sha256": "3f28bf4f3c9d455b65565f475ba554c40c2f4808fd4a953b0bd9c7aaf0138719" + }, + { + "path": "wordpress/wp-includes/fonts/dashicons.eot", + "offset": 25051252, + "size": 56564, + "sha256": "c04a6545ea1a3860f6c8eb4b9eed8191d857dbf2716b812c816cae8e40bbea7a" + }, + { + "path": "wordpress/wp-includes/fonts/dashicons.svg", + "offset": 25107816, + "size": 124614, + "sha256": "b4e4808c8eab6627df55993496e682f2757976f65f7982c0edeb1b0055a146d5" + }, + { + "path": "wordpress/wp-includes/fonts/dashicons.ttf", + "offset": 25232430, + "size": 56392, + "sha256": "cacd900a2587c234c53e9d0108fc51ff6c61420c4f89a1731014b673a7b9a1a8" + }, + { + "path": "wordpress/wp-includes/fonts/dashicons.woff", + "offset": 25288822, + "size": 26124, + "sha256": "20fabfa803d080cde34001dc0371473d089cb619e20ea2e1d99c16832af9ed1f" + }, + { + "path": "wordpress/wp-includes/fonts/dashicons.woff2", + "offset": 25314946, + "size": 26132, + "sha256": "c225536c9abcc922dc1223b1457a307b0545d9fc8a3963d5ac56851c9c54c6b2" + }, + { + "path": "wordpress/wp-includes/images/admin-bar-sprite-2x.png", + "offset": 25341078, + "size": 3999, + "sha256": "be18f697934cd52ef97975493928528d74fa4f5ee51464519f7317d6018c1fb6" + }, + { + "path": "wordpress/wp-includes/images/admin-bar-sprite.png", + "offset": 25345077, + "size": 2467, + "sha256": "11f7b290e58fcd2831e02474390c3772c811882dc3c8045e6dadd4fcb98f9984" + }, + { + "path": "wordpress/wp-includes/images/arrow-pointer-blue-2x.png", + "offset": 25347544, + "size": 1666, + "sha256": "2cf236059fc92b35cf4dc7132b36ed9d5fc63c4a09087c7dfd405c4825030a0c" + }, + { + "path": "wordpress/wp-includes/images/arrow-pointer-blue.png", + "offset": 25349210, + "size": 793, + "sha256": "2562655f226029568c2bb08728c5c5bc6afc659f070292002dd0ee0a4f58c94d" + }, + { + "path": "wordpress/wp-includes/images/blank.gif", + "offset": 25350003, + "size": 43, + "sha256": "5c390bb4424108c7804361c9b49cb8fbcef0aab2a412f87c0a8a5f016e35a98f" + }, + { + "path": "wordpress/wp-includes/images/crystal/archive.png", + "offset": 25350046, + "size": 2454, + "sha256": "6078622343f458ddc8c3aed934c090cfd8cf1c85583c8734284e8ba5c1efb6e1" + }, + { + "path": "wordpress/wp-includes/images/crystal/audio.png", + "offset": 25352500, + "size": 2184, + "sha256": "38197c45a62b78e545083f2dc26796b1fc75c8db7cefe05cf84c1f63199955cc" + }, + { + "path": "wordpress/wp-includes/images/crystal/code.png", + "offset": 25354684, + "size": 1604, + "sha256": "447759c5326269bafe551458dbe0ae6c55b4481f0ffd522916797eee1281f48c" + }, + { + "path": "wordpress/wp-includes/images/crystal/default.png", + "offset": 25356288, + "size": 453, + "sha256": "524f0956403d109f95c6429bd181816336680ef8269439ee2aa73edd78013c54" + }, + { + "path": "wordpress/wp-includes/images/crystal/document.png", + "offset": 25356741, + "size": 2063, + "sha256": "bec52cf65efa3a363d630ff2fec452561b5f41015da7b1b361af460d2b068d1e" + }, + { + "path": "wordpress/wp-includes/images/crystal/interactive.png", + "offset": 25358804, + "size": 2217, + "sha256": "8e74b5e31b8839cede4492b4bb41fade133f860bc38cf6040a9a592f21494caf" + }, + { + "path": "wordpress/wp-includes/images/crystal/spreadsheet.png", + "offset": 25361021, + "size": 2408, + "sha256": "045bb9e2bf2ae73be6bceae869ee24d601591c18f05ca5d193b400e6bd013c60" + }, + { + "path": "wordpress/wp-includes/images/crystal/text.png", + "offset": 25363429, + "size": 670, + "sha256": "f95a41295fda7d4734e03092542e031fa38c48cfe8970c8b1c90bb0e2d837eb7" + }, + { + "path": "wordpress/wp-includes/images/crystal/video.png", + "offset": 25364099, + "size": 1339, + "sha256": "63855176c5055abd07a2a1c599d85864cef015565e514d22adae90e2985be674" + }, + { + "path": "wordpress/wp-includes/images/down_arrow-2x.gif", + "offset": 25365438, + "size": 84, + "sha256": "ce868ff20778f554f17ec1b5dc70ec2a3a3e57e6a4f8e31939df83a90c199198" + }, + { + "path": "wordpress/wp-includes/images/down_arrow.gif", + "offset": 25365522, + "size": 59, + "sha256": "52c607d99df8a06079d659c0e8a5a7f8984ee4148679c21f2032c06bfb6c9e38" + }, + { + "path": "wordpress/wp-includes/images/icon-library/accordion-heading.svg", + "offset": 25365581, + "size": 223, + "sha256": "dd31ae0ba70bb74f6aac37ed3cc2ec64a10d12fb43e420cdaf5c83182a870a50" + }, + { + "path": "wordpress/wp-includes/images/icon-library/accordion-item.svg", + "offset": 25365804, + "size": 429, + "sha256": "d84e37a946fec75ce1767f16380ee4704fb8c0282193462c68dccc50a8cec374" + }, + { + "path": "wordpress/wp-includes/images/icon-library/accordion.svg", + "offset": 25366233, + "size": 366, + "sha256": "d59681bad139890175b9ff16226ef18e5fab17728f0b073d4b4a7f9e44ca8f90" + }, + { + "path": "wordpress/wp-includes/images/icon-library/add-card.svg", + "offset": 25366599, + "size": 248, + "sha256": "773f3e12e9362329645f1822d272cf55bbd206d42cbf295dcba818cb04a710b7" + }, + { + "path": "wordpress/wp-includes/images/icon-library/add-submenu.svg", + "offset": 25366847, + "size": 245, + "sha256": "fc11d35cb00551480618ec5c405aaab5af7e40b91702343dd86c582fce94078b" + }, + { + "path": "wordpress/wp-includes/images/icon-library/add-template.svg", + "offset": 25367092, + "size": 498, + "sha256": "31cf4c7c5c23c75d2d8fe63fa5b338aa8d7c7b6ddaec4612193020da14fb0e9c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/align-center.svg", + "offset": 25367590, + "size": 142, + "sha256": "a6a0d994a41a9abcd06ad9d5a8eefbde0aa6888039204b945b9a6f507d0d7da8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/align-justify.svg", + "offset": 25367732, + "size": 146, + "sha256": "591fa7e2eec8ce2403540f644ff4ebe45b6bf05768f9d2901fd146f5e480cb38" + }, + { + "path": "wordpress/wp-includes/images/icon-library/align-left.svg", + "offset": 25367878, + "size": 135, + "sha256": "e06fc06e938bd374d4992633568223559e830ee3a4bd44bfeb427abfea5b362c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/align-none.svg", + "offset": 25368013, + "size": 136, + "sha256": "6eef5879b0d5449f57ec5e014e7bc2dd493d15dea68baeaf89a40671e9c76980" + }, + { + "path": "wordpress/wp-includes/images/icon-library/align-right.svg", + "offset": 25368149, + "size": 157, + "sha256": "db5bb54c0474543e4a9ddb6e04746f98532b37db59c8be95c2a2cd5488a323ab" + }, + { + "path": "wordpress/wp-includes/images/icon-library/archive.svg", + "offset": 25368306, + "size": 445, + "sha256": "217cf24078028298e04e9a0d9d6d174a50c30c67d2808b0cd08dd35c249918d4" + }, + { + "path": "wordpress/wp-includes/images/icon-library/arrow-down-left.svg", + "offset": 25368751, + "size": 129, + "sha256": "a723e6238bb9bf3317c600826b435e5b50b2de51cdaf6c518720966d26fd550d" + }, + { + "path": "wordpress/wp-includes/images/icon-library/arrow-down-right.svg", + "offset": 25368880, + "size": 126, + "sha256": "2f0c359ecab61f39fe6d82a210c820f8f43ba6cca3bd1bc168e60c4092def92f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/arrow-down.svg", + "offset": 25369006, + "size": 143, + "sha256": "65538b40543bb665ac3581c0697dc8dd8f0ba8ba88d7fdd1f90b4fc4f81bd294" + }, + { + "path": "wordpress/wp-includes/images/icon-library/arrow-left.svg", + "offset": 25369149, + "size": 138, + "sha256": "9cc470793eb3f846a3f2bb4028b78203356190781363681a71dd2a10f1781556" + }, + { + "path": "wordpress/wp-includes/images/icon-library/arrow-right.svg", + "offset": 25369287, + "size": 137, + "sha256": "fd37a26bbd42904b65e6fb78fc3e0a5b517b191bf53af08a2f84020b87033ff4" + }, + { + "path": "wordpress/wp-includes/images/icon-library/arrow-up-left.svg", + "offset": 25369424, + "size": 124, + "sha256": "b25c0241c9824f90889dffd693dbfb39bf7b848186d8180a00e48471cca1c825" + }, + { + "path": "wordpress/wp-includes/images/icon-library/arrow-up-right.svg", + "offset": 25369548, + "size": 128, + "sha256": "dcc7ef66beeea736b060ea0848b0c10351bb6548a58074e10be952cafb9599f9" + }, + { + "path": "wordpress/wp-includes/images/icon-library/arrow-up.svg", + "offset": 25369676, + "size": 134, + "sha256": "ee442bcb9f70434595ca0c07db613ebf5c405e93eea309a915ca75587cc73c24" + }, + { + "path": "wordpress/wp-includes/images/icon-library/aspect-ratio.svg", + "offset": 25369810, + "size": 298, + "sha256": "5db31ea925d5546775aa5ba9e16c3b5f1c906be571fba1100b1d2e97d9c2d6d8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/at-symbol.svg", + "offset": 25370108, + "size": 1030, + "sha256": "37db84bb54d2ffe427306ae513b3cd3ab790930af27497088ec6a99aa39fb8b2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/audio.svg", + "offset": 25371138, + "size": 277, + "sha256": "0b6450a45413659391eaf7df49f09db254d17fb0db9a6bf3d6a99c71f884a505" + }, + { + "path": "wordpress/wp-includes/images/icon-library/background.svg", + "offset": 25371415, + "size": 546, + "sha256": "bfd25ef1513a5994e0bf7468a828fe0a0994ec506835b36628e7ffc40e1ddbff" + }, + { + "path": "wordpress/wp-includes/images/icon-library/backup.svg", + "offset": 25371961, + "size": 278, + "sha256": "31767aa1eb942b6980cb6c06fa3c28b1a6db71843f85fca25041140e7aaf59d1" + }, + { + "path": "wordpress/wp-includes/images/icon-library/bell-unread.svg", + "offset": 25372239, + "size": 587, + "sha256": "e863240a722f8eddae2e935393a0d3bdb70db969177ccd41745da603f0b68321" + }, + { + "path": "wordpress/wp-includes/images/icon-library/bell.svg", + "offset": 25372826, + "size": 527, + "sha256": "e06a911314204ddfca7788affea481705cffc742681b5494a641c510ef4750e9" + }, + { + "path": "wordpress/wp-includes/images/icon-library/block-default.svg", + "offset": 25373353, + "size": 257, + "sha256": "751a83cfa76744dd146c0e154310252bdc010627fc56109a622c909689651f3e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/block-meta.svg", + "offset": 25373610, + "size": 481, + "sha256": "3c9804954caf107d82f31f85eba3e89cfe3e198b5a6f12a3dc4ec9fa4a5c1b31" + }, + { + "path": "wordpress/wp-includes/images/icon-library/block-table.svg", + "offset": 25374091, + "size": 323, + "sha256": "fe1561b023c1a6ffbdaddb3c81797a2be832568e4606e295bcc2e29f8015866e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/border.svg", + "offset": 25374414, + "size": 702, + "sha256": "01f8f3215ab364be922f977238703371638a1d15efd386c0d4d53661d614d088" + }, + { + "path": "wordpress/wp-includes/images/icon-library/box.svg", + "offset": 25375116, + "size": 363, + "sha256": "550cff7d96749c13f8d3aab53cc65d6e9c848fb1b990c65c07c6db19ec8a7af2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/breadcrumbs.svg", + "offset": 25375479, + "size": 147, + "sha256": "361d5b8080f70be8cbbee6c30f7a25f543663279ede6d3b64fec295dc7aced99" + }, + { + "path": "wordpress/wp-includes/images/icon-library/brush.svg", + "offset": 25375626, + "size": 413, + "sha256": "99e1df3f8b5ab3caa6e9d42e0fc01ca7305e94b11d5d4f4f707f1b661e7df8ca" + }, + { + "path": "wordpress/wp-includes/images/icon-library/bug.svg", + "offset": 25376039, + "size": 416, + "sha256": "3955b02f4647f8581c069b5cbf9a57570fcc7d608cca4a145d96bf47a167c376" + }, + { + "path": "wordpress/wp-includes/images/icon-library/button.svg", + "offset": 25376455, + "size": 260, + "sha256": "56f477e003d8802a511e3555e479d9d02b22f712f7765fd1416ab8c0138dda31" + }, + { + "path": "wordpress/wp-includes/images/icon-library/buttons.svg", + "offset": 25376715, + "size": 688, + "sha256": "146137b30290bb0ad21167567dec446f54fc17b956089bbb21420c47f1f43d5e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/calendar.svg", + "offset": 25377403, + "size": 299, + "sha256": "667f10231c4d2ff5ec4cd5df30be09075cf8d0c21b85ea44a5f9ae85b15d3ab5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/cancel-circle-filled.svg", + "offset": 25377702, + "size": 242, + "sha256": "0a1d338be1665ba35337a1d13dd6794e50b3bfb5c2e4ed15edb4200ef1d84593" + }, + { + "path": "wordpress/wp-includes/images/icon-library/caption.svg", + "offset": 25377944, + "size": 331, + "sha256": "348c93db2083f963d78af13af4d735f2f90cfdf179e6a013fe4d462bcbac5e1f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/capture-photo.svg", + "offset": 25378275, + "size": 663, + "sha256": "016ec954115b74ddf81a0ccec3095a7fb4ef6cac6a388d0263d0d0ff380ff20e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/capture-video.svg", + "offset": 25378938, + "size": 289, + "sha256": "330764e533b4c031ce316ab8c11c1346213fc0bb164cd8c6dcc762c80717cc38" + }, + { + "path": "wordpress/wp-includes/images/icon-library/cart.svg", + "offset": 25379227, + "size": 427, + "sha256": "2907682ce549d70e295df6e8d4904a62db1a96fc40bec7c787d33d3f9cf276c2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/category.svg", + "offset": 25379654, + "size": 688, + "sha256": "fd951e6ccc6315da9de57b12b33aee74a42c58d752e90cb2d42521286e8aba91" + }, + { + "path": "wordpress/wp-includes/images/icon-library/caution-filled.svg", + "offset": 25380342, + "size": 253, + "sha256": "98ed058f524791a7a6fda9b6a30cd76f3e8569184362bd107780981308f7b0df" + }, + { + "path": "wordpress/wp-includes/images/icon-library/caution.svg", + "offset": 25380595, + "size": 249, + "sha256": "227a9319ef40483dcd4356582c59041ef099975b2e2917a1fae879b8d957f61d" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chart-bar.svg", + "offset": 25380844, + "size": 183, + "sha256": "7550c7a245b41b6c62383cced002bef06a7ad6b658198536de86bbc152bfcb33" + }, + { + "path": "wordpress/wp-includes/images/icon-library/check.svg", + "offset": 25381027, + "size": 130, + "sha256": "edd95254cdd0af0388c5ffdbbc54e15c8014e99c5fc427a8e5b387136a4df3b8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-down-small.svg", + "offset": 25381157, + "size": 164, + "sha256": "797c2d20e32b45f65a8f2a060114bb91d137f8b504413fbfb7d74bd823666c3e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-down.svg", + "offset": 25381321, + "size": 135, + "sha256": "8d27e7e9b3c7d71ff118860beb054291a95b6e50f5d586241cf53c05e618ce06" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-left-small.svg", + "offset": 25381456, + "size": 128, + "sha256": "6d13d5fc4ce21022fe74648e59cd6a9d24f3412ee77e79e86848b11cbf05204a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-left.svg", + "offset": 25381584, + "size": 121, + "sha256": "4bde543a25695f839d193fdda408629977a1d2819b5221e4d2944a5467b79e96" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-right-small.svg", + "offset": 25381705, + "size": 196, + "sha256": "25209bc2829e71554dcf119eb7ccb9876a1f0299e1c3d8247f07ad29a3383325" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-right.svg", + "offset": 25381901, + "size": 121, + "sha256": "b10c6e86e4754e366ce1ef1224c4e19be4f80bc8425b4f065df2ae6bcb77cc54" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-up-down.svg", + "offset": 25382022, + "size": 177, + "sha256": "5db7d68dd2a5d286f818e5267edbdca8cd637b57d635c08e4c80b3442a2ad1a1" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-up-small.svg", + "offset": 25382199, + "size": 164, + "sha256": "1db61c8dd33cb582336d4475287d14fc80330823a5405de09c4c87ccf255dd6c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/chevron-up.svg", + "offset": 25382363, + "size": 134, + "sha256": "86ab5f36851815a4a3931450443de7987164852ff516cf4e1d2d011da36c15b9" + }, + { + "path": "wordpress/wp-includes/images/icon-library/classic.svg", + "offset": 25382497, + "size": 357, + "sha256": "6e7c54752ba42b23d6245c0fa5036a107ded4d72f22e1581cd20a1c35e3cb0c2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/close-small.svg", + "offset": 25382854, + "size": 226, + "sha256": "5be8bff61cfa6f852b148fee4858ac41ae0273e7f8be1d5603e4ad42920b4d48" + }, + { + "path": "wordpress/wp-includes/images/icon-library/close.svg", + "offset": 25383080, + "size": 210, + "sha256": "701d245cb36e4cb6d186f2c6f4d6c725aff217d13e7064461754438f3292ebf0" + }, + { + "path": "wordpress/wp-includes/images/icon-library/cloud-download.svg", + "offset": 25383290, + "size": 744, + "sha256": "f9e53ff0615df4310400f5356372364c4d735fcca10d0cba499973067ee4fce3" + }, + { + "path": "wordpress/wp-includes/images/icon-library/cloud-upload.svg", + "offset": 25384034, + "size": 649, + "sha256": "c71d7ae20e2f4b65236af58ed98f104710f95a942b7529c241908adbbefeeb7a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/cloud.svg", + "offset": 25384683, + "size": 384, + "sha256": "3ed77a0950d370531500789149e779ea3237b3c48e3b69f09e19f21036b37a58" + }, + { + "path": "wordpress/wp-includes/images/icon-library/code.svg", + "offset": 25385067, + "size": 268, + "sha256": "358983589012c1222e2b032e1d4dd2e8001bd16dd7bb73770a915106248eff99" + }, + { + "path": "wordpress/wp-includes/images/icon-library/cog.svg", + "offset": 25385335, + "size": 750, + "sha256": "273bce012e5f5396dc8bc6f9c1ffad7ddc069d6a0731f9343c6e6dfd67ff4e01" + }, + { + "path": "wordpress/wp-includes/images/icon-library/color.svg", + "offset": 25386085, + "size": 430, + "sha256": "05902d9f5afd831959f0b6009874d881cc7fa9a1bc7f8032c523323ec8759b3e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/column.svg", + "offset": 25386515, + "size": 257, + "sha256": "9d4ec4facbae86fa4fb413051c90318a797cd33817a60a1966a2a4f97db5dd86" + }, + { + "path": "wordpress/wp-includes/images/icon-library/columns.svg", + "offset": 25386772, + "size": 319, + "sha256": "a76e087245363a74ad5d88f30294f20b3b54381a5c171a3c09412ebe71dcf596" + }, + { + "path": "wordpress/wp-includes/images/icon-library/comment-author-avatar.svg", + "offset": 25387091, + "size": 407, + "sha256": "d84c917c003f7f9216b7d01e5ec246f4cd545cd67525f4ec1304003108bff777" + }, + { + "path": "wordpress/wp-includes/images/icon-library/comment-author-name.svg", + "offset": 25387498, + "size": 499, + "sha256": "d69619a4497e84c26b3579ae303c22333df38b8c9c4ae151508b6b958c4dd4bd" + }, + { + "path": "wordpress/wp-includes/images/icon-library/comment-content.svg", + "offset": 25387997, + "size": 472, + "sha256": "2ddb8dc5e3df7b1b479017c2169a43f9265edaaeea927d97aaa646161c3106c8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/comment-edit-link.svg", + "offset": 25388469, + "size": 305, + "sha256": "8264fac91b6b87508fd276a1e4661fe1d442f915a6326b2f910e957709f41d39" + }, + { + "path": "wordpress/wp-includes/images/icon-library/comment-reply-link.svg", + "offset": 25388774, + "size": 872, + "sha256": "1e2c2ef877a7a65dd570c30dd8a06204cea6c443f3a2cf03c6912dd899176264" + }, + { + "path": "wordpress/wp-includes/images/icon-library/comment.svg", + "offset": 25389646, + "size": 259, + "sha256": "4ed4aa22be549c364b9b6811d65f6f5a1f1e7fdf2580408824ad89439d7c7ab4" + }, + { + "path": "wordpress/wp-includes/images/icon-library/connection.svg", + "offset": 25389905, + "size": 659, + "sha256": "a06dfec910fc69d9da9b6e302c4fb23ca5c3a1986ccfea556b5f9f306e4d883b" + }, + { + "path": "wordpress/wp-includes/images/icon-library/contents.svg", + "offset": 25390564, + "size": 755, + "sha256": "e4ef2ab7677c312c7ff981ad8ed107dde1083a4584fa0b42b68be09179f9b617" + }, + { + "path": "wordpress/wp-includes/images/icon-library/copy-small.svg", + "offset": 25391319, + "size": 487, + "sha256": "ac2c47092fe667b408a397f27569e94ba046caa9c0575d7755075d2d364c3b6f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/copy.svg", + "offset": 25391806, + "size": 357, + "sha256": "f8f3ac67d6b79e1a13fe84073c94a7912287482dc3443a71041e3c0b9ccdcd05" + }, + { + "path": "wordpress/wp-includes/images/icon-library/corner-all.svg", + "offset": 25392163, + "size": 415, + "sha256": "3b47decea09d48d3dce1be52638aae9d710656b0e394bbdeca72335627d4c03d" + }, + { + "path": "wordpress/wp-includes/images/icon-library/corner-bottom-left.svg", + "offset": 25392578, + "size": 526, + "sha256": "1239ec2e8d285b22ff011e6cbbc0ca1fd17df7082c44a2e04a21858d71aa6a6d" + }, + { + "path": "wordpress/wp-includes/images/icon-library/corner-bottom-right.svg", + "offset": 25393104, + "size": 531, + "sha256": "09eb02e1eccddfcd4cc0f6d512128dc2b23373162420015497f8c7d611f3ed08" + }, + { + "path": "wordpress/wp-includes/images/icon-library/corner-top-left.svg", + "offset": 25393635, + "size": 527, + "sha256": "5c8ff538abe07ea70766b775ec03d9546ad8335ad3891a2ae428cfe4b13797be" + }, + { + "path": "wordpress/wp-includes/images/icon-library/corner-top-right.svg", + "offset": 25394162, + "size": 531, + "sha256": "8d6dbf89a7fa8d6c38816a57e165f7c46dae125b01d31cdd68f230529e01946e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/cover.svg", + "offset": 25394693, + "size": 293, + "sha256": "64bf896f51857c7c07dc0395888808ded47c7a4bbad689f9d2580852839ec187" + }, + { + "path": "wordpress/wp-includes/images/icon-library/create.svg", + "offset": 25394986, + "size": 133, + "sha256": "299ad3995409df450a0ea8ffc23f2afb028da8c8bffba332bb09fc222fd7bc6f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/crop.svg", + "offset": 25395119, + "size": 266, + "sha256": "f302923b9167aa97597746e89637388ee6c89810ca133eebf3c007bb990ea6ca" + }, + { + "path": "wordpress/wp-includes/images/icon-library/currency-dollar.svg", + "offset": 25395385, + "size": 740, + "sha256": "e25b857da6cf620d8ab32bfc916c315f4d40a9782bdd9cbc5891c0e547c4099a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/currency-euro.svg", + "offset": 25396125, + "size": 856, + "sha256": "322cc3ce15d05c8d5210532b902da538d009a8a7f3d6bd90070e4e6532ad4657" + }, + { + "path": "wordpress/wp-includes/images/icon-library/currency-pound.svg", + "offset": 25396981, + "size": 744, + "sha256": "cc0bc0b04168dd3f4a63fd625d1933bbc5ffd492d39f0ff8d97350fcec8e1018" + }, + { + "path": "wordpress/wp-includes/images/icon-library/custom-link.svg", + "offset": 25397725, + "size": 447, + "sha256": "533d580e0d6330ff9713c8e2c5a7a139209d0d7939b195edac2f604d84ab1211" + }, + { + "path": "wordpress/wp-includes/images/icon-library/custom-post-type.svg", + "offset": 25398172, + "size": 219, + "sha256": "d49b501417542ed0b0c6e7c7ef95fd3bfeabd94c516f8dc7deb69a1e9187d536" + }, + { + "path": "wordpress/wp-includes/images/icon-library/dashboard.svg", + "offset": 25398391, + "size": 365, + "sha256": "c1704f61f7c08ce0513804a43bf0ededdd8d59147b76ee440b443e59733916c5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/desktop.svg", + "offset": 25398756, + "size": 233, + "sha256": "569e9d8cfee2be81c96d842847db70ace6d069be51c00b71371f2190f66410fd" + }, + { + "path": "wordpress/wp-includes/images/icon-library/details.svg", + "offset": 25398989, + "size": 217, + "sha256": "55a528c7c993563dce6da6710989c35dadf6e7a9984cee7fc35278fcd0367afb" + }, + { + "path": "wordpress/wp-includes/images/icon-library/download.svg", + "offset": 25399206, + "size": 179, + "sha256": "e7517b43e0ae6b68595e1a7d1309d3c5c909bf3218eddd7d25cb5c309d9660d7" + }, + { + "path": "wordpress/wp-includes/images/icon-library/drafts.svg", + "offset": 25399385, + "size": 241, + "sha256": "ea3cf2676a72e28d69467815b38e274a330f6e7f2089e1fcee5ac2de00533fdd" + }, + { + "path": "wordpress/wp-includes/images/icon-library/drag-handle.svg", + "offset": 25399626, + "size": 169, + "sha256": "a62fa97454f8d20f68b9c76a6d04234be2d1da51b11dfa0a2c84c9dee633b7e4" + }, + { + "path": "wordpress/wp-includes/images/icon-library/drawer-left.svg", + "offset": 25399795, + "size": 301, + "sha256": "90bbc66e8207c9032e37ce720a27916a884b756db85edbb96a696b09b8a49903" + }, + { + "path": "wordpress/wp-includes/images/icon-library/drawer-right.svg", + "offset": 25400096, + "size": 302, + "sha256": "64ecbde0c481655f88b316c4343073fffc57baccc0ce8b00ef5d59ecdeea2f38" + }, + { + "path": "wordpress/wp-includes/images/icon-library/envelope.svg", + "offset": 25400398, + "size": 316, + "sha256": "09f761ce7ad34525112ce118d949864bbc9c69fd180756a38124e132fc2e28b5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/error.svg", + "offset": 25400714, + "size": 409, + "sha256": "40f8d93593faca995d42223fcb1e19dccc7fb3a089fe65081c0194af21712764" + }, + { + "path": "wordpress/wp-includes/images/icon-library/external.svg", + "offset": 25401123, + "size": 274, + "sha256": "48ee7249807c60cde20ef6c954b0afe7e6f2274741a2058a004fda07db34c9b5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/file.svg", + "offset": 25401397, + "size": 406, + "sha256": "8fbc389feed65befc53bdaa400934e4b9090a14b90b15d323e33a143e6b3bca6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/filter.svg", + "offset": 25401803, + "size": 125, + "sha256": "0079a667b44c8a389b921a3873c9a00417be6924df1a90ca696234a257142529" + }, + { + "path": "wordpress/wp-includes/images/icon-library/flip-horizontal.svg", + "offset": 25401928, + "size": 390, + "sha256": "74ba40ed451f6fb2aba5273de5e8acf43bea1d5c16b588c2a2fffc0934e6d2b3" + }, + { + "path": "wordpress/wp-includes/images/icon-library/flip-vertical.svg", + "offset": 25402318, + "size": 380, + "sha256": "8cb2d1bfc12954a74cd5a3ef0acc9ae270b6e1b67901ae135c2c4e696f26cd08" + }, + { + "path": "wordpress/wp-includes/images/icon-library/footer.svg", + "offset": 25402698, + "size": 237, + "sha256": "ea40af190a023c1bdcf8e826427ec4a64c713dfb48dc72a14b6f2b01a9739a6b" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-bold.svg", + "offset": 25402935, + "size": 368, + "sha256": "d1cf4d59e10b6dce979858a98ad495bddb2eb4dbfd7361f770c1193ea2899e88" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-capitalize.svg", + "offset": 25403303, + "size": 505, + "sha256": "af61fe3e42c95b10028fe9629b7cf0c3c2a7c9da7bd2c38fbeac17276f1fe62e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-indent-rtl.svg", + "offset": 25403808, + "size": 279, + "sha256": "9e82ff50bcb900916d53f5089b8b40f9738b04035a70b7c1073c10cca042b747" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-indent.svg", + "offset": 25404087, + "size": 156, + "sha256": "5d7e28500c98da4c02db25e7aa9b0d93e84387c7298b30b7c814d41f585519c1" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-italic.svg", + "offset": 25404243, + "size": 108, + "sha256": "eb52ed27306a26a055055bb1da2811502561c865ae867035caf54a72548ec7eb" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-list-bullets-rtl.svg", + "offset": 25404351, + "size": 222, + "sha256": "c65b8bdd77de293fc86ff27b68e4d8214d39da7078d07e9c302154a793b4e821" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-list-bullets.svg", + "offset": 25404573, + "size": 231, + "sha256": "3d3c0dc9660e6fd21184b0339a5fe49f21950fd62e2d3ce90d02d105f1b03a04" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-list-numbered-rtl.svg", + "offset": 25404804, + "size": 330, + "sha256": "6430c4221d04a2ed665a37df221ead6c7b0a4489f83d899beabe69c2a19aef01" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-list-numbered.svg", + "offset": 25405134, + "size": 330, + "sha256": "d58b3712afcf8ad6701dfa4c079821e7cbb3ca441d03990366703a02327bb467" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-lowercase.svg", + "offset": 25405464, + "size": 876, + "sha256": "72df8cbb6f3dcd6cedcc07b73b81c94f82a840c7450d979e484b15672a7a0065" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-ltr.svg", + "offset": 25406340, + "size": 199, + "sha256": "9d1f5c0453f6b10946bd734e5bc7377f1995d01ba95a0296546478293ac4a9af" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-outdent-rtl.svg", + "offset": 25406539, + "size": 279, + "sha256": "22f3f9a37296ab50ae1cf7547e78f0000226efbf260aaf452c4bf258c4e9c2c3" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-outdent.svg", + "offset": 25406818, + "size": 157, + "sha256": "9b768f84f02ec08e6424906f0fe37eb639ab8b728a1985be2b8a4a42fe9426f3" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-rtl.svg", + "offset": 25406975, + "size": 199, + "sha256": "07d7e80f57e3e1e3542cfaee8401717051c0d1e1bad266e20aa448ce4694bf7b" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-strikethrough.svg", + "offset": 25407174, + "size": 485, + "sha256": "2489b220284e0c3eafa665ce614c12a112e4a79784f7a61d1b958faf208d31bb" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-underline.svg", + "offset": 25407659, + "size": 273, + "sha256": "754306d7e9b9452f4ae33ea0e624bbc5359dd7690a0e400de6db2b457a808193" + }, + { + "path": "wordpress/wp-includes/images/icon-library/format-uppercase.svg", + "offset": 25407932, + "size": 484, + "sha256": "f62c019bb3baeccaec2ae1e5d701bc0e714b78466632ae0315afd4deb9d123c2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/full-height.svg", + "offset": 25408416, + "size": 238, + "sha256": "82b25b48cb5d2d033277b4283fe355cc9f0ec6e2d06a28aa3df951b3b88da6f5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/fullscreen.svg", + "offset": 25408654, + "size": 297, + "sha256": "da855d8f83f4706b30eb08f5dd4fd4bb5a52fefbe207ebd31a0178cf9574249d" + }, + { + "path": "wordpress/wp-includes/images/icon-library/funnel.svg", + "offset": 25408951, + "size": 143, + "sha256": "0009cd091de697b32fd3f4ef44af2d16872a4ae932fcc48c1b6295c847c67c80" + }, + { + "path": "wordpress/wp-includes/images/icon-library/gallery.svg", + "offset": 25409094, + "size": 649, + "sha256": "e7ed4504eeeeb6439714370eb35f7259ec48b852dd0828a2282965378f21bac5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/gift.svg", + "offset": 25409743, + "size": 895, + "sha256": "cc723ab720855e9eab3538e75303989b9bf87e85da7e5c10d2e033a9d73ffd6c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/globe.svg", + "offset": 25410638, + "size": 816, + "sha256": "73a995b121fbc81f75660f30867d69ad126774218bdb260c027d29ac79e650b5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/grid.svg", + "offset": 25411454, + "size": 387, + "sha256": "e8036500d136de5a29e8acc03112659985d76381b186fe01a464f36361fbdb75" + }, + { + "path": "wordpress/wp-includes/images/icon-library/group.svg", + "offset": 25411841, + "size": 421, + "sha256": "c7f2c1c33c3f086f5f2b4af1a342ad4fbb665624629385bbd846f24b605966fd" + }, + { + "path": "wordpress/wp-includes/images/icon-library/handle.svg", + "offset": 25412262, + "size": 119, + "sha256": "817598c42419bd4b23a2cce073dbfc0527952ceecf28c346f67690e9465cd49c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/header.svg", + "offset": 25412381, + "size": 220, + "sha256": "db3fd39ed6e6067a2bd3b0c6ac80e563efa6ebe7f6faa5f9d276cec73e9bd33d" + }, + { + "path": "wordpress/wp-includes/images/icon-library/heading-level-1.svg", + "offset": 25412601, + "size": 160, + "sha256": "cb73c5cc781fc9e0e803e18362a3e12a299e5c4aca4af5929a6f729d05ee81bf" + }, + { + "path": "wordpress/wp-includes/images/icon-library/heading-level-2.svg", + "offset": 25412761, + "size": 478, + "sha256": "49852436971bbffa777ff33d1780a1656612d3e0c31bb830fcd6b49fc240431a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/heading-level-3.svg", + "offset": 25413239, + "size": 634, + "sha256": "c500a36c46b15dcb3e32977a36519113c582eb0e57d32317afcb66ac83e8789a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/heading-level-4.svg", + "offset": 25413873, + "size": 169, + "sha256": "2776d96f557d4d42725bc774bf134a415717a1aa49e2a4109f00fc16fcdda5c5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/heading-level-5.svg", + "offset": 25414042, + "size": 488, + "sha256": "3a791cb54e8f309d38474ee57b91f0565a574d97a56a637d79856ba500a0cba2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/heading-level-6.svg", + "offset": 25414530, + "size": 717, + "sha256": "162337fede96560ac0f956802d7d147ce0e398f4d81b7c8126990b49e97cf571" + }, + { + "path": "wordpress/wp-includes/images/icon-library/heading.svg", + "offset": 25415247, + "size": 122, + "sha256": "27f58eb98e39393c9482b0dc3af6295bed216a145509cf330c9d175b3436b4a1" + }, + { + "path": "wordpress/wp-includes/images/icon-library/help-filled.svg", + "offset": 25415369, + "size": 458, + "sha256": "e5ef47213dc23ac300b70dee2ff61fa23142b476251b206dfc32dcca9e9e5dbb" + }, + { + "path": "wordpress/wp-includes/images/icon-library/help.svg", + "offset": 25415827, + "size": 343, + "sha256": "69994e9bd3d15577737f957c2a3af244d57cfb4a3d345371d0fb3f8316d847d6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/home-button.svg", + "offset": 25416170, + "size": 354, + "sha256": "1f1a58d6fe12c289345f6cd333a27b6b73032f78afb618d9d8d1fd158adb32e8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/home.svg", + "offset": 25416524, + "size": 160, + "sha256": "38e537cd536f6bf63611483addf152bb5ecc74899573aa436381f35697e74c6f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/html.svg", + "offset": 25416684, + "size": 274, + "sha256": "18abde3aa0d40315080546fb1274a483478f9c2e9ad5f2cabd70768ec3c0d6f6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/image.svg", + "offset": 25416958, + "size": 360, + "sha256": "b47a9e25ae39f72ee30ed9137d638bad10f196648542c66435b527a29b72c926" + }, + { + "path": "wordpress/wp-includes/images/icon-library/inbox.svg", + "offset": 25417318, + "size": 334, + "sha256": "47797027e9e14f33de838ffedb7e6560275aba2a6e40005732cde75c973355ab" + }, + { + "path": "wordpress/wp-includes/images/icon-library/info.svg", + "offset": 25417652, + "size": 246, + "sha256": "336ec2ba204b46a915b22b0f684d05b693346106b9d2d1cb87e94b0befd4d860" + }, + { + "path": "wordpress/wp-includes/images/icon-library/insert-after.svg", + "offset": 25417898, + "size": 236, + "sha256": "cb0aa643cfee9430707449ed10a91d113aae47a00685492825c58f1bc5204a17" + }, + { + "path": "wordpress/wp-includes/images/icon-library/insert-before.svg", + "offset": 25418134, + "size": 236, + "sha256": "b85327350cf7c684d6452c9f66aa01ef1f3b4b60c90960080a93798dc93297a2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/institution.svg", + "offset": 25418370, + "size": 362, + "sha256": "4688f651a36a474d148654a48f8f5680669749b859bd3517b1399f7767630082" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-bottom.svg", + "offset": 25418732, + "size": 119, + "sha256": "72bbbd6301cacdaa5a064ff90afad9da1b49d9f11af6e4e31e0d2f00a9f8abee" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-center-vertical.svg", + "offset": 25418851, + "size": 119, + "sha256": "4396b6344157d2946043d9dee2245c88fc4b595e73b4691214f60d9a8c54763c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-center.svg", + "offset": 25418970, + "size": 121, + "sha256": "f376015144e1003c19be6631681d7c4033783181155c059d7a2b7f8809759bff" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-left.svg", + "offset": 25419091, + "size": 114, + "sha256": "5b3cfadabcbdb90308952aa0b21aeb167ba87070366aceceece53f4934a6a823" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-right.svg", + "offset": 25419205, + "size": 119, + "sha256": "e5c4df6a0b6bb2051e9c43346554d06a01141c1e624e3e208f1f2fb5783f4a6e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-space-between-vertical.svg", + "offset": 25419324, + "size": 149, + "sha256": "508828a7c759aebad2bcb04c07fac44febab84463074b335613b16157dcedae6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-space-between.svg", + "offset": 25419473, + "size": 135, + "sha256": "22e71c03aa44c0af7b8dfe34e54f86b79afd142288f0f68378942ccebc62b3d9" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-stretch-vertical.svg", + "offset": 25419608, + "size": 173, + "sha256": "b303cbde311799ee6ee9855baf7523ce746d5b0d27c57fec5cbb90589670540c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-stretch.svg", + "offset": 25419781, + "size": 141, + "sha256": "0fa9d9f1f91b9af8c779e943f666d7a7018ed391a9e3e50c2e5f6f7326bf2997" + }, + { + "path": "wordpress/wp-includes/images/icon-library/justify-top.svg", + "offset": 25419922, + "size": 114, + "sha256": "67db21f36ead7d2f81665deefd753e680d780d53a2c2494fa4f85e66adf0e771" + }, + { + "path": "wordpress/wp-includes/images/icon-library/key.svg", + "offset": 25420036, + "size": 256, + "sha256": "c933688cead7e35e4ca8cf79461a19ebda9d3b1ac4bdb3c53d04a60b2c77999a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/keyboard-close.svg", + "offset": 25420292, + "size": 589, + "sha256": "1eb1aba90f45a7f3535567976536265d465d57ec9fc6398fb6da2121c3eeda14" + }, + { + "path": "wordpress/wp-includes/images/icon-library/keyboard-return.svg", + "offset": 25420881, + "size": 525, + "sha256": "b664e8bcff891f1cb8d1ae682361fc051ff2b693446846cd2736755e879e2bd6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/keyboard.svg", + "offset": 25421406, + "size": 404, + "sha256": "38b7d49b5a029556cc26e198dece9b8441fe6860d1b2cb279fad7d10e63b3ec8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/language.svg", + "offset": 25421810, + "size": 416, + "sha256": "8eaaade3600e02834115eb5d60e75d22554b0922736e4aac69254aee83b3e418" + }, + { + "path": "wordpress/wp-includes/images/icon-library/layout.svg", + "offset": 25422226, + "size": 263, + "sha256": "06b9c15571e75a34501fa366dfcba20bea07fdb5742b330a99270f3e7f2273bf" + }, + { + "path": "wordpress/wp-includes/images/icon-library/level-up.svg", + "offset": 25422489, + "size": 361, + "sha256": "41c63376ccb49ab832e239f41413ec43d078d4b5d43aca1cba9e78d2d99004cd" + }, + { + "path": "wordpress/wp-includes/images/icon-library/lifesaver.svg", + "offset": 25422850, + "size": 732, + "sha256": "0301ee6f32b7003954b87266521421664e206c6d9b731228548acbcd6ad41aba" + }, + { + "path": "wordpress/wp-includes/images/icon-library/line-dashed.svg", + "offset": 25423582, + "size": 187, + "sha256": "11f6fe144968ce40cc4eebd04a13309eb8d5f133028babe0b6e95520b9ba955c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/line-dotted.svg", + "offset": 25423769, + "size": 251, + "sha256": "1180693def9dd8ab8e873b8e22ac579f31a3a917cb31322e430c1c31e7816c99" + }, + { + "path": "wordpress/wp-includes/images/icon-library/line-solid.svg", + "offset": 25424020, + "size": 101, + "sha256": "ea69b24a1d6ec6f7e4f42e21e8bcc231d0a9feabd816378db3f2da0e7fa988d9" + }, + { + "path": "wordpress/wp-includes/images/icon-library/link-off.svg", + "offset": 25424121, + "size": 348, + "sha256": "045ddb96ceee75bcf04201ea6039222420069c1d14498226e391d7e132e48806" + }, + { + "path": "wordpress/wp-includes/images/icon-library/link.svg", + "offset": 25424469, + "size": 275, + "sha256": "c204c4e7acf37d0473b2e0dc52ba6be2f9168854023d3c200e1ac2e3e345bc5c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/list-item.svg", + "offset": 25424744, + "size": 151, + "sha256": "900306ec7192e795dd8c5203c501571bd5cd88485fded95bd642728e341b79e6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/list-view.svg", + "offset": 25424895, + "size": 143, + "sha256": "d3e2f5e297df8ed4769ccd3157fdd3174bfd8db3a975d00908f9e406c1b38d85" + }, + { + "path": "wordpress/wp-includes/images/icon-library/list.svg", + "offset": 25425038, + "size": 185, + "sha256": "6ccb732aa7a2c1ed5ff8b9dd2514355473ea08fb1b8c5c07b1299b7ca65e6781" + }, + { + "path": "wordpress/wp-includes/images/icon-library/lock-outline.svg", + "offset": 25425223, + "size": 284, + "sha256": "1e2483d42050882e3dbbc13bef4327437e3a350c18a679764c4ba5084b28c6d7" + }, + { + "path": "wordpress/wp-includes/images/icon-library/lock-small.svg", + "offset": 25425507, + "size": 289, + "sha256": "218c2bee140b078e2edc19a70d78f9ad135f078341e0a52778433d1e85f18d7f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/lock.svg", + "offset": 25425796, + "size": 259, + "sha256": "55a26baf501109439dd1c5cd3ea9f9bfb78ecfad316a0fd5842e6985169c69a5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/login.svg", + "offset": 25426055, + "size": 317, + "sha256": "299319df74f0b764df884bd833328a4d4d418db44c85421d5fdec07e78982d10" + }, + { + "path": "wordpress/wp-includes/images/icon-library/loop.svg", + "offset": 25426372, + "size": 985, + "sha256": "85ea59d977faf44e3d8502f4d54c99bcc03411e5b93e4105cd5bfdaa67b30667" + }, + { + "path": "wordpress/wp-includes/images/icon-library/map-marker.svg", + "offset": 25427357, + "size": 487, + "sha256": "cbc8b2cf62738b2e5f796c591f3cc2ac85f11576d52bb4b93ba97c6437a522ba" + }, + { + "path": "wordpress/wp-includes/images/icon-library/math.svg", + "offset": 25427844, + "size": 321, + "sha256": "fa4fcda67ccb7d63b9927738152da1871533a9560c24538a6026b3a0643f1544" + }, + { + "path": "wordpress/wp-includes/images/icon-library/media-and-text.svg", + "offset": 25428165, + "size": 155, + "sha256": "2933f60c864b61466b5b77fd4b7f0fc53701ac9358d5fab68cb734ed543393ea" + }, + { + "path": "wordpress/wp-includes/images/icon-library/media.svg", + "offset": 25428320, + "size": 640, + "sha256": "20d5ba0c1f7c875b1b8d8a95bac3a53d195431047dcf445ae48ac746576f0501" + }, + { + "path": "wordpress/wp-includes/images/icon-library/megaphone.svg", + "offset": 25428960, + "size": 349, + "sha256": "055f0796de62cae766bbc951cac025b0819f0c7cf8d82f1fb1ee3deb90a80247" + }, + { + "path": "wordpress/wp-includes/images/icon-library/menu.svg", + "offset": 25429309, + "size": 139, + "sha256": "b6c37a1512e49d595ab84b7ec0a374fdadf3d9da7a0f3af976eb5fa4c916f39b" + }, + { + "path": "wordpress/wp-includes/images/icon-library/mobile.svg", + "offset": 25429448, + "size": 257, + "sha256": "fd3d5932d9e7068e00c95c67428b32ef637c1bb0ac363286cb97c59c00fd21ae" + }, + { + "path": "wordpress/wp-includes/images/icon-library/more-horizontal.svg", + "offset": 25429705, + "size": 131, + "sha256": "2a7c6551bf59b7da34f38344461e59fed3a79194bae4debf3b15585ca0d23c1f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/more-vertical.svg", + "offset": 25429836, + "size": 129, + "sha256": "b59af435f59212052639450b3e6aa95b25f07d9846b74ac77e3c2a0cb5d611c2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/more.svg", + "offset": 25429965, + "size": 154, + "sha256": "957351915e3aabd598a6564167d2b4d04e980a183c5a920380c915e4830b21fb" + }, + { + "path": "wordpress/wp-includes/images/icon-library/move-to.svg", + "offset": 25430119, + "size": 870, + "sha256": "74a660a6cc37cbe7b039b638653c83f0f35529146168f8e276f19c74e271daf7" + }, + { + "path": "wordpress/wp-includes/images/icon-library/navigation-overlay.svg", + "offset": 25430989, + "size": 319, + "sha256": "a7dd777e295eb753b1be0e0b624bb1c1684d734ba4a0801705c68ba66c72e003" + }, + { + "path": "wordpress/wp-includes/images/icon-library/navigation.svg", + "offset": 25431308, + "size": 245, + "sha256": "fc4c8fc92c540325e2e7f37781dab8ad279a2a91232e1aeb759afd17b64e8d5c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/next.svg", + "offset": 25431553, + "size": 168, + "sha256": "f7d32fae242fb1564407070c0d6a0d00858ac7568683cb0ca8983118a7d17864" + }, + { + "path": "wordpress/wp-includes/images/icon-library/not-allowed.svg", + "offset": 25431721, + "size": 282, + "sha256": "0c7bf4016e99bc24959ed64b9f53f968d36660be1ec1d60a4b030a8b85eb3d68" + }, + { + "path": "wordpress/wp-includes/images/icon-library/not-found.svg", + "offset": 25432003, + "size": 822, + "sha256": "f175455a07dc42bc7e21f4a7ad6f227abe4a3dfe1c8ce716c1bc570b7805ce6f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/offline.svg", + "offset": 25432825, + "size": 746, + "sha256": "3591f761a09b791e8f6e7eb5df49fa2b5cce8bdc7afdb3cc3b4f37c71dff9302" + }, + { + "path": "wordpress/wp-includes/images/icon-library/overlay-text.svg", + "offset": 25433571, + "size": 289, + "sha256": "986a66553320e9e2375df7be93b10cd5212fe3399cd0a57b535386371fb88797" + }, + { + "path": "wordpress/wp-includes/images/icon-library/page-break.svg", + "offset": 25433860, + "size": 285, + "sha256": "58119402d555243bcfb9baecdbe09eb38a9620e9fa1b2fe2bbcb1013698f19d0" + }, + { + "path": "wordpress/wp-includes/images/icon-library/page.svg", + "offset": 25434145, + "size": 314, + "sha256": "97843d3695e3de3fa4c49d4fd2c2d254a1034a6f58b12a7ea2205a3541f294bb" + }, + { + "path": "wordpress/wp-includes/images/icon-library/pages.svg", + "offset": 25434459, + "size": 401, + "sha256": "fd34cd0f5ec68863dd37eca587a889f8b963c09295ab4635bc248abe2bd547ee" + }, + { + "path": "wordpress/wp-includes/images/icon-library/paragraph.svg", + "offset": 25434860, + "size": 213, + "sha256": "3b664db5356978aa7ac1c1afdf1934fb977ab9b7abb0875e1a3bfa76af4e30c0" + }, + { + "path": "wordpress/wp-includes/images/icon-library/payment.svg", + "offset": 25435073, + "size": 226, + "sha256": "b571a8f8ac98cc854fa02ca3fbe465beb935ef569221150136332e856a792184" + }, + { + "path": "wordpress/wp-includes/images/icon-library/pencil.svg", + "offset": 25435299, + "size": 135, + "sha256": "0ec02048413be2951b54472719d62f3c4b424b8df6cd1939710c0307ded7438c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/pending.svg", + "offset": 25435434, + "size": 243, + "sha256": "b86d764b22acff5d6e27554043fcaec986e9941ae1fb589d243d2ed93b82a1f7" + }, + { + "path": "wordpress/wp-includes/images/icon-library/people.svg", + "offset": 25435677, + "size": 452, + "sha256": "8eac4fa97c032dbed001b4349703fb35e90411d386abf08ef7f721ee922d8bc7" + }, + { + "path": "wordpress/wp-includes/images/icon-library/percent.svg", + "offset": 25436129, + "size": 341, + "sha256": "80dd87a35a6965bd0bf068a73bb9c759bf039f7f0dd69bc2bc7cb5c282b6bd8c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/pin-small.svg", + "offset": 25436470, + "size": 268, + "sha256": "b1e3e3ad3bb13cfb8eedd7f4f74e7ff49a6ed13dfd7af5998c9642343097a0e5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/pin.svg", + "offset": 25436738, + "size": 343, + "sha256": "8c408096201e7d45c1053fad7adc68b874ddaa3e213570fd8610d5c9cc025aee" + }, + { + "path": "wordpress/wp-includes/images/icon-library/plugins.svg", + "offset": 25437081, + "size": 217, + "sha256": "39b99a8af60457bf9af08b9c179b4094f0d2e47b4c62524d9314302cff0d9cdf" + }, + { + "path": "wordpress/wp-includes/images/icon-library/plus-circle-filled.svg", + "offset": 25437298, + "size": 183, + "sha256": "cdae9159d6aee47cd1aa32009e8b734852a9a1cec5b7d187ba1b97b88fd22c76" + }, + { + "path": "wordpress/wp-includes/images/icon-library/plus-circle.svg", + "offset": 25437481, + "size": 299, + "sha256": "829a2a88c20aa87862bd21e84ee49d32b0cf44dc57abd357f565f2e8777e0d55" + }, + { + "path": "wordpress/wp-includes/images/icon-library/plus.svg", + "offset": 25437780, + "size": 138, + "sha256": "ab0d42d6d1f62ce108605a84df818dd391d579af06edc38b467a0906c2ed4759" + }, + { + "path": "wordpress/wp-includes/images/icon-library/position-center.svg", + "offset": 25437918, + "size": 136, + "sha256": "fb8706a489ef78529e8d92e7aea2534bcb0015a1fac8ff9fbc9f8c4c5e84f6fe" + }, + { + "path": "wordpress/wp-includes/images/icon-library/position-left.svg", + "offset": 25438054, + "size": 133, + "sha256": "c90939e5d3b5e0c995e8c03a360978d8e15d61588acc81ef9d0f4429d90455ce" + }, + { + "path": "wordpress/wp-includes/images/icon-library/position-right.svg", + "offset": 25438187, + "size": 136, + "sha256": "f3aeddb9723213bf376a2933555bdf6a1f2f05ab614a34ab43b3d9f682953ad2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-author.svg", + "offset": 25438323, + "size": 357, + "sha256": "e678c6fe1b60f5131860a1a0c39cb5bea1e94a2c95b1b95ec924b6ae5aa71109" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-categories.svg", + "offset": 25438680, + "size": 463, + "sha256": "62fa291360f15f86aafec853ad4e8abe1eb3369b6da7de23649f8159dae89179" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-comments-count.svg", + "offset": 25439143, + "size": 468, + "sha256": "6faffc9567d41037aa8da41db4dc660335588c30caa62f4939a4cfb58d35f87e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-comments-form.svg", + "offset": 25439611, + "size": 256, + "sha256": "690291a5dc81e835f5562b8d9b11ac998d778a95fa25d75abb9ab4b92abd5765" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-comments.svg", + "offset": 25439867, + "size": 374, + "sha256": "667f714c37caf124ec351b4996bb320695e84139d1dccc1f111ddafcc82076a2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-content.svg", + "offset": 25440241, + "size": 156, + "sha256": "72e68db831e1dfcb0a05554aa5cdc7a4ffc0bf2e9cd113a1cf2a9fc86d06a197" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-date.svg", + "offset": 25440397, + "size": 605, + "sha256": "c32f0abee90ff2cf0f732de0e3ddd828c0b35c911e2c69870270ed11928e0081" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-excerpt.svg", + "offset": 25441002, + "size": 299, + "sha256": "b6a124ef18e7e304ab27d99ffd43503440d18561336e0a3e98c0eb6c98e86da5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-featured-image.svg", + "offset": 25441301, + "size": 334, + "sha256": "af4a999ccbb793cf5232f2eaf49b164f151535e692e175f7e848612bf1b39953" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-list.svg", + "offset": 25441635, + "size": 315, + "sha256": "b731d5f630e873e68fae9764f5bd5e2739aac49b2d428c7e0f01bb396663f28a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post-terms.svg", + "offset": 25441950, + "size": 347, + "sha256": "89468f865e3ded6fca1e067d619ea4f2eb4602d6eecbf97f044356639418003a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/post.svg", + "offset": 25442297, + "size": 275, + "sha256": "da4e91c9e2dad8c085a8053934b4f01483d33399351a14a5138dc6026ab549ab" + }, + { + "path": "wordpress/wp-includes/images/icon-library/preformatted.svg", + "offset": 25442572, + "size": 310, + "sha256": "8c589c58ff9f20a9c57b4cc4da32a64397886e241867aee9bb61bce14d1a4fba" + }, + { + "path": "wordpress/wp-includes/images/icon-library/previous.svg", + "offset": 25442882, + "size": 168, + "sha256": "e71af90ad6c540b9376f9437a2677fc8145e77d90ff72301723de37766170a32" + }, + { + "path": "wordpress/wp-includes/images/icon-library/published.svg", + "offset": 25443050, + "size": 277, + "sha256": "54c007f657bfc9e8667c6631cc57a8b944b7d99be6637ab268636961bb5dc637" + }, + { + "path": "wordpress/wp-includes/images/icon-library/pull-left.svg", + "offset": 25443327, + "size": 134, + "sha256": "cfe828e362b1a4ee9e05447146fe98ad8267a192e5039475f6661512ba380440" + }, + { + "path": "wordpress/wp-includes/images/icon-library/pull-right.svg", + "offset": 25443461, + "size": 134, + "sha256": "ea2c466cfadf4791e57f73af2f6bcd1a007e24c2814bb2b2112a55478fbdf1c6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/pullquote.svg", + "offset": 25443595, + "size": 272, + "sha256": "f3e22554d46d53ff64d20309370c4dc080365de895eacd3f46a3b16cc50422df" + }, + { + "path": "wordpress/wp-includes/images/icon-library/query-pagination-next.svg", + "offset": 25443867, + "size": 146, + "sha256": "bcbf2e7a3a448130f22f32192c2ee974ee7092e2427862e785c5333cfc2b81f8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/query-pagination-numbers.svg", + "offset": 25444013, + "size": 299, + "sha256": "aeff55a16a0771555879bc83e3b39f9952dff42ae0ffc06cf13fdce0de8c22ac" + }, + { + "path": "wordpress/wp-includes/images/icon-library/query-pagination-previous.svg", + "offset": 25444312, + "size": 148, + "sha256": "7cc81e0da7b198177198314c1813a4d385cd92293f4cc9d84479a1056416c918" + }, + { + "path": "wordpress/wp-includes/images/icon-library/query-pagination.svg", + "offset": 25444460, + "size": 130, + "sha256": "c279d95ff0624722fb7e4d5c2dcc5d43dcc7adc306b4bbc39645ddc09d8097db" + }, + { + "path": "wordpress/wp-includes/images/icon-library/quote.svg", + "offset": 25444590, + "size": 284, + "sha256": "5ac016fc8ba05901bb29e0dae844d0da395f81c52cef5ff2a69f7b3bd94836f5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/receipt.svg", + "offset": 25444874, + "size": 519, + "sha256": "c1346e37448b8256c89fb4e67331a9ef512959503eb99b662ca59ee8eca2b6df" + }, + { + "path": "wordpress/wp-includes/images/icon-library/redo.svg", + "offset": 25445393, + "size": 232, + "sha256": "34d79d640d79f41dae8be2e530c74fb8de1959b3c20a366fd56f23d31ec2f4c8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/remove-bug.svg", + "offset": 25445625, + "size": 912, + "sha256": "afaa9d9061487bd4a1ccbd3b2266fc267c730dc2daee37e416b9700db9e2a957" + }, + { + "path": "wordpress/wp-includes/images/icon-library/remove-submenu.svg", + "offset": 25446537, + "size": 439, + "sha256": "8af733096e19daa7fa69e8b39a431be9c460d102e851297e2a33d70f5362e118" + }, + { + "path": "wordpress/wp-includes/images/icon-library/replace.svg", + "offset": 25446976, + "size": 497, + "sha256": "8ed99099f4e3ffd870ac8bfd3ef33879c2d2911fdbb3830ecae42eae10a01dac" + }, + { + "path": "wordpress/wp-includes/images/icon-library/reset.svg", + "offset": 25447473, + "size": 99, + "sha256": "d4377ffeca2388ec9b4f9547b73506af7d494054bc0adc7e59613d74587dc523" + }, + { + "path": "wordpress/wp-includes/images/icon-library/resize-corner-ne.svg", + "offset": 25447572, + "size": 136, + "sha256": "8f703be4bdccd8a04ac79817e47727773bc8844d32b988b740c3fa34e63ba51f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/reusable-block.svg", + "offset": 25447708, + "size": 373, + "sha256": "b9c62bcd58252415e0d8911c129955afe2ba551761245696e4aa3b5c13776bfd" + }, + { + "path": "wordpress/wp-includes/images/icon-library/rotate-left.svg", + "offset": 25448081, + "size": 460, + "sha256": "79afad0be017634eac873fc7a63736298cbf396c1c05fe399d6b1ec086e2ecad" + }, + { + "path": "wordpress/wp-includes/images/icon-library/rotate-right.svg", + "offset": 25448541, + "size": 459, + "sha256": "6309cd247bb7b8e3d0dd498312e2e5d143fbdf5d38baed10f5fa2faf44b99530" + }, + { + "path": "wordpress/wp-includes/images/icon-library/row.svg", + "offset": 25449000, + "size": 259, + "sha256": "f19950897cbb8a76d029a954c1f81910d669c74651ceb4bb0aa8145e68a78b29" + }, + { + "path": "wordpress/wp-includes/images/icon-library/rss.svg", + "offset": 25449259, + "size": 329, + "sha256": "e3c80827e04b63f534e36ee8961f334f836305461f71e3695206c5ce0814cc39" + }, + { + "path": "wordpress/wp-includes/images/icon-library/scheduled.svg", + "offset": 25449588, + "size": 231, + "sha256": "360e93773be644a8031d44f7698ea86cb1aadb0b7579308b4edf3da65ce87392" + }, + { + "path": "wordpress/wp-includes/images/icon-library/search.svg", + "offset": 25449819, + "size": 271, + "sha256": "3f1a5d8fcd67c8784f1279965476e5097c3e16fb75d205c731b2ebe61142dd16" + }, + { + "path": "wordpress/wp-includes/images/icon-library/seen.svg", + "offset": 25450090, + "size": 1525, + "sha256": "3c298e6a83d91a0670e435eee6bbb196b7f7cc010f3a35ca4eef143d23523676" + }, + { + "path": "wordpress/wp-includes/images/icon-library/send.svg", + "offset": 25451615, + "size": 317, + "sha256": "48baf92e5566d821f36db9aa66744f7e8fcd9f28f0bb3fcb0d6e3d602ea767b6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/separator.svg", + "offset": 25451932, + "size": 133, + "sha256": "923524c176d66f1f77b58d91af56d4158ece2846bf1bfb9d6af68fab8a930a47" + }, + { + "path": "wordpress/wp-includes/images/icon-library/settings.svg", + "offset": 25452065, + "size": 427, + "sha256": "69fc478b48c6d5c62e715000e50e7700f9fbb0720ec0dd5c57d81eb00d456f8c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/shadow.svg", + "offset": 25452492, + "size": 468, + "sha256": "c7af1158093b97f412040a13a53925e26394771d2449c716e271647f1fbb8f74" + }, + { + "path": "wordpress/wp-includes/images/icon-library/share.svg", + "offset": 25452960, + "size": 330, + "sha256": "09bf271884b23615cca045ea60546b4fb5314f535742730947da26a96491add2" + }, + { + "path": "wordpress/wp-includes/images/icon-library/shield.svg", + "offset": 25453290, + "size": 380, + "sha256": "4033e855720053468abfd305b6e3c5e4a61c08d9809388d94d907f1fe980a922" + }, + { + "path": "wordpress/wp-includes/images/icon-library/shipping.svg", + "offset": 25453670, + "size": 546, + "sha256": "5eeb68f297e7a13dce779b0cd4c81c086d9d65f0a0c7c2179e577bc89f399709" + }, + { + "path": "wordpress/wp-includes/images/icon-library/shortcode.svg", + "offset": 25454216, + "size": 197, + "sha256": "3d286bb3a826378a34b78e342c3f2e80b6ca083656705d5beed82fa84edfb5e8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/shuffle.svg", + "offset": 25454413, + "size": 878, + "sha256": "6c29d4666a0ee7545dbb3cd2d3a60eab2a7a2d62986a49c94a87a3381e87e44d" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sidebar.svg", + "offset": 25455291, + "size": 229, + "sha256": "f943cb60be241f43c8a8b0e279c9d59ea1f77f08844ca31b2a40856ea501d2d1" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sides-all.svg", + "offset": 25455520, + "size": 153, + "sha256": "912af6faa7c5b09747f7bded34a1a96234a802b1266faa37f1e8e1023cea1a38" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sides-axial.svg", + "offset": 25455673, + "size": 422, + "sha256": "e48dc00b68b8682d4a5697bca726ab8b2d00b8c23c1c81b0ffd3901381ba9376" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sides-bottom.svg", + "offset": 25456095, + "size": 217, + "sha256": "a43bc29b3c0ca1c2edf6d6ddfadec9c8e96deefc82388bfa616ff5ba84a1ae05" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sides-horizontal.svg", + "offset": 25456312, + "size": 246, + "sha256": "472d082f4f7e2888a7a6d126545947be8d249c7e625faf448b933303707b6389" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sides-left.svg", + "offset": 25456558, + "size": 215, + "sha256": "32a658967364109f97b68ef0f11b3ec51255079d3bae17751db98a4cc0608e7c" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sides-right.svg", + "offset": 25456773, + "size": 214, + "sha256": "570a2b06916ceb481daada77879f2db0d74d1747391cc4b7a60f6caab152ca19" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sides-top.svg", + "offset": 25456987, + "size": 214, + "sha256": "0e48952254ce46cfc6f16981ad01ec006ac36d9d86e9df8581c2485bbd7db69e" + }, + { + "path": "wordpress/wp-includes/images/icon-library/sides-vertical.svg", + "offset": 25457201, + "size": 248, + "sha256": "494e224cca6e20de57ec4f2cc0fef607d4a4b4fa2f82957035082ff84093a1ce" + }, + { + "path": "wordpress/wp-includes/images/icon-library/site-logo.svg", + "offset": 25457449, + "size": 376, + "sha256": "7374fa4412a57de305efe209cc9b280f54b32a79b7307e1f5dfc103a33a7f217" + }, + { + "path": "wordpress/wp-includes/images/icon-library/square.svg", + "offset": 25457825, + "size": 206, + "sha256": "e30a17cb70facbda96d6e8efb5ab4f716c06d5419dbec9b21d1a760618f2b29a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/stack.svg", + "offset": 25458031, + "size": 258, + "sha256": "3bb98254da6be42d7f74ba255523ea7a092aeaf117a175544f6260f5a56e86b6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/star-empty.svg", + "offset": 25458289, + "size": 695, + "sha256": "496cca4786472b43d1d583ea6751aa98549a165d75d2e00fa0f11586d3a9f703" + }, + { + "path": "wordpress/wp-includes/images/icon-library/star-filled.svg", + "offset": 25458984, + "size": 418, + "sha256": "8db60a1d058fe3fb68da0fef6a1a088f3b93cabf54051831a768f0f6eaddf427" + }, + { + "path": "wordpress/wp-includes/images/icon-library/star-half.svg", + "offset": 25459402, + "size": 553, + "sha256": "27c3c535cde1317240a1bcaa69a4be7f2c4d7c12970b53a92cda67510a9b3cb5" + }, + { + "path": "wordpress/wp-includes/images/icon-library/store.svg", + "offset": 25459955, + "size": 419, + "sha256": "99f2b58366f3d5ac1c253872a8cdc02b9458d5aea1c68500afe8c6be45c7bec0" + }, + { + "path": "wordpress/wp-includes/images/icon-library/stretch-full-width.svg", + "offset": 25460374, + "size": 117, + "sha256": "b1385aae517e50a7be22ea29d68c3791d3b38478525796611d63f015a6e3537f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/stretch-wide.svg", + "offset": 25460491, + "size": 134, + "sha256": "09e4b3a86c957e9e0fb79cf45deaf5fd5f9b53f41a1a199a0c51ebf2d298b763" + }, + { + "path": "wordpress/wp-includes/images/icon-library/styles.svg", + "offset": 25460625, + "size": 216, + "sha256": "0632bab851d567eb44014885ba9bf40aa446711828c8cc81dd260f3857c47f19" + }, + { + "path": "wordpress/wp-includes/images/icon-library/subscript.svg", + "offset": 25460841, + "size": 407, + "sha256": "6970e618639714a97a5859b0a8722d6f889280c76d015782ac2ece5b1d1852e0" + }, + { + "path": "wordpress/wp-includes/images/icon-library/superscript.svg", + "offset": 25461248, + "size": 408, + "sha256": "bce79492793e595af411a642bf19f8aa3921987226f138e734c4cc983d2adeab" + }, + { + "path": "wordpress/wp-includes/images/icon-library/swatch.svg", + "offset": 25461656, + "size": 692, + "sha256": "506e860dbdd6ce20b6fa635acbd178e08684fc082e30cab6708de63336103d79" + }, + { + "path": "wordpress/wp-includes/images/icon-library/symbol-filled.svg", + "offset": 25462348, + "size": 302, + "sha256": "73fdeb29c9e170b2998916cc8f3cf8816df27a7b9cbd3317512e4e9c7f161c20" + }, + { + "path": "wordpress/wp-includes/images/icon-library/symbol.svg", + "offset": 25462650, + "size": 413, + "sha256": "4901d66e639fb810a5dc3bea778b230d4da6b410c63c5034838e027a22ca95af" + }, + { + "path": "wordpress/wp-includes/images/icon-library/tab.svg", + "offset": 25463063, + "size": 225, + "sha256": "849c2d4c13ce3e7306d69b8e6a58f20a2c4ef2cee770c38d7384555e82f0d5aa" + }, + { + "path": "wordpress/wp-includes/images/icon-library/table-column-after.svg", + "offset": 25463288, + "size": 354, + "sha256": "e9fe08067beb0485a2ab69b280f90a1ba2c95df16023f81cd0bbb8c09ce89db3" + }, + { + "path": "wordpress/wp-includes/images/icon-library/table-column-before.svg", + "offset": 25463642, + "size": 351, + "sha256": "041a6edc9b62e1efe2d87d89d6f7063f192051356482af0c2b26a41d69645b3a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/table-column-delete.svg", + "offset": 25463993, + "size": 379, + "sha256": "b1bf20a60e7e0ababb19d76287d453794286b2742478dc41d041f519c07ae423" + }, + { + "path": "wordpress/wp-includes/images/icon-library/table-of-contents.svg", + "offset": 25464372, + "size": 338, + "sha256": "38bb4aec6c1d222dc4b87c6e8f872c4eaa7769a455881fa99ebccc90f98429d8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/table-row-after.svg", + "offset": 25464710, + "size": 351, + "sha256": "3f55adcb17ce9230b183024ff011d948c70d128de2f1d907085f9073ab9a7656" + }, + { + "path": "wordpress/wp-includes/images/icon-library/table-row-before.svg", + "offset": 25465061, + "size": 349, + "sha256": "3293dd0f58a0c3e9d259116fb1dfaae23fa8f52e5e0a360a2da0246171014433" + }, + { + "path": "wordpress/wp-includes/images/icon-library/table-row-delete.svg", + "offset": 25465410, + "size": 376, + "sha256": "f24377832b5a0e7b96721dd9e8fa4213f73fb68ef04aa45d3ef21a96665822f0" + }, + { + "path": "wordpress/wp-includes/images/icon-library/table.svg", + "offset": 25465786, + "size": 318, + "sha256": "c9992a4d47f32f51cb20ce86f662ef9d1f35262acaa9a50d41b42e49e2c79c8b" + }, + { + "path": "wordpress/wp-includes/images/icon-library/tablet.svg", + "offset": 25466104, + "size": 259, + "sha256": "9da7f81be657473f98917afb7a6ebcd32b103a0b5dd035065ae325f2a6103c00" + }, + { + "path": "wordpress/wp-includes/images/icon-library/tabs-menu-item.svg", + "offset": 25466363, + "size": 216, + "sha256": "bdf28218e387763fece4ddf6bf1b38370f4f0658d7e86d7a45d0daba942e18a7" + }, + { + "path": "wordpress/wp-includes/images/icon-library/tabs-menu.svg", + "offset": 25466579, + "size": 250, + "sha256": "044745b22f9e86712aed01cfa4bb20c2147cee6ad1675e1233d98edd0d2700f4" + }, + { + "path": "wordpress/wp-includes/images/icon-library/tabs.svg", + "offset": 25466829, + "size": 284, + "sha256": "a0b7ae4856b75d39a0417fa1d7f9ef7a997fcff7c6a575cd58dc4d1b5c5fdc15" + }, + { + "path": "wordpress/wp-includes/images/icon-library/tag.svg", + "offset": 25467113, + "size": 430, + "sha256": "f0d94a26d251651508dbc2ac56603119f0719ac0ee424f61a1fe343741878d96" + }, + { + "path": "wordpress/wp-includes/images/icon-library/term-count.svg", + "offset": 25467543, + "size": 1103, + "sha256": "a32be7071dd48e5c7406e59072e930cbbe7b65eaf0b704c2b72b866a6ba61529" + }, + { + "path": "wordpress/wp-includes/images/icon-library/term-description.svg", + "offset": 25468646, + "size": 264, + "sha256": "9553d84a2aad6a23149263024f969cb466a01b3391eb5c694b8d72367e1bf71f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/term-name.svg", + "offset": 25468910, + "size": 471, + "sha256": "e5ba7f3e72e620012fdeec8da36778d1f9ffdae85c79c8dbcfc627c47305226a" + }, + { + "path": "wordpress/wp-includes/images/icon-library/text-color.svg", + "offset": 25469381, + "size": 162, + "sha256": "3afb4f0154e0b5797baea01eee74e8213393c0f3654367de5176a82a61c428b4" + }, + { + "path": "wordpress/wp-includes/images/icon-library/text-horizontal.svg", + "offset": 25469543, + "size": 201, + "sha256": "5404d25f7832f74f274869ba4328206db7c7dad56142c97876bb864291ad01bb" + }, + { + "path": "wordpress/wp-includes/images/icon-library/text-vertical.svg", + "offset": 25469744, + "size": 196, + "sha256": "2bcc64cb9c4b00eb4870e4421a82222b776830c51bd0e0226e07baad7385b85f" + }, + { + "path": "wordpress/wp-includes/images/icon-library/thumbs-down.svg", + "offset": 25469940, + "size": 435, + "sha256": "768aed3022b67ddda35cba5f0a747cc0cf3c3b1e603d36a39cf9144ae4711dc8" + }, + { + "path": "wordpress/wp-includes/images/icon-library/thumbs-up.svg", + "offset": 25470375, + "size": 432, + "sha256": "9ca676d0b754b6d472e9219f10bbbf846af4d390c526f7ca96594a9ecac96614" + }, + { + "path": "wordpress/wp-includes/images/icon-library/time-to-read.svg", + "offset": 25470807, + "size": 249, + "sha256": "bc8fce462e0236b3b0961d3740c8cbb5f3d0f187d1a35e57336ecc8e4b152cd7" + }, + { + "path": "wordpress/wp-includes/images/icon-library/tip.svg", + "offset": 25471056, + "size": 278, + "sha256": "64ebbc4d2c20448d980917709ca35940e330b669064f961fe17813452033d228" + }, + { + "path": "wordpress/wp-includes/images/icon-library/title.svg", + "offset": 25471334, + "size": 154, + "sha256": "332f4f2c73e6c5dd4b9285ae044a50e1f586cc5bd1d3dcc7641d891036b3e101" + }, + { + "path": "wordpress/wp-includes/images/icon-library/tool.svg", + "offset": 25471488, + "size": 273, + "sha256": "aecef9c2fbb17724035daaab1908d07714f26a43228ed3c12824669d86a0b076" + }, + { + "path": "wordpress/wp-includes/images/icon-library/trash.svg", + "offset": 25471761, + "size": 446, + "sha256": "cb41ba3a499bbfa586ba49ca8ffbad57b4f429e668a1eb63c728fff515aa16a3" + }, + { + "path": "wordpress/wp-includes/images/icon-library/trending-down.svg", + "offset": 25472207, + "size": 236, + "sha256": "a2b689408bbf4d3532a227b9fbbb408917a3bad1d9bc7c8a6dd5cfb77334bf53" + }, + { + "path": "wordpress/wp-includes/images/icon-library/trending-up.svg", + "offset": 25472443, + "size": 232, + "sha256": "a63368aa39ee5002837a706d83f623016cf0a9e3be1104c1eba7537e0d6d5fb4" + }, + { + "path": "wordpress/wp-includes/images/icon-library/typography.svg", + "offset": 25472675, + "size": 1457, + "sha256": "930d9480a5a30b3e14f5038f6b298591c445ea23ac469a2e7e8b827eb6299608" + }, + { + "path": "wordpress/wp-includes/images/icon-library/undo.svg", + "offset": 25474132, + "size": 224, + "sha256": "a5e2cfc2d098fdbf30439c1abb147d888bfc14e22901e3f1749a8cf11434fbfd" + }, + { + "path": "wordpress/wp-includes/images/icon-library/ungroup.svg", + "offset": 25474356, + "size": 359, + "sha256": "3268f67fbc15e385e705ffa58b822bb3a07bb77a0f21453e8105ac4167c0073d" + }, + { + "path": "wordpress/wp-includes/images/icon-library/unlock.svg", + "offset": 25474715, + "size": 247, + "sha256": "a06eff6878c704f0af6c5ecebe532f7d9cf06166ea45ec07bf86830ef1fbb298" + }, + { + "path": "wordpress/wp-includes/images/icon-library/unseen.svg", + "offset": 25474962, + "size": 555, + "sha256": "dd625989150762d4f85b94d8c1969b727bdaa30fc202aeba4e67d96c53c70075" + }, + { + "path": "wordpress/wp-includes/images/icon-library/update.svg", + "offset": 25475517, + "size": 287, + "sha256": "02dd0c147578a87bab02d2514a5faa246aec0292129685e97dc20ba583ca8632" + }, + { + "path": "wordpress/wp-includes/images/icon-library/upload.svg", + "offset": 25475804, + "size": 164, + "sha256": "99ddf3678170f41566f9d86c6432f37e251ec706a99abbbb3d87e8741e35c949" + }, + { + "path": "wordpress/wp-includes/images/icon-library/verse.svg", + "offset": 25475968, + "size": 520, + "sha256": "8c76ea2d00c8479a8ee241969d8359d19b04cdadbafb735180e2d006822ee8c1" + }, + { + "path": "wordpress/wp-includes/images/icon-library/video.svg", + "offset": 25476488, + "size": 283, + "sha256": "05d6975ad2ca71fd655e62eb2f5f5aa03443cc4e6841eae564684196da88c2b9" + }, + { + "path": "wordpress/wp-includes/images/icon-library/widget.svg", + "offset": 25476771, + "size": 440, + "sha256": "e2368d7e325aff064f0181a6f6c193384c9c0b83a149dee1fe918c64e41ea8e6" + }, + { + "path": "wordpress/wp-includes/images/icon-library/word-count.svg", + "offset": 25477211, + "size": 1107, + "sha256": "d84fb6ff24268ad8324d5cd723ead485522ad23fbf57a140b92decbbf43a2fed" + }, + { + "path": "wordpress/wp-includes/images/icon-library/wordpress.svg", + "offset": 25478318, + "size": 869, + "sha256": "b7e4498a891728a226cc78d88ec914df784b32acc59f812b41cb6526dccebcad" + }, + { + "path": "wordpress/wp-includes/images/icon-pointer-flag-2x.png", + "offset": 25479187, + "size": 1369, + "sha256": "554e5e628d5301b7d3ed9d20f79342c1176a334e0a0d6d2ee7612afec0ed6b25" + }, + { + "path": "wordpress/wp-includes/images/icon-pointer-flag.png", + "offset": 25480556, + "size": 783, + "sha256": "655107a80cf1820b5a26a53ba055fb3dcee5430b1e6cb72f0b2c3a3e75387243" + }, + { + "path": "wordpress/wp-includes/images/media/archive.png", + "offset": 25481339, + "size": 417, + "sha256": "59cd4db7166e01d6444630f7ab099c069a0032660585c3a6a2ac4cf1a1e9c220" + }, + { + "path": "wordpress/wp-includes/images/media/archive.svg", + "offset": 25481756, + "size": 391, + "sha256": "632b1ac49c9c59e7dfb0a63b2b098cec10ad6eb1b53efaa8ceab3374c220b6be" + }, + { + "path": "wordpress/wp-includes/images/media/audio.png", + "offset": 25482147, + "size": 382, + "sha256": "7263d0a183d50bef10e32252611423ff50582aa63f2c82e129742b2d71f3001d" + }, + { + "path": "wordpress/wp-includes/images/media/audio.svg", + "offset": 25482529, + "size": 384, + "sha256": "abcf228207c988f7398a7cee2b49da7bec3989c7eda175dec7256d5c599d73f4" + }, + { + "path": "wordpress/wp-includes/images/media/code.png", + "offset": 25482913, + "size": 274, + "sha256": "ea983e02004db1a13828d04665935785e991b38cc540d92b6f970b106088917f" + }, + { + "path": "wordpress/wp-includes/images/media/code.svg", + "offset": 25483187, + "size": 249, + "sha256": "4547bc022a353674efa6ef179934d246f7e0d9916063ccd019f179073014c5c3" + }, + { + "path": "wordpress/wp-includes/images/media/default.png", + "offset": 25483436, + "size": 168, + "sha256": "6dfe1e05661b087111ce1b280d4f082cbfd2b786a0941224da5af2ce1b29fb75" + }, + { + "path": "wordpress/wp-includes/images/media/default.svg", + "offset": 25483604, + "size": 161, + "sha256": "e9788061b71de84ee8b6050801c2bb642c898f072b526bd70f5ce790b5bf7cb3" + }, + { + "path": "wordpress/wp-includes/images/media/document.png", + "offset": 25483765, + "size": 200, + "sha256": "2fc10f9452d76439d0ad3bb712d360c869347110abf205cc0d733f489d56f8c9" + }, + { + "path": "wordpress/wp-includes/images/media/document.svg", + "offset": 25483965, + "size": 318, + "sha256": "944938f013ab89fd1dbb26363e53eaa7f4666f52b0ebc95bd07da356ee346c3e" + }, + { + "path": "wordpress/wp-includes/images/media/interactive.png", + "offset": 25484283, + "size": 319, + "sha256": "ac9e53f5b88e2f2b1304ff2b3d1ec6132463ce2bf3ef18a1f091c3ce78df2e58" + }, + { + "path": "wordpress/wp-includes/images/media/interactive.svg", + "offset": 25484602, + "size": 298, + "sha256": "d177f4a6281ad67d413eade7c469cdadeaf5abbe5581009d3a4224a9b6616516" + }, + { + "path": "wordpress/wp-includes/images/media/spreadsheet.png", + "offset": 25484900, + "size": 188, + "sha256": "ff0e1abf5b65624af79d2af2b8e542f8eba40a409695229ff947355e20b78b9c" + }, + { + "path": "wordpress/wp-includes/images/media/spreadsheet.svg", + "offset": 25485088, + "size": 460, + "sha256": "6bb909a17e4413d0a35563297c61e8202f8fab9f99a0137d7dc8a2e2a7e479df" + }, + { + "path": "wordpress/wp-includes/images/media/text.png", + "offset": 25485548, + "size": 188, + "sha256": "04cdd847ef8863e8d7185a689067f6a18c2ccd2bc589c7006aceb6890e611b9c" + }, + { + "path": "wordpress/wp-includes/images/media/text.svg", + "offset": 25485736, + "size": 281, + "sha256": "b4211302df5e9c72034874e8bacd09f7162913292cc9e17d8a6fd376c3b97410" + }, + { + "path": "wordpress/wp-includes/images/media/video.png", + "offset": 25486017, + "size": 283, + "sha256": "075c8a3fe3c72c935cd4513b8c88157a503823986cb0cfde616f2182edb145eb" + }, + { + "path": "wordpress/wp-includes/images/media/video.svg", + "offset": 25486300, + "size": 302, + "sha256": "5be7a8a645e00e88dbf282b9de387fe0f45d5ac0e71bef62bda07b2632b2b991" + }, + { + "path": "wordpress/wp-includes/images/rss-2x.png", + "offset": 25486602, + "size": 1306, + "sha256": "4ab3e7328166e5c6c5875e5db14ac3178ca2db1225ae0e25af3dda8b1d4477cd" + }, + { + "path": "wordpress/wp-includes/images/rss.png", + "offset": 25487908, + "size": 608, + "sha256": "0c6daa646e0a867e5f721b5017c98cfd2c82c26c60b614531ddae8a5d9986be8" + }, + { + "path": "wordpress/wp-includes/images/smilies/frownie.png", + "offset": 25488516, + "size": 1007, + "sha256": "f14a201d5e30cdf604d6a1d0e30095ed47ed2bcd2a45d12924f2d22aeca8702e" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_arrow.gif", + "offset": 25489523, + "size": 169, + "sha256": "044e9b8a037d4163bded47d4b2b598417311772de704242c3281ebe82dacd818" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_biggrin.gif", + "offset": 25489692, + "size": 173, + "sha256": "2f40b22c7cdb2c74711e2d698a956b750777f11a01b79a37225a0867e852de5b" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_confused.gif", + "offset": 25489865, + "size": 170, + "sha256": "9503f2b1c8e497f3ba14c92174fe30b41a7b0292bf868041ff3f5f8534818d1d" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_cool.gif", + "offset": 25490035, + "size": 172, + "sha256": "613859b8b3de47cc23b23e2aa60eebfa84e88d7cadb3c19d043be2be205576f0" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_cry.gif", + "offset": 25490207, + "size": 412, + "sha256": "c8068c436af5ca2fba8246cd695c0a8d9d10b24df3d1baffbecd1d9bca2500e2" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_eek.gif", + "offset": 25490619, + "size": 170, + "sha256": "5109a87f343caaa9346525f1ab84845e6492529c512f8fe0f44c1bdc28810e4c" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_evil.gif", + "offset": 25490789, + "size": 193, + "sha256": "c6cfb86865b827d8ce2f8c3c9bad8e3d343e8d0d10246d525696328baed47052" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_exclaim.gif", + "offset": 25490982, + "size": 236, + "sha256": "9e4ab665dc71006fd42525f61f758e615e5070a99432da2d406ac0fef12e2bd2" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_idea.gif", + "offset": 25491218, + "size": 174, + "sha256": "533afbf7a1abd912749b35b11c53e4ff27a21f0b474ee1139806b338870dd1b6" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_lol.gif", + "offset": 25491392, + "size": 331, + "sha256": "ccc3808f8f1b3376d5b64b787237932b3466b6b0745fbc0985ef229c32f37529" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_mad.gif", + "offset": 25491723, + "size": 172, + "sha256": "731fdf2acb11bf8b453089e3b23dc9787f1e050521e02ba3e028ea607c9831e0" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_mrgreen.gif", + "offset": 25491895, + "size": 348, + "sha256": "b79e72d93bdb7ae96b5e397ceeb338c44a5e3dc3d46b46901d5d857bd2c0c1e2" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_neutral.gif", + "offset": 25492243, + "size": 167, + "sha256": "2617457b3798a63667a9d2615d8c75d47cc81703500936726ae43fc446a861d9" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_question.gif", + "offset": 25492410, + "size": 247, + "sha256": "37bf38d305937adc7b519f0f77a50f1f286101f12160e3e8810c3b6cbc7fdf86" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_razz.gif", + "offset": 25492657, + "size": 175, + "sha256": "d7a51e6ebe68850e346f06a93f47bceeea494b8d32a8696fd1cf0fc1cafaf343" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_redface.gif", + "offset": 25492832, + "size": 645, + "sha256": "daba7a68ac81452b9b66724828fc56b90cfa6161ae4b16ac4c24648fda069aac" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_rolleyes.gif", + "offset": 25493477, + "size": 471, + "sha256": "83750e919e293834182e8601e9061b0bf45f7db614972ddcdf93c8c1037670d5" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_sad.gif", + "offset": 25493948, + "size": 167, + "sha256": "4f58b9ca5e80a2fa963c332acdb7e135d2b2accb4ecf6e92c500d808bcb02bb6" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_smile.gif", + "offset": 25494115, + "size": 173, + "sha256": "5f5239a67e6b484eb8ecee803f3de3ff86de2b40e82999bfc950e0e54ab6968a" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_surprised.gif", + "offset": 25494288, + "size": 174, + "sha256": "ab54efa6fdf9fa7b6f81f66103900fca586a223520750530ed0e7c35b8edf768" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_twisted.gif", + "offset": 25494462, + "size": 241, + "sha256": "7e2f8bb3909c0884332e2da356f319ef750d28df4213fb248b102c1284b24784" + }, + { + "path": "wordpress/wp-includes/images/smilies/icon_wink.gif", + "offset": 25494703, + "size": 168, + "sha256": "1fb1f54995946b7049c93bb3d281cd089b11bc1d56dc6f0b50f2b7acd8e560b1" + }, + { + "path": "wordpress/wp-includes/images/smilies/mrgreen.png", + "offset": 25494871, + "size": 1486, + "sha256": "98a1f7d28eafca1248848ff14817cc42d3831c76bfac7a4d7a5b1df7d6c6dc2b" + }, + { + "path": "wordpress/wp-includes/images/smilies/rolleyes.png", + "offset": 25496357, + "size": 1233, + "sha256": "5ae63b145b47c2a9ef1e88aebb73862d08db7ff59de147924ccc2a888169d47f" + }, + { + "path": "wordpress/wp-includes/images/smilies/simple-smile.png", + "offset": 25497590, + "size": 1008, + "sha256": "3197ddce1839615fe2c648b744cfae61ec20fae4326574db3820cb749ac09a07" + }, + { + "path": "wordpress/wp-includes/images/spinner-2x.gif", + "offset": 25498598, + "size": 7536, + "sha256": "3e9fd050059a29fbab9bff42df8fd81207702d8feed30256d375d61cba089045" + }, + { + "path": "wordpress/wp-includes/images/spinner.gif", + "offset": 25506134, + "size": 3656, + "sha256": "5c2d44038a6c53bd74f3a26e690c1974e1385fffbc6def4c59f91dc1f738f0b3" + }, + { + "path": "wordpress/wp-includes/images/toggle-arrow-2x.png", + "offset": 25509790, + "size": 354, + "sha256": "4ea5ddc1076bd9c4c40a944a8265bf49d0df10a7d6ba3dcba689ed50c9cdd925" + }, + { + "path": "wordpress/wp-includes/images/toggle-arrow.png", + "offset": 25510144, + "size": 289, + "sha256": "26ba4ab5f7fae15f041b6061b76afa4a178ef98d97f7d578a0930b8532888910" + }, + { + "path": "wordpress/wp-includes/images/uploader-icons-2x.png", + "offset": 25510433, + "size": 3542, + "sha256": "87f7bc3b5729467166e41a4e1e25cd0aa4b758e7f915aa8fcc4be4da2b0eb0c3" + }, + { + "path": "wordpress/wp-includes/images/uploader-icons.png", + "offset": 25513975, + "size": 1556, + "sha256": "b884167f785cff78edb90c4fde4e386a6f6e8717fb06be26c440303c8f955b8c" + }, + { + "path": "wordpress/wp-includes/images/w-logo-blue-white-bg.png", + "offset": 25515531, + "size": 4119, + "sha256": "6bdb369337ac2496761c6f063bffea0aa6a91d4662279c399071a468251f51f0" + }, + { + "path": "wordpress/wp-includes/images/w-logo-blue.png", + "offset": 25519650, + "size": 3113, + "sha256": "a8c9355719e180f67753c823b87c29f40e21df91c20b44eb92d4cb36ef575d09" + }, + { + "path": "wordpress/wp-includes/images/wpicons-2x.png", + "offset": 25522763, + "size": 14931, + "sha256": "3e3ca5c608cb0d34133579068509510fb171a1f58af2647e1368e88eca88f644" + }, + { + "path": "wordpress/wp-includes/images/wpicons.png", + "offset": 25537694, + "size": 7086, + "sha256": "2b4db9a98238c766cf405f00d78cb0c96cd8f0ba536bc83a8659213e999442fa" + }, + { + "path": "wordpress/wp-includes/images/wpspin-2x.gif", + "offset": 25544780, + "size": 8875, + "sha256": "e8a839ef2ddca2cf9f89f0661e5fcce03c58b0f240b38c53b33780a6d5a79a75" + }, + { + "path": "wordpress/wp-includes/images/wpspin.gif", + "offset": 25553655, + "size": 2052, + "sha256": "2a8f12a7ebe254858e4b9c2a61f9e6e82acb18958dedb5c03265413618ef5518" + }, + { + "path": "wordpress/wp-includes/images/xit-2x.gif", + "offset": 25555707, + "size": 825, + "sha256": "03bd4516b71adfd84327f9a4f13d6f0824d445a22ecae0742781bf1b3b7f436d" + }, + { + "path": "wordpress/wp-includes/images/xit.gif", + "offset": 25556532, + "size": 181, + "sha256": "cd3ba896382ba0845bf20deb332650e6fd0e2f8d40137c2057c79d1cb88719b1" + }, + { + "path": "wordpress/wp-includes/js/admin-bar.js", + "offset": 25556713, + "size": 10547, + "sha256": "66076a79c995da5a65b1428b72e23547c97d5b8c9eef1844f77151a128714d0c" + }, + { + "path": "wordpress/wp-includes/js/admin-bar.min.js", + "offset": 25567260, + "size": 3487, + "sha256": "6be3c1147b0af8bae5ef536661d17aeb6a838bf63579e3fdf48a0de74675244f" + }, + { + "path": "wordpress/wp-includes/js/api-request.js", + "offset": 25570747, + "size": 3324, + "sha256": "c63eed6f195f9b76b4624a1c46da8ce43bd10881bab248057f27e017342a37ba" + }, + { + "path": "wordpress/wp-includes/js/api-request.min.js", + "offset": 25574071, + "size": 1023, + "sha256": "1a234275545ba883616ac6b4151a0f06d9bb097146e806e40317a263bbf1c51e" + }, + { + "path": "wordpress/wp-includes/js/autosave.js", + "offset": 25575094, + "size": 22476, + "sha256": "89a200235872235a8287c89888a12496f1afda582c7650cdb9811d7957e8b569" + }, + { + "path": "wordpress/wp-includes/js/autosave.min.js", + "offset": 25597570, + "size": 5807, + "sha256": "4189d799362a425f68e0a85f4d421732f0e3bfb16aacc0b39ff22dd8b65eb2c3" + }, + { + "path": "wordpress/wp-includes/js/backbone.js", + "offset": 25603377, + "size": 80436, + "sha256": "843bd01aa3944ef9e3f426f35cf6fa46f4a55985bf9af23a2d01b3d447ed4121" + }, + { + "path": "wordpress/wp-includes/js/backbone.min.js", + "offset": 25683813, + "size": 24314, + "sha256": "9824888215d664c489cf631193c9e29a990e0b3e79bd9a1219f59ddda475fe6e" + }, + { + "path": "wordpress/wp-includes/js/clipboard.js", + "offset": 25708127, + "size": 26807, + "sha256": "ba5f78267d0c04e8a31aebd7860c7fd7fc236891cddd477a4c7fdc19a0eb176e" + }, + { + "path": "wordpress/wp-includes/js/clipboard.min.js", + "offset": 25734934, + "size": 9009, + "sha256": "700c8bd73d93522ca53cdc35e2a71e96caf7c344bc7a8391f3af90c10b917033" + }, + { + "path": "wordpress/wp-includes/js/codemirror/codemirror.min.css", + "offset": 25743943, + "size": 16488, + "sha256": "8ef5163cfa185d744a0ffe0434845181d3fdd4648b9ec24efbdc378aa99fc95b" + }, + { + "path": "wordpress/wp-includes/js/codemirror/codemirror.min.js", + "offset": 25760431, + "size": 620150, + "sha256": "0a4d4ef379c633ddb6635a65d74004bec2d3af49035f98f8b3f699d62f0bfb2c" + }, + { + "path": "wordpress/wp-includes/js/codemirror/csslint.js", + "offset": 26380581, + "size": 374004, + "sha256": "b4384e34ca9091f8da24f9f40071567d8ed7b213a230f6d7313e0e1d4ade2c2b" + }, + { + "path": "wordpress/wp-includes/js/codemirror/espree.min.js", + "offset": 26754585, + "size": 254153, + "sha256": "4f7d640db9790bf61bf02a349aced96377510615ba6d6fa05473016fe15cf717" + }, + { + "path": "wordpress/wp-includes/js/codemirror/esprima.js", + "offset": 27008738, + "size": 283567, + "sha256": "6c36c0e60387f5398f98f68ac76ae832688b32fa9162eae4cc9b6b2cad5f554e" + }, + { + "path": "wordpress/wp-includes/js/codemirror/fakejshint.js", + "offset": 27292305, + "size": 1280, + "sha256": "d51dfa846a7426d39b3394827e61be3a4a5c4248783cc0a63e1cf2f0cd280c65" + }, + { + "path": "wordpress/wp-includes/js/codemirror/htmlhint-kses.js", + "offset": 27293585, + "size": 1220, + "sha256": "3e7a9cc6267e22a2a04422ef0ed95670152fc00682c9b46424e0d4b495d3f165" + }, + { + "path": "wordpress/wp-includes/js/codemirror/htmlhint.js", + "offset": 27294805, + "size": 41978, + "sha256": "16c426fc4d14836f75f512b1c8126fad57bb57afe82a9cae61002c68ec989f78" + }, + { + "path": "wordpress/wp-includes/js/codemirror/jsonlint.js", + "offset": 27336783, + "size": 8775, + "sha256": "ed87a523bc38710c76238bb5751b78b055ee8af9096ef4a3f2a2a1801adc4af6" + }, + { + "path": "wordpress/wp-includes/js/colorpicker.js", + "offset": 27345558, + "size": 29083, + "sha256": "ec35cdd800091a8b653a918ee41a801529ccbd43d28b7c32114396512b3fd93e" + }, + { + "path": "wordpress/wp-includes/js/colorpicker.min.js", + "offset": 27374641, + "size": 16498, + "sha256": "ca8e08111435f4e90d4f5088ddba69400b0eb6b81fcc045c632d8da625ac9ec8" + }, + { + "path": "wordpress/wp-includes/js/comment-reply.js", + "offset": 27391139, + "size": 12513, + "sha256": "a29028aa1ed8ed2be79f32e47e8d89d9e4fafa294bd6e1d854d44955fa5f6a67" + }, + { + "path": "wordpress/wp-includes/js/comment-reply.min.js", + "offset": 27403652, + "size": 3026, + "sha256": "9d4687a19cab8f7442a3bda40c45be4d10e42488e091ddd706c3caed83c3ee1f" + }, + { + "path": "wordpress/wp-includes/js/crop/cropper.css", + "offset": 27406678, + "size": 2949, + "sha256": "89a21c35e3444539f733edb7bbfd43089659e4c708006526ebb0ba1ec7c23b40" + }, + { + "path": "wordpress/wp-includes/js/crop/cropper.js", + "offset": 27409627, + "size": 16485, + "sha256": "1f3927500803cba997b34a82fa44bc020f55787709b088ff124e03ab14ef4ccf" + }, + { + "path": "wordpress/wp-includes/js/crop/marqueeHoriz.gif", + "offset": 27426112, + "size": 277, + "sha256": "555aa22858328ed3878666ec5622b8ec00295b95a2740a612637ce5aef284c7d" + }, + { + "path": "wordpress/wp-includes/js/crop/marqueeVert.gif", + "offset": 27426389, + "size": 293, + "sha256": "a636ddc6c2fa174a187c9ebb74d040f52eb820bf3a563f1cda1b52b11908e327" + }, + { + "path": "wordpress/wp-includes/js/customize-base.js", + "offset": 27426682, + "size": 25822, + "sha256": "0b966cb75885edaedd7e669f17ef5a74b88c64bc822eb34c75c33c6e2e935c5e" + }, + { + "path": "wordpress/wp-includes/js/customize-base.min.js", + "offset": 27452504, + "size": 7852, + "sha256": "ba2fd67d688d785cd133c19f2f6aefd4571b94670a1e3502fd015b4e2866954b" + }, + { + "path": "wordpress/wp-includes/js/customize-loader.js", + "offset": 27460356, + "size": 7903, + "sha256": "c5568ea0111c2cbf741096e85985860ea93c14967e26353f0fc3542f70d1b9c9" + }, + { + "path": "wordpress/wp-includes/js/customize-loader.min.js", + "offset": 27468259, + "size": 3551, + "sha256": "556135b9951c765d2d86dda11795840506da3709ed1ecc63f4c59abd90528a24" + }, + { + "path": "wordpress/wp-includes/js/customize-models.js", + "offset": 27471810, + "size": 6821, + "sha256": "62d9478028be78364221b3aacb15557daba37d943641e946988440dbe31b19df" + }, + { + "path": "wordpress/wp-includes/js/customize-models.min.js", + "offset": 27478631, + "size": 3681, + "sha256": "037ffde44e5bd1f280e46bf74ea30deab38c170a3c91ffe6044101d0becb048a" + }, + { + "path": "wordpress/wp-includes/js/customize-preview-nav-menus.js", + "offset": 27482312, + "size": 15024, + "sha256": "a1c13ad4032f19a407857176f9d30ba32af5d5c637dfb7e365536c7b9f08dc5a" + }, + { + "path": "wordpress/wp-includes/js/customize-preview-nav-menus.min.js", + "offset": 27497336, + "size": 5033, + "sha256": "d2763cdfb26c86b9eed3bff87c7167e7722e1a72656b0e8961cd2dcbb58ec72c" + }, + { + "path": "wordpress/wp-includes/js/customize-preview-widgets.js", + "offset": 27502369, + "size": 23253, + "sha256": "7af45a433563b4bfecc15f0049883f4eee380093065f3a831d304456ce217f60" + }, + { + "path": "wordpress/wp-includes/js/customize-preview-widgets.min.js", + "offset": 27525622, + "size": 7820, + "sha256": "3d16a8c947d65ccd0066ae83531c7845dcaa5c5009ccf131fbb9f2edecbd23c2" + }, + { + "path": "wordpress/wp-includes/js/customize-preview.js", + "offset": 27533442, + "size": 28597, + "sha256": "fd3b1401ae274dce59418cdaa7745d1da0619065ecbfdf79078f2b4c43588812" + }, + { + "path": "wordpress/wp-includes/js/customize-preview.min.js", + "offset": 27562039, + "size": 11011, + "sha256": "9f0171ec2ef8eecc495efbce82b602652c2eef1c2f269c3f4ae67a50e3e9daad" + }, + { + "path": "wordpress/wp-includes/js/customize-selective-refresh.js", + "offset": 27573050, + "size": 33335, + "sha256": "01721b69e1e7c783e3fe8ceb88b49f36a09c023857ae518d90e83dfe9e224c25" + }, + { + "path": "wordpress/wp-includes/js/customize-selective-refresh.min.js", + "offset": 27606385, + "size": 10693, + "sha256": "86cd9be348c1fe230421930ad4073a57aa4081f4013ea8465926b63cf5410173" + }, + { + "path": "wordpress/wp-includes/js/customize-views.js", + "offset": 27617078, + "size": 5222, + "sha256": "abaa3f176631ed1f95e3f3be51d444d472414af59c1c501d2a13e97b7bc42ca9" + }, + { + "path": "wordpress/wp-includes/js/customize-views.min.js", + "offset": 27622300, + "size": 2567, + "sha256": "9686e9238cc659d7575951798944fb1cc41dadde368df9e0dcfef22d5f90926f" + }, + { + "path": "wordpress/wp-includes/js/dist/a11y.js", + "offset": 27624867, + "size": 5584, + "sha256": "33756647a940483f1c5d2cf3fe78e2503a22ab288a738dec5f9dcdde9cce3303" + }, + { + "path": "wordpress/wp-includes/js/dist/a11y.min.js", + "offset": 27630451, + "size": 2503, + "sha256": "e95c93f8f0e2434b17b200a8294c5f7c4b939aea63e6c0ec3dbb4dcf52cfe11e" + }, + { + "path": "wordpress/wp-includes/js/dist/annotations.js", + "offset": 27632954, + "size": 15493, + "sha256": "0a97c2dc13605b41e128dec0522fa6a60fd280c64d6f3a9bac9c89c17439e6de" + }, + { + "path": "wordpress/wp-includes/js/dist/annotations.min.js", + "offset": 27648447, + "size": 5753, + "sha256": "4a0f5e236aefb8638e02f6c71a507e801e030de3560605ca66f7c2bfd4ec5894" + }, + { + "path": "wordpress/wp-includes/js/dist/api-fetch.js", + "offset": 27654200, + "size": 17875, + "sha256": "432726b8850bfc66e1048fdba5765e1262242ca1312693409a784dc8e99dbee4" + }, + { + "path": "wordpress/wp-includes/js/dist/api-fetch.min.js", + "offset": 27672075, + "size": 6472, + "sha256": "9f24fcd9d972c7fa3ee796f266d677172fc6ed32b263b02b92d58f08860c61ce" + }, + { + "path": "wordpress/wp-includes/js/dist/autop.js", + "offset": 27678547, + "size": 9962, + "sha256": "955ace4d38d1721c70be231f62bdfa8711cb727c49e43125b7cfb95d34722792" + }, + { + "path": "wordpress/wp-includes/js/dist/autop.min.js", + "offset": 27688509, + "size": 5637, + "sha256": "494641ffb357d9aa2eb484b9d3ad3edc958aea152cbef8b677298b1d82e2f5d4" + }, + { + "path": "wordpress/wp-includes/js/dist/base-styles.js", + "offset": 27694146, + "size": 48, + "sha256": "091c46cfa799ea624006210039239e26f794cca091f8a1ddb45abd5f6de667ed" + }, + { + "path": "wordpress/wp-includes/js/dist/base-styles.min.js", + "offset": 27694194, + "size": 75, + "sha256": "a4a94d280cdb98dd04ec39202535f33d1ac90656953056dca3c969336dba0c8b" + }, + { + "path": "wordpress/wp-includes/js/dist/blob.js", + "offset": 27694269, + "size": 2355, + "sha256": "bbd8cd94ecb274948d6df4c349cc555d487e5d42b8324932602cff85aabb78ff" + }, + { + "path": "wordpress/wp-includes/js/dist/blob.min.js", + "offset": 27696624, + "size": 1188, + "sha256": "a25882ea92c9ad32e420b7cd7980095b3124d8c1a09107854d9ad552730ea06f" + }, + { + "path": "wordpress/wp-includes/js/dist/block-directory.js", + "offset": 27697812, + "size": 57006, + "sha256": "3f54cda0d46b8531c29d1e06d06efe13ee111e30858f1a17b0c6122dc6a1ff19" + }, + { + "path": "wordpress/wp-includes/js/dist/block-directory.min.js", + "offset": 27754818, + "size": 22482, + "sha256": "e55da248c04372bf34029b003e1990fb1096da1628c91df0b45a7e919422361e" + }, + { + "path": "wordpress/wp-includes/js/dist/block-editor.js", + "offset": 27777300, + "size": 2832320, + "sha256": "cc518d856bbbfe82686cd38bac00c05f346ea9b218114c2f4453caa73f91c507" + }, + { + "path": "wordpress/wp-includes/js/dist/block-editor.min.js", + "offset": 30609620, + "size": 1056002, + "sha256": "3c36ffe5c2514d8aa61b5b723e87f9a71baff81fba997271fef2348a3927e0bc" + }, + { + "path": "wordpress/wp-includes/js/dist/block-library.js", + "offset": 31665622, + "size": 2692845, + "sha256": "8399a6a446f09c59781154ce205daabe75a909a5ff70f980f33bbccd4f271ed8" + }, + { + "path": "wordpress/wp-includes/js/dist/block-library.min.js", + "offset": 34358467, + "size": 1143976, + "sha256": "2335971a5e1e1b65161525ec4ad88b0eccc492b722e6f13d99f4406b50c07967" + }, + { + "path": "wordpress/wp-includes/js/dist/block-serialization-default-parser.js", + "offset": 35502443, + "size": 6695, + "sha256": "324132ceb07668279dd3ce9f7fe10f2b833257288b9474bd2ea845139a255cfe" + }, + { + "path": "wordpress/wp-includes/js/dist/block-serialization-default-parser.min.js", + "offset": 35509138, + "size": 2451, + "sha256": "b9ec1694c9974965f4412ac6842670fad22c93e58f6bf1cd48db71c4c7a988e7" + }, + { + "path": "wordpress/wp-includes/js/dist/block-serialization-spec-parser.js", + "offset": 35511589, + "size": 51693, + "sha256": "ef33ac30a72200354bc8b7c05b54dedb8cc52a8240b5cab4e3ae913571d5f0c9" + }, + { + "path": "wordpress/wp-includes/js/dist/block-serialization-spec-parser.min.js", + "offset": 35563282, + "size": 10625, + "sha256": "666b27d67fd485f6d6f67e8f58d360df5dcf74cfeb3c2405e2996d5661954060" + }, + { + "path": "wordpress/wp-includes/js/dist/blocks.js", + "offset": 35573907, + "size": 390206, + "sha256": "dd44438079e8e1e360e14950c0632a612a409ee12ba397c3268b60af92bca201" + }, + { + "path": "wordpress/wp-includes/js/dist/blocks.min.js", + "offset": 35964113, + "size": 184952, + "sha256": "cfd4afe01ef445ff7c48e4f1e5d725b3f798f2a4caebebef058010c907304f9e" + }, + { + "path": "wordpress/wp-includes/js/dist/commands.js", + "offset": 36149065, + "size": 155346, + "sha256": "87c8d4568ab0202203947ae44d6c552643ab2bf4d01af6d25f07d7027a132ae5" + }, + { + "path": "wordpress/wp-includes/js/dist/commands.min.js", + "offset": 36304411, + "size": 64653, + "sha256": "a684ddc25a6c2e09fb5ef5fb2fc421b9b05ce6187b58db1c85786124824293e0" + }, + { + "path": "wordpress/wp-includes/js/dist/components.js", + "offset": 36369064, + "size": 4017464, + "sha256": "78109ff55fbb8f8872d2f714f391bf53a36aeaca2e019cd2436c32859a06df1f" + }, + { + "path": "wordpress/wp-includes/js/dist/components.min.js", + "offset": 40386528, + "size": 805148, + "sha256": "dfb0d402b372b73df634ccd16cc6b3fd15064861e47734b1fc5120aabd428b63" + }, + { + "path": "wordpress/wp-includes/js/dist/compose.js", + "offset": 41191676, + "size": 82688, + "sha256": "217560f3bdd45e83ddb65c5a6617ac97a7e36bb18a6c3c0b189c8befa4705fc4" + }, + { + "path": "wordpress/wp-includes/js/dist/compose.min.js", + "offset": 41274364, + "size": 28856, + "sha256": "8ae52f352e1c158857d3a5142c1998ba2cc437cefb3e2e6766158fa72ddc0889" + }, + { + "path": "wordpress/wp-includes/js/dist/core-commands.js", + "offset": 41303220, + "size": 29367, + "sha256": "955f258e1c42cae772bb42d7ff072dd61d6dbba17b4abd612f0a213a790712eb" + }, + { + "path": "wordpress/wp-includes/js/dist/core-commands.min.js", + "offset": 41332587, + "size": 12153, + "sha256": "9729ab547351272ced3d2e4853e8711a81eaa01741126ed3375adbf22ad44d28" + }, + { + "path": "wordpress/wp-includes/js/dist/core-data.js", + "offset": 41344740, + "size": 626718, + "sha256": "2a02fc28ac67e04d4267d3f6cbb48dc00fdde55a6768acb03b0d6c0728ae1e86" + }, + { + "path": "wordpress/wp-includes/js/dist/core-data.min.js", + "offset": 41971458, + "size": 215516, + "sha256": "e35ee8e6ee148d7b9fbab57a2398dba0c708e2e8490a7abff93d46dcb2ea8480" + }, + { + "path": "wordpress/wp-includes/js/dist/customize-widgets.js", + "offset": 42186974, + "size": 93770, + "sha256": "ed4e44c8dbf82959499572500bba1e30b7f0bb7c65a7272ceb27ba3c3d7a1306" + }, + { + "path": "wordpress/wp-includes/js/dist/customize-widgets.min.js", + "offset": 42280744, + "size": 37494, + "sha256": "1b4824ae6756fb6bad82c7a3fcca1cb919a5d92ccb0b162c5a390acea196927b" + }, + { + "path": "wordpress/wp-includes/js/dist/data-controls.js", + "offset": 42318238, + "size": 4230, + "sha256": "45b38f95fe266aa508736a53584cbcf2a8f21064af6ddcba9d4b4a36b769e718" + }, + { + "path": "wordpress/wp-includes/js/dist/data-controls.min.js", + "offset": 42322468, + "size": 1814, + "sha256": "ee567190760c2e4ed84f80a9b636986033d36afb727f6ef87910151fe5565aca" + }, + { + "path": "wordpress/wp-includes/js/dist/data.js", + "offset": 42324282, + "size": 84771, + "sha256": "0fcb6462b33d1de67241e38a5056ac48b6d8e4448958742f9fe73798e3bf911f" + }, + { + "path": "wordpress/wp-includes/js/dist/data.min.js", + "offset": 42409053, + "size": 26540, + "sha256": "424958a7b6aa77469b68af2b07bb033c53733c6a58b7cae7350a5c3826f3c57d" + }, + { + "path": "wordpress/wp-includes/js/dist/date.js", + "offset": 42435593, + "size": 180576, + "sha256": "3de33887bd1b2d8c35255a21011a220488adeac6744f4b2ed8894e400a54c7ed" + }, + { + "path": "wordpress/wp-includes/js/dist/date.min.js", + "offset": 42616169, + "size": 144608, + "sha256": "0c7f85eafdaf3f77064e9eceae3dd850f1a47597aff4f32a337f1d39d4f95ec0" + }, + { + "path": "wordpress/wp-includes/js/dist/deprecated.js", + "offset": 42760777, + "size": 3113, + "sha256": "e6a3f3faaa2851e33a4a562c432f0af784af0e28ed029c2dfda79a9bc7ca133e" + }, + { + "path": "wordpress/wp-includes/js/dist/deprecated.min.js", + "offset": 42763890, + "size": 1315, + "sha256": "0e57d83ac15f7bd131547cc06b08b3816860a14ddcd02f92cf31f60e176a7fd4" + }, + { + "path": "wordpress/wp-includes/js/dist/development/react-refresh-entry.js", + "offset": 42765205, + "size": 48144, + "sha256": "0c960b562db87d21c20e2c1d8758f1856c5e97c613c91f0669197e3160ece4d0" + }, + { + "path": "wordpress/wp-includes/js/dist/development/react-refresh-entry.min.js", + "offset": 42813349, + "size": 13567, + "sha256": "d58ef6228611faa2100e3b595af0bfc4184dc69f32ef36cc31c35cc560c53df8" + }, + { + "path": "wordpress/wp-includes/js/dist/development/react-refresh-runtime.js", + "offset": 42826916, + "size": 22760, + "sha256": "393be10fe5db345955699aa125044fc08510dd9757753564b4d69139b6447750" + }, + { + "path": "wordpress/wp-includes/js/dist/development/react-refresh-runtime.min.js", + "offset": 42849676, + "size": 5497, + "sha256": "6a5466c7f7cb9f5b1257fd41095bd71ce7f2f223767dbc38647f26f0bc26b4d4" + }, + { + "path": "wordpress/wp-includes/js/dist/dom-ready.js", + "offset": 42855173, + "size": 1508, + "sha256": "785423e1cf921cd12f563ebdb8da534c7a4f2373e1d7d98180d9c5619af58459" + }, + { + "path": "wordpress/wp-includes/js/dist/dom-ready.min.js", + "offset": 42856681, + "size": 818, + "sha256": "b603dcb13af1e3a58ec5df38685448b0e500e5d254b1e95891fca711875cea30" + }, + { + "path": "wordpress/wp-includes/js/dist/dom.js", + "offset": 42857499, + "size": 35445, + "sha256": "d3a438cbe7553b0e3fb88f93cfbbbf143901a6d735353b9ebd425bbd1c1eed24" + }, + { + "path": "wordpress/wp-includes/js/dist/dom.min.js", + "offset": 42892944, + "size": 12954, + "sha256": "832b0aa3850e7712b62c86717ed7973136655145832678d53ead4ece4fe5684c" + }, + { + "path": "wordpress/wp-includes/js/dist/edit-post.js", + "offset": 42905898, + "size": 120213, + "sha256": "1a7279e3d381560541360ef4d1c97b12e078fd7e6d572e82fbfd9cbfa3b3fe2d" + }, + { + "path": "wordpress/wp-includes/js/dist/edit-post.min.js", + "offset": 43026111, + "size": 50284, + "sha256": "5356ad28bbbb6a5a85e51415ce5b0dfc0b0f0621f6d5d6bde72350ae03accd01" + }, + { + "path": "wordpress/wp-includes/js/dist/edit-site.js", + "offset": 43076395, + "size": 1767456, + "sha256": "76b3989e66448f15403641bdfa165e3fff3d07285584511713dfad8c24b3b047" + }, + { + "path": "wordpress/wp-includes/js/dist/edit-site.min.js", + "offset": 44843851, + "size": 700880, + "sha256": "d435445a438101dc1b2da4125aefae9316d72a18018c534a227d376aa64809f6" + }, + { + "path": "wordpress/wp-includes/js/dist/edit-widgets.js", + "offset": 45544731, + "size": 164094, + "sha256": "4ef5f967e00d370e1114dc780645182148892eb9046c2773628240dd24c31743" + }, + { + "path": "wordpress/wp-includes/js/dist/edit-widgets.min.js", + "offset": 45708825, + "size": 63344, + "sha256": "3383e60a1d8a0645429eeb85a15a506f5f05df0bf84e13d691973b6af0cd06c9" + }, + { + "path": "wordpress/wp-includes/js/dist/editor.js", + "offset": 45772169, + "size": 2617702, + "sha256": "0c127ec49667de456d8d2e1ecae2860a374a8b4565914df6565abc0aeb74d286" + }, + { + "path": "wordpress/wp-includes/js/dist/editor.min.js", + "offset": 48389871, + "size": 1046150, + "sha256": "8067e9b55ea894d8cef230a5aac2e91028f9ecb3f64717d02875a0d9610cd766" + }, + { + "path": "wordpress/wp-includes/js/dist/element.js", + "offset": 49436021, + "size": 28602, + "sha256": "8715e58ad14091b2d8584585dace891994a6101b51e254f25f4f4250f1fffcab" + }, + { + "path": "wordpress/wp-includes/js/dist/element.min.js", + "offset": 49464623, + "size": 12458, + "sha256": "15b68344bed56bab72064899515af374832e2d2ea6fa6a996b7b847f4b78b300" + }, + { + "path": "wordpress/wp-includes/js/dist/escape-html.js", + "offset": 49477081, + "size": 2346, + "sha256": "510cf5018a83c12da889d89d694237c78f7dd687bee97398fef4b3cc605ab7d2" + }, + { + "path": "wordpress/wp-includes/js/dist/escape-html.min.js", + "offset": 49479427, + "size": 1091, + "sha256": "7bd739193c5075cca573d58fbceed30ba467e9213c6d240006b443dacd3edc12" + }, + { + "path": "wordpress/wp-includes/js/dist/format-library.js", + "offset": 49480518, + "size": 75099, + "sha256": "33675fd2e1c137e7ede14b9f37a3dbccb1de5b9efa3d76021a59b4202056cfd8" + }, + { + "path": "wordpress/wp-includes/js/dist/format-library.min.js", + "offset": 49555617, + "size": 29575, + "sha256": "53650a1afce6dea45013818f96cf229a8f623bf94ed6b472cad1f7bd500ea259" + }, + { + "path": "wordpress/wp-includes/js/dist/hooks.js", + "offset": 49585192, + "size": 12242, + "sha256": "8b05345b952f53e016e78d3c436e84ea1f86a2a5c3023f3984da949fd7c720f4" + }, + { + "path": "wordpress/wp-includes/js/dist/hooks.min.js", + "offset": 49597434, + "size": 5050, + "sha256": "cce388fed1a82ebd20ce7d9601e4b6c83a425ea201c333d71b1183ee887cc090" + }, + { + "path": "wordpress/wp-includes/js/dist/html-entities.js", + "offset": 49602484, + "size": 1685, + "sha256": "66e84db73030bb3271f4d599b685941bf2d50c6ed4f4e053f21f496554ff71ad" + }, + { + "path": "wordpress/wp-includes/js/dist/html-entities.min.js", + "offset": 49604169, + "size": 879, + "sha256": "1be4393bcf16b77497e1276556e3fad5901f8e9986e4582312b613ceeea8e656" + }, + { + "path": "wordpress/wp-includes/js/dist/i18n.js", + "offset": 49605048, + "size": 15670, + "sha256": "b304dec391ddb1cff0e1c31dd3dc15ee7bc51b4e606e7af95e6e14ec0548c8d9" + }, + { + "path": "wordpress/wp-includes/js/dist/i18n.min.js", + "offset": 49620718, + "size": 5732, + "sha256": "558ae0ec83cc88de0929fde86b87c231246aafa9c961ebffb3257b05f62ca5cf" + }, + { + "path": "wordpress/wp-includes/js/dist/is-shallow-equal.js", + "offset": 49626450, + "size": 2758, + "sha256": "94a3380cf2863ad5679f48b895b871c2d1348376233e981c6711341f6d9f485a" + }, + { + "path": "wordpress/wp-includes/js/dist/is-shallow-equal.min.js", + "offset": 49629208, + "size": 1122, + "sha256": "7c618f856c87442ad579fc5b7d498e509ea7ef99f28bbae07c608435b4dbcfc7" + }, + { + "path": "wordpress/wp-includes/js/dist/keyboard-shortcuts.js", + "offset": 49630330, + "size": 9913, + "sha256": "76c9d247f80a4abdccbbca57a045a07053716d350cdf4d309427b9611d4cf05a" + }, + { + "path": "wordpress/wp-includes/js/dist/keyboard-shortcuts.min.js", + "offset": 49640243, + "size": 3527, + "sha256": "566d8384540ccd4500d7b5cf14218afc6f1b6228318a8b7ef9d23632c6b95c33" + }, + { + "path": "wordpress/wp-includes/js/dist/keycodes.js", + "offset": 49643770, + "size": 8501, + "sha256": "1592838a003dc7d884f99ac2c30e5602faf40cd265440707e135b5f0d3b6e1c1" + }, + { + "path": "wordpress/wp-includes/js/dist/keycodes.min.js", + "offset": 49652271, + "size": 2975, + "sha256": "9a8ebc74edae86f3989fe760fea85c24d973d5d635c0c86f8394c893c9e7cbf1" + }, + { + "path": "wordpress/wp-includes/js/dist/list-reusable-blocks.js", + "offset": 49655246, + "size": 13374, + "sha256": "f26535d9ac2482c8989d15d68d1afe7e4d97e371fa81105174f9eb00a504fb12" + }, + { + "path": "wordpress/wp-includes/js/dist/list-reusable-blocks.min.js", + "offset": 49668620, + "size": 5367, + "sha256": "fa8f7b37aea86b8c74b26f0128166b81818884b1faa3b321154d988f3bb598ae" + }, + { + "path": "wordpress/wp-includes/js/dist/media-utils.js", + "offset": 49673987, + "size": 652438, + "sha256": "034a0e2a92e143dca9841cf79db3c787a90c2323303255faf175420c7e93e6e0" + }, + { + "path": "wordpress/wp-includes/js/dist/media-utils.min.js", + "offset": 50326425, + "size": 243343, + "sha256": "e634a61f81afc6f8f0f0b56ee385b3360ed4167cd1b1e16aa55c087b3956ccbf" + }, + { + "path": "wordpress/wp-includes/js/dist/notices.js", + "offset": 50569768, + "size": 10356, + "sha256": "54a5ce13a8bc2656ca422d6c70d077dc5ef4afb37bd132ca4987620954d47885" + }, + { + "path": "wordpress/wp-includes/js/dist/notices.min.js", + "offset": 50580124, + "size": 4489, + "sha256": "8533d376085eaae40dc8d822bb795e71c32ef9f27bf26194e5f359c04a2fb904" + }, + { + "path": "wordpress/wp-includes/js/dist/nux.js", + "offset": 50584613, + "size": 10572, + "sha256": "844fe932431eb925ea311adb21fe306d037080bf7a0ce51b754bb585bf3646c7" + }, + { + "path": "wordpress/wp-includes/js/dist/nux.min.js", + "offset": 50595185, + "size": 4048, + "sha256": "72d4c16af95de461c1755a617923709e8702d518bd91491e913c22eaff0b86c9" + }, + { + "path": "wordpress/wp-includes/js/dist/patterns.js", + "offset": 50599233, + "size": 59644, + "sha256": "aab8948bf4d7b5e3ecb7ae08d95d4ef63d0a91c2ca30d0fef58e71c28ca1fbd9" + }, + { + "path": "wordpress/wp-includes/js/dist/patterns.min.js", + "offset": 50658877, + "size": 22152, + "sha256": "1e169d8a5f3ad071bcb5b3925d8499f7c6bd5c17359594bdece5affbdbd80028" + }, + { + "path": "wordpress/wp-includes/js/dist/plugins.js", + "offset": 50681029, + "size": 11951, + "sha256": "869267d49636638e2e9da8f28dac8c6b268ea7e7c54f1e9be382cb9de66d9201" + }, + { + "path": "wordpress/wp-includes/js/dist/plugins.min.js", + "offset": 50692980, + "size": 4870, + "sha256": "0ba9c332cc0976910e5134b2f2fc4ea7eaddd5a9e6055e8c45bea12181b2f259" + }, + { + "path": "wordpress/wp-includes/js/dist/preferences-persistence.js", + "offset": 50697850, + "size": 17629, + "sha256": "09886beb616f11c197259c5dde9d45e83aef1c67cf797a202169511362007d01" + }, + { + "path": "wordpress/wp-includes/js/dist/preferences-persistence.min.js", + "offset": 50715479, + "size": 5622, + "sha256": "ef7999d88625a39677033ce7aa12f2ffa5200fd608d85982b14aa0e0a8a56999" + }, + { + "path": "wordpress/wp-includes/js/dist/preferences.js", + "offset": 50721101, + "size": 21574, + "sha256": "ec3331a6c013c1d5a13ead869cbbde3ba15c78af832acad01afb420b08c53fc7" + }, + { + "path": "wordpress/wp-includes/js/dist/preferences.min.js", + "offset": 50742675, + "size": 7899, + "sha256": "0cd5b9ce567ed0e779adc091b5cc9867afa88ed0bf05b2208d5ce87a8d73ff42" + }, + { + "path": "wordpress/wp-includes/js/dist/primitives.js", + "offset": 50750574, + "size": 5203, + "sha256": "2c6b78248c42a20eb0e3edbaacba16fc90eab9f8661b7c4f245d55f50020d8a0" + }, + { + "path": "wordpress/wp-includes/js/dist/primitives.min.js", + "offset": 50755777, + "size": 2026, + "sha256": "82618149c374168dc9fb883362bb76383ccda55bf49a7855766d100d3f04cae4" + }, + { + "path": "wordpress/wp-includes/js/dist/priority-queue.js", + "offset": 50757803, + "size": 10265, + "sha256": "78ebd2c38a47321bf3892fd0831c99daae54cec112608c7ed85a2bccdbcfb2d4" + }, + { + "path": "wordpress/wp-includes/js/dist/priority-queue.min.js", + "offset": 50768068, + "size": 3432, + "sha256": "414f5eff9217a7b6b056f786fcaf84cda805740d7057e1cc4d84bb0f666a62ba" + }, + { + "path": "wordpress/wp-includes/js/dist/private-apis.js", + "offset": 50771500, + "size": 4192, + "sha256": "27d996dc264a98f9cd6efefb615595e6f40ce7d77d91c9663da9e6068a42270c" + }, + { + "path": "wordpress/wp-includes/js/dist/private-apis.min.js", + "offset": 50775692, + "size": 2723, + "sha256": "afd32b69311c2138034ff64a8f190059e33d1caf907eef2c25b8d8d3a7ebe1b6" + }, + { + "path": "wordpress/wp-includes/js/dist/react-i18n.js", + "offset": 50778415, + "size": 3999, + "sha256": "c905f0b48abfb683bc8a7ccb0c08462542a4c5ddb41fa5cd29320ed1171d3488" + }, + { + "path": "wordpress/wp-includes/js/dist/react-i18n.min.js", + "offset": 50782414, + "size": 1562, + "sha256": "527adf276bdaa2326180338be1894f67feb0505c60701eccd824f5deafaaac2b" + }, + { + "path": "wordpress/wp-includes/js/dist/redux-routine.js", + "offset": 50783976, + "size": 25275, + "sha256": "f78f733d79dc6d248c89d5dd17dc0565877a72cfa28603488a9c7a1e9f4abde6" + }, + { + "path": "wordpress/wp-includes/js/dist/redux-routine.min.js", + "offset": 50809251, + "size": 9881, + "sha256": "085a1093eafd01de37cc52860243ec0b85a66c19634b5fe48d8b7e00a51a9025" + }, + { + "path": "wordpress/wp-includes/js/dist/reusable-blocks.js", + "offset": 50819132, + "size": 21004, + "sha256": "b05b3d005f18213e2d50b861b00478e7738e7336191908874e7246196c4638cd" + }, + { + "path": "wordpress/wp-includes/js/dist/reusable-blocks.min.js", + "offset": 50840136, + "size": 7396, + "sha256": "013b9780454065b7ffaa6c28f5587b52c930c4f13710170282ea81152966c1ce" + }, + { + "path": "wordpress/wp-includes/js/dist/rich-text.js", + "offset": 50847532, + "size": 99366, + "sha256": "3d4cab6655d87f52bc736d58ada908e1251604e0a46491dd202b5c5af71995b6" + }, + { + "path": "wordpress/wp-includes/js/dist/rich-text.min.js", + "offset": 50946898, + "size": 40847, + "sha256": "eb2e18277890ce4c77740b375eaae0ed31f8c86b7ab131aed1cf2dc3ea19e484" + }, + { + "path": "wordpress/wp-includes/js/dist/router.js", + "offset": 50987745, + "size": 36987, + "sha256": "176cb6ed07b47f8d6d0cb9403ad42479af35171d84dfefec3f9dc811b7529783" + }, + { + "path": "wordpress/wp-includes/js/dist/router.min.js", + "offset": 51024732, + "size": 14470, + "sha256": "aa0540a6c8e302887675cb1d3656a236dd177a8c838e1cd41483843a9b6f1843" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/a11y/index.js", + "offset": 51039202, + "size": 1346, + "sha256": "eb66745f6242c70f97c574560ee063a27d6a8382e9199b599b387967a1bedf09" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/a11y/index.min.js", + "offset": 51040548, + "size": 601, + "sha256": "ba5caa83c12c7a03f8610a8ae54ae560d5b6ad7278df823f420002bd65b685ed" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/abilities/index.js", + "offset": 51041149, + "size": 288362, + "sha256": "c340e93619a7ef084773e43edab9b04c7ce64feb108a7aacd878384696d9ed5f" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/abilities/index.min.js", + "offset": 51329511, + "size": 140747, + "sha256": "b06bc21283ffeef24e959e9d41fea4c34ef66f687ba11d4802cb17060e737900" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-editor/utils/fit-text-frontend.js", + "offset": 51470258, + "size": 3129, + "sha256": "443d212a1c7f0b6e47b7cdfc7bb8eba5c05e4cf36205ad56ad21b75bffeb36c7" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-editor/utils/fit-text-frontend.min.js", + "offset": 51473387, + "size": 1110, + "sha256": "43d6900a36175075238dc4305c4bda6f5591eb1274c2dc991b43d70a820643f6" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/accordion/view.js", + "offset": 51474497, + "size": 3590, + "sha256": "de20efbd9e2f362985482f3c71d8a105ea535cf502c99c1d32006d7c9f039ce7" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/accordion/view.min.js", + "offset": 51478087, + "size": 1433, + "sha256": "14e7e8104abe3d6d95e5749d6f7fae7384d46a3de552fbc70d5ae1f4be8b1156" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/file/view.js", + "offset": 51479520, + "size": 1111, + "sha256": "faf0b6fb697caf41af713b7852309c1b75d6c69c05efecd5186295facc8ec7d5" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/file/view.min.js", + "offset": 51480631, + "size": 543, + "sha256": "08f9ccc4328c4a82d4c127f9a9fdf3ca276ee5abe30f6f9ae6480a6ed1596404" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/form/view.js", + "offset": 51481174, + "size": 1450, + "sha256": "7a1cf5e8ca52f7cf530c6dfcff2ff4896d9bc9cd1ca3a7cf7852fde02406f381" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/form/view.min.js", + "offset": 51482624, + "size": 815, + "sha256": "eeceb5ffd045d34566a605f78a79107b6b07d87379dd734418154191e42b122e" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/image/view.js", + "offset": 51483439, + "size": 17199, + "sha256": "7f5b91531b71cbd0dacaf7b1beacbee96843b99e539a7fa4026652808dbb1d92" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/image/view.min.js", + "offset": 51500638, + "size": 7200, + "sha256": "dfec8281e2fc4fdaa973518efc7d1fb38e709c6e670fdb0dce34d18f3bf0340d" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/navigation/view.js", + "offset": 51507838, + "size": 5912, + "sha256": "57e7084c3e395b07e16a3b51a2d9a4154ce9fc49f20d2a7f8a68ddc6a58620c0" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/navigation/view.min.js", + "offset": 51513750, + "size": 3053, + "sha256": "caf7ad56bd767d2bb8c5b78dfd3c2c4e005e7d01abca7314fbaffed99c7df69d" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/playlist/view.js", + "offset": 51516803, + "size": 1698, + "sha256": "60d36a0b8bd5de0bb38c07be872df76161b2406f911e3f9efaee0a51a35cb6e1" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/playlist/view.min.js", + "offset": 51518501, + "size": 701, + "sha256": "cb8ce5877391c7a96af9a7ae794405bb933721e63178d06a99a7e91e2553d499" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/query/view.js", + "offset": 51519202, + "size": 1770, + "sha256": "a1791791f4c1ca097109f87b3f715100e1676e8a4cc69231c3bc3d5148f0ba32" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/query/view.min.js", + "offset": 51520972, + "size": 929, + "sha256": "0887a114aee0754997e81959b762c9fb5f1a6bd43c84c2f7767e4674e62e0db4" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/search/view.js", + "offset": 51521901, + "size": 1857, + "sha256": "3c68e873452f842a0c7e32fbd23052eb5a17fc109052793b30336c0f43afe9ac" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/search/view.min.js", + "offset": 51523758, + "size": 957, + "sha256": "03f9888c8ad61c6571d33bb0e9b4505c5e191cc4612b98e79dd5af4caf3cef53" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/tabs/view.js", + "offset": 51524715, + "size": 7016, + "sha256": "fd687d510a450cdfe8a4188afb6e98cec9f7223a949da3ddebdd0785ac305791" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/block-library/tabs/view.min.js", + "offset": 51531731, + "size": 1968, + "sha256": "b534a245b0c7d3a9227df69b016956878f0a48ca91edb79d51cffe18807e5770" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/boot/index.js", + "offset": 51533699, + "size": 114208, + "sha256": "a812d2253d5f324a8ec512cc0bebd61f0ea6f6bc6b3029f5a8a10d02d99f3293" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/boot/index.min.js", + "offset": 51647907, + "size": 63495, + "sha256": "8df8c410edd7757c59b43f15ea210fa5eb4e236bbeb0c32d191e266747a66fe1" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/connectors/index.js", + "offset": 51711402, + "size": 10779, + "sha256": "bc0c5b6774fb90eff96bbe2fc286f91b39e3220f802528e937703d4b2ea5fc2f" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/connectors/index.min.js", + "offset": 51722181, + "size": 4598, + "sha256": "cfb2a2f65b8c258681b3377e851ff8714f266d8b90748d7d3a651b463c89fd57" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/core-abilities/index.js", + "offset": 51726779, + "size": 4266, + "sha256": "82c41e52aa1f70161a97700f5b7ffaf1fb4ee18c043e24139e51b46f2d1dd2ca" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/core-abilities/index.min.js", + "offset": 51731045, + "size": 1821, + "sha256": "5e926b12f9f644574c2aa957fbb2974eff63fc78303442f47259e5ef888344a0" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/edit-site-init/index.js", + "offset": 51732866, + "size": 8694, + "sha256": "792fe2ad5697f0161c7a53892998af452ca63a40ec2e39740455f51f00001fc5" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/edit-site-init/index.min.js", + "offset": 51741560, + "size": 4798, + "sha256": "a8edfd04610704f8668e6b665024afb7d510695735102877e858acd3fd852550" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity-router/full-page.js", + "offset": 51746358, + "size": 1292, + "sha256": "62e9d3cf0ca081213953c4312d0f32d1e3b221180ea988c02be39b727bff9cff" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity-router/full-page.min.js", + "offset": 51747650, + "size": 784, + "sha256": "db433e952c975cf494fa0e9ccdb6cc876e55e4cca1327f569c899eb51814761b" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity-router/index.js", + "offset": 51748434, + "size": 44424, + "sha256": "f25ddd6fa43bf6aaf826dc392de2e78308f5c16567e64196b35d1fe6bab2d778" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity-router/index.min.js", + "offset": 51792858, + "size": 25664, + "sha256": "80390a937ea037b0237aec262caaf29cecaad593c0979c7cbe2422a5e07ddc6d" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity/index.js", + "offset": 51818522, + "size": 104425, + "sha256": "fe0aea8a093886115c9aeebaf0654c13de998bad1607a61788394c6bc999342d" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/interactivity/index.min.js", + "offset": 51922947, + "size": 40106, + "sha256": "726ac59577c8aeeec94c61bfb7791a709e401b5d871d35c39a65cd948f6124e3" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/latex-to-mathml/index.js", + "offset": 51963053, + "size": 373781, + "sha256": "b6b42a8c738cae6808b90c640f10d4427b800086aef4ae9a9970b70319c190e5" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/latex-to-mathml/index.min.js", + "offset": 52336834, + "size": 202736, + "sha256": "e081106603db3374a0eda08f2e1b0e1eec21e8b1d9331190a110aec957d6f3dd" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/latex-to-mathml/loader.js", + "offset": 52539570, + "size": 153, + "sha256": "413435138460fab93fc7487bbf014d5d92631a243c41864cf729c220c4ef9763" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/latex-to-mathml/loader.min.js", + "offset": 52539723, + "size": 79, + "sha256": "f58bb5f435ae53a73d1e2d82bf1a3163827a52cf61fdc4e52e53a48b5e898668" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/lazy-editor/index.js", + "offset": 52539802, + "size": 89865, + "sha256": "839a2598261068d7c8127e770d5a14172f70902abfff435fd01bf7ca1737a400" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/lazy-editor/index.min.js", + "offset": 52629667, + "size": 37678, + "sha256": "063e60fe791efa602d913269ae4d4d74ac6f2593ee21cdc1915c7611f8dd935a" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/route/index.js", + "offset": 52667345, + "size": 181654, + "sha256": "b70fdf3aba32e2ca5f131f67540035e28071b9bae2dd82ca1157a85e11cf4ba5" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/route/index.min.js", + "offset": 52848999, + "size": 74994, + "sha256": "273b6b21956cb3f17a2b9134b442539034474255d6186aee0233a7e2cf0a5d7e" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/workflow/index.js", + "offset": 52923993, + "size": 139980, + "sha256": "eca3aa9abdfe73dec1190a91e5ac95c5978700c19663ebb42ae4c1dc6c5334e4" + }, + { + "path": "wordpress/wp-includes/js/dist/script-modules/workflow/index.min.js", + "offset": 53063973, + "size": 63576, + "sha256": "618cf81ae9723a6aa85fb4b2e66e01b8b4ffc4c4934cba0acb0be9f73a5e6c43" + }, + { + "path": "wordpress/wp-includes/js/dist/server-side-render.js", + "offset": 53127549, + "size": 11231, + "sha256": "88b60f4f0c72bb6dff6008407fa70010108925eb3b4174d2848696ed720b9d55" + }, + { + "path": "wordpress/wp-includes/js/dist/server-side-render.min.js", + "offset": 53138780, + "size": 4035, + "sha256": "8ea74229d8da35cab616e0ba4e06ba0e0a154eabd5c5449a948d9954ebdf5290" + }, + { + "path": "wordpress/wp-includes/js/dist/shortcode.js", + "offset": 53142815, + "size": 8150, + "sha256": "7bc1b948e3bff5e9165a6de9a33305f7351a262b539801a228667de9a7d85eec" + }, + { + "path": "wordpress/wp-includes/js/dist/shortcode.min.js", + "offset": 53150965, + "size": 3361, + "sha256": "3a624c4550700f734185e9620a5c868dbf4fce2a6cea520b50bf01cc3d98520c" + }, + { + "path": "wordpress/wp-includes/js/dist/style-engine.js", + "offset": 53154326, + "size": 18040, + "sha256": "640fd5bfdbe9afc801891f8157812baf2cd00f8d420f4dac7e6a494b4f630181" + }, + { + "path": "wordpress/wp-includes/js/dist/style-engine.min.js", + "offset": 53172366, + "size": 6569, + "sha256": "8f3599ce9e95f7623be002554dd632e0191d7fe3b761cf5e58e86c5b0c68af13" + }, + { + "path": "wordpress/wp-includes/js/dist/theme.js", + "offset": 53178935, + "size": 130711, + "sha256": "9e874cf0053f063558b517b4bdb24525cdac2c31cf92254232fd948384e2521b" + }, + { + "path": "wordpress/wp-includes/js/dist/theme.min.js", + "offset": 53309646, + "size": 57465, + "sha256": "05a6f130f01afdbac628112bf541bc82ae4d764539ca4709a598b9d5befcf9d9" + }, + { + "path": "wordpress/wp-includes/js/dist/token-list.js", + "offset": 53367111, + "size": 6262, + "sha256": "0768f7821c6519d244cd0bfc43f167882d930ce79fbc204d32fefc7a974b119f" + }, + { + "path": "wordpress/wp-includes/js/dist/token-list.min.js", + "offset": 53373373, + "size": 1658, + "sha256": "873b5047f4916c6d8d7e387f88be8e94a321b04c1a41acde960dd061f2d4fbc3" + }, + { + "path": "wordpress/wp-includes/js/dist/undo-manager.js", + "offset": 53375031, + "size": 5422, + "sha256": "30d73532d15e41dac7063248061c1ddc610d3dfebc894aaf0f7cccca08445fd7" + }, + { + "path": "wordpress/wp-includes/js/dist/undo-manager.min.js", + "offset": 53380453, + "size": 1739, + "sha256": "32c218f42001a898a6bd36ef78d859110aeda601fd347b5a19f47575585979fa" + }, + { + "path": "wordpress/wp-includes/js/dist/upload-media.js", + "offset": 53382192, + "size": 57002, + "sha256": "0c2a24a62800fb9f5930d21bd2de0e64cf519e9dbc40b66e97eaf5c8cfc4d6b3" + }, + { + "path": "wordpress/wp-includes/js/dist/upload-media.min.js", + "offset": 53439194, + "size": 23606, + "sha256": "0aab63b3583e59647acd3461848648719ae0695dd6bbca18148354c00a8ad1ae" + }, + { + "path": "wordpress/wp-includes/js/dist/url.js", + "offset": 53462800, + "size": 23951, + "sha256": "f0bfe4bd57bf697a4069536915cb9a8ac2016eea60008df4b1242928f5d6a6cc" + }, + { + "path": "wordpress/wp-includes/js/dist/url.min.js", + "offset": 53486751, + "size": 10370, + "sha256": "3b6961f564bd4de3c1d63bb56d2360efda476bbde17d8470f6c0a4aea24c30f4" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/lodash.js", + "offset": 53497121, + "size": 545945, + "sha256": "f5465f55566bf544aad0a31c6135889ca1ed81eea8f53ec61c6cbe86926f07cf" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/lodash.min.js", + "offset": 54043066, + "size": 73234, + "sha256": "a8d7e6291ad80256f976ace90824a71018d2f706992c9107b20bdced97bee27b" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/moment.js", + "offset": 54116300, + "size": 176435, + "sha256": "7dc0a51c32dae143f2eade235145dfd6a7756388c0f0bf409fa373dd6c233629" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/moment.min.js", + "offset": 54292735, + "size": 58812, + "sha256": "74dda5bd9aa7fd915989733552449304255eff518a5191050674bb72687d6a30" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/react-dom.js", + "offset": 54351547, + "size": 1080227, + "sha256": "f9044a5e9c39db8bb1a204dff924e526ec0a621e695bb69de1035811be8709e4" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/react-dom.min.js", + "offset": 55431774, + "size": 131835, + "sha256": "35f4f974f4b2bcd44da73963347f8952e341f83909e4498227d4e26b98f66f0d" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/react-jsx-runtime.js", + "offset": 55563609, + "size": 39605, + "sha256": "46a5517e634882a7db796c7f9759cbf96a1841b5964749acf9df8495229e2266" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/react-jsx-runtime.min.js", + "offset": 55603214, + "size": 1137, + "sha256": "086c56ce48d2e3c5884a54e3c1aa8c65d2c595f0cd634716eda2b91066600bda" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/react.js", + "offset": 55604351, + "size": 109931, + "sha256": "28348fef6cb0ed8b2ceeb22deaf824428fd13875d84c73d38f77dd216fc24e7f" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/react.min.js", + "offset": 55714282, + "size": 10751, + "sha256": "d949f1c3687aedadcedac85261865f29b17cd273997e7f6b2bfc53b2f9d4c4dd" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/regenerator-runtime.js", + "offset": 55725033, + "size": 25307, + "sha256": "7beda998b3fd277f3548e12b277bafc3037d243421f007032d38dffee412224c" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/regenerator-runtime.min.js", + "offset": 55750340, + "size": 6638, + "sha256": "7a06028bb46ece97d8a52d267b615d8c8f93af705a3d64fc588a090a90192e6e" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-dom-rect.js", + "offset": 55756978, + "size": 1927, + "sha256": "1054afbaaa4ffdaf992d7c9714284752e0adfadcf5734b86154fe38301d3ccf4" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-dom-rect.min.js", + "offset": 55758905, + "size": 891, + "sha256": "c4f4b31370136e696cba3dca994e2b5faca1f34d30a92692623754bc4930f5eb" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-element-closest.js", + "offset": 55759796, + "size": 428, + "sha256": "386770ed7f2a31abb6f543e8d9b765ca629bc121062ac427999bfc054638b025" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-element-closest.min.js", + "offset": 55760224, + "size": 428, + "sha256": "386770ed7f2a31abb6f543e8d9b765ca629bc121062ac427999bfc054638b025" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-fetch.js", + "offset": 55760652, + "size": 19815, + "sha256": "8c006dece7b95b63787c3ae297593c6bad9386cb8054697d18104177a5f52c87" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-fetch.min.js", + "offset": 55780467, + "size": 10013, + "sha256": "0517b25f40a63f71d85d808e87b7751c4bc1bf60145aee37cb2f1a1ecefde882" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-formdata.js", + "offset": 55790480, + "size": 11874, + "sha256": "a5dc0fe8f78d02ddf6554e75bab527612c047b80610128fa721287f71187fd7d" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-formdata.min.js", + "offset": 55802354, + "size": 8890, + "sha256": "697a9732b7e7c2ea771298fe0020dd80797b280a3ce528a5d3044c89f891f1d4" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-inert.js", + "offset": 55811244, + "size": 30222, + "sha256": "d197ac5500e41336b0e47a60fb77395adba7bafc6a672e59a7757ecb1f8bbda6" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-inert.min.js", + "offset": 55841466, + "size": 8190, + "sha256": "71958c7deff4f4e21163f27f0129e5a3101344bf7a9b8a4becf688022465c549" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-node-contains.js", + "offset": 55849656, + "size": 643, + "sha256": "b8abf30b51c569f9a95db8b9f449698543c9ef22d6fcfda25f507587a35d12ee" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-node-contains.min.js", + "offset": 55850299, + "size": 353, + "sha256": "ce97c401e13076dfe4a5e4146feaccc1e53f46ca9d21d1e8896966bad664c96c" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-object-fit.js", + "offset": 55850652, + "size": 9185, + "sha256": "16e0a30adc1c52bf2b5f61aa07ab7864c47e31bdc532b5af997d49b378fe3400" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-object-fit.min.js", + "offset": 55859837, + "size": 2981, + "sha256": "d34e48a3786e41d32a4226c46d08a8a86d7a77aa56d49dbd74615d83106e32cf" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-url.js", + "offset": 55862818, + "size": 110332, + "sha256": "2e8c34f7be8bef8ba8aa1e26a3a9f63eb1076a400eb2165deedd35249e2716dc" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill-url.min.js", + "offset": 55973150, + "size": 47085, + "sha256": "4ae8650ae71d9defb388bc959bda1b6a94999b034bb4fdcd5ce83828bbec9350" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill.js", + "offset": 56020235, + "size": 108672, + "sha256": "4f421075cd365dfd7525b15fe1da730bba9e5a17b1b83ad1384252eddc347808" + }, + { + "path": "wordpress/wp-includes/js/dist/vendor/wp-polyfill.min.js", + "offset": 56128907, + "size": 33744, + "sha256": "9a213bc9f56e363cd2ac88b8e751dac1acf8039d6ffd542535f849cb2b2f971c" + }, + { + "path": "wordpress/wp-includes/js/dist/viewport.js", + "offset": 56162651, + "size": 6765, + "sha256": "9492c8770020548d0161badb93f27c26c133ae72a54f1786cdea96299bac7f39" + }, + { + "path": "wordpress/wp-includes/js/dist/viewport.min.js", + "offset": 56169416, + "size": 2297, + "sha256": "51cbef660f0afe9fed8fb650ca680f245cb7cc11479ddb634174bcd9d498e3ac" + }, + { + "path": "wordpress/wp-includes/js/dist/warning.js", + "offset": 56171713, + "size": 1559, + "sha256": "97970b7bc034a2931916d019c799a0d272be2f2c0cc9d44f7e5ed00b38e67edc" + }, + { + "path": "wordpress/wp-includes/js/dist/warning.min.js", + "offset": 56173272, + "size": 755, + "sha256": "c8dc0a39613191874ce15d827c9d9a8d73c34bda16e2006d6b5e52afb1ae7386" + }, + { + "path": "wordpress/wp-includes/js/dist/widgets.js", + "offset": 56174027, + "size": 51282, + "sha256": "7380d6c43795d2762658d436f730839cac20b52e2728586498d9d4c7f75857c8" + }, + { + "path": "wordpress/wp-includes/js/dist/widgets.min.js", + "offset": 56225309, + "size": 21327, + "sha256": "edaa299e93883390165d0e2588b25a7bfac55aa44be8f542cdc577f2e4b4e0c6" + }, + { + "path": "wordpress/wp-includes/js/dist/wordcount.js", + "offset": 56246636, + "size": 7101, + "sha256": "92c66d84ba7dea3cf9fae0abbcdee38bafec8185ae70018543cb1364f69b23ea" + }, + { + "path": "wordpress/wp-includes/js/dist/wordcount.min.js", + "offset": 56253737, + "size": 2505, + "sha256": "c862b12a62ac4d46c7d1da946261b34aa338f97626dfe324b6383e99ed259740" + }, + { + "path": "wordpress/wp-includes/js/heartbeat.js", + "offset": 56256242, + "size": 24052, + "sha256": "ba7e1f3a28b3ecc38a76b07bb7f32f3118f9a3d23353b520e705d4dd2f4621ad" + }, + { + "path": "wordpress/wp-includes/js/heartbeat.min.js", + "offset": 56280294, + "size": 5947, + "sha256": "a5f83cd1d67cfb064597a5420fe2964401bcf3f7eaef0d6aa57fb4f3e1eea910" + }, + { + "path": "wordpress/wp-includes/js/hoverIntent.js", + "offset": 56286241, + "size": 7225, + "sha256": "fa8784da9303367c522ef462ca7ae8c30b92c30c9437fdcf1c39a4d57825f282" + }, + { + "path": "wordpress/wp-includes/js/hoverIntent.min.js", + "offset": 56293466, + "size": 1499, + "sha256": "ed5b5df9ceacfe76857ac51964972b0b417a215b2f50e837fd6b64bad7339c40" + }, + { + "path": "wordpress/wp-includes/js/hoverintent-js.min.js", + "offset": 56294965, + "size": 1718, + "sha256": "443feed322344a434640a69836e166337b3ca74b23b3e5f2709743d6511cf015" + }, + { + "path": "wordpress/wp-includes/js/imagesloaded.min.js", + "offset": 56296683, + "size": 5520, + "sha256": "b65b3de1bc923b9355248a0d941a0eaee15dfb9a6b8eadb51323a8df6189dcd1" + }, + { + "path": "wordpress/wp-includes/js/imgareaselect/border-anim-h.gif", + "offset": 56302203, + "size": 178, + "sha256": "7c7dc4bd1ba3ba750b78dee15096a3d3428718d9e728818c811d84e7560705e8" + }, + { + "path": "wordpress/wp-includes/js/imgareaselect/border-anim-v.gif", + "offset": 56302381, + "size": 178, + "sha256": "3213bbc3d254db6ce61d696f7975876adf0842d36b384c62eeab1e1bf4542fdf" + }, + { + "path": "wordpress/wp-includes/js/imgareaselect/imgareaselect.css", + "offset": 56302559, + "size": 790, + "sha256": "55a36298517619f755ac3c59b3c37cde07d3c2ce66526bf42df296bda945838c" + }, + { + "path": "wordpress/wp-includes/js/imgareaselect/jquery.imgareaselect.js", + "offset": 56303349, + "size": 38637, + "sha256": "d269aa84fb9027b1760bf47d99ea87d8e9b07c45d5e8c9152973aa1f1337d970" + }, + { + "path": "wordpress/wp-includes/js/imgareaselect/jquery.imgareaselect.min.js", + "offset": 56341986, + "size": 9770, + "sha256": "4e3ac656300b0a7e435b05a6b017bb8d030445db230030a904bfbbbd8a0bfaff" + }, + { + "path": "wordpress/wp-includes/js/jcrop/Jcrop.gif", + "offset": 56351756, + "size": 323, + "sha256": "8982fa425633fec44ebad6516869c14cebd96b776c307e3a35964c77e6731bed" + }, + { + "path": "wordpress/wp-includes/js/jcrop/jquery.Jcrop.min.css", + "offset": 56352079, + "size": 2068, + "sha256": "8a30154971b879596ecb8ef1dd228ae4e02faab22603438221befdb873bf33d6" + }, + { + "path": "wordpress/wp-includes/js/jcrop/jquery.Jcrop.min.js", + "offset": 56354147, + "size": 22585, + "sha256": "cd412f92a9ac171d52128b6fa8bd0a77c46cd044aef2c2c09266e2089733ccac" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery-migrate.js", + "offset": 56376732, + "size": 31978, + "sha256": "09f417c2e643b736c19e96b99e166681af1002e9b192b84e4e85b0794e764f7f" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery-migrate.min.js", + "offset": 56408710, + "size": 13577, + "sha256": "5274f11e6fb32ae0cf2dfb9f8043272865c397a7c4223b4cfa7d50ea52fbde89" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.color.min.js", + "offset": 56422287, + "size": 6645, + "sha256": "29f9f1c0e5771615cdec0ff6f130adaac968e5f452db77313b95dcc553b9c1ef" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.form.js", + "offset": 56428932, + "size": 41911, + "sha256": "8e98c3b32ddbaaf2bc59655747927edf44b1d5694fef89e6b4d41cf66d6a052b" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.form.min.js", + "offset": 56470843, + "size": 15977, + "sha256": "dc869996cbc8f47cab9aeb9523f81a7f420207a2601cce9ba45e9b7e0e261452" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.hotkeys.js", + "offset": 56486820, + "size": 5617, + "sha256": "e72589e06671375ca6a85c07441fdd2a731d6acf955a3ffa60774cf7e5f36295" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.hotkeys.min.js", + "offset": 56492437, + "size": 1793, + "sha256": "f5eaf5f6641aea33117e26ca1e110b1a3c0fc9d79bbdad67f745d60b71979160" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.js", + "offset": 56494230, + "size": 285334, + "sha256": "1e7f83052e1e3442c4397ced9555033cd1d3f08444d85960683bcf91c8433cdb" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.masonry.min.js", + "offset": 56779564, + "size": 1819, + "sha256": "c2e606e1fc82ea3a554aad5d0520e25d2677b89a891dc5c49e7ace08fce92e25" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.min.js", + "offset": 56781383, + "size": 87553, + "sha256": "cb6f2d32c49d1c2b25e9ffc9aaafa3f83075346c01bcd4ae6eb187392a4292cf" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.query.js", + "offset": 56868936, + "size": 3694, + "sha256": "d67e44159482fe8f3fff8ce93dcfa2cff2407f2fa1f3296dacf1595cc26692ac" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.schedule.js", + "offset": 56872630, + "size": 3457, + "sha256": "2cc108de09e7c499aeb9a9976c5beb7ac291cc34754b946ac00251d16aac95a4" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.serialize-object.js", + "offset": 56876087, + "size": 769, + "sha256": "6879fbda93174f19926cf0409f828450b4035740384cb1c46462becf3aa5367d" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.table-hotkeys.js", + "offset": 56876856, + "size": 3752, + "sha256": "e9882bc706ec306b9dbac14d3d56c17c17ddad319d14a348b0def1f7a0fda36a" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.table-hotkeys.min.js", + "offset": 56880608, + "size": 2295, + "sha256": "f5aa66bb7b44086c2a4aef578cc823e49dbde5fabb0c5cebb507c13905f87a75" + }, + { + "path": "wordpress/wp-includes/js/jquery/jquery.ui.touch-punch.js", + "offset": 56882903, + "size": 1179, + "sha256": "196bed4faf0fe38b89a496b1f41319b2a8077263f85819f8ad42933e0a2e2e52" + }, + { + "path": "wordpress/wp-includes/js/jquery/suggest.js", + "offset": 56884082, + "size": 6991, + "sha256": "8ddc5dd7e71e61db7caa6a28a1255800329d44c57631d9c5e8e0d40a5d66fe72" + }, + { + "path": "wordpress/wp-includes/js/jquery/suggest.min.js", + "offset": 56891073, + "size": 2993, + "sha256": "9b7e574146adb2e34f5d1210cea786679805a648a73ac03ddab6fefaec903de3" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/accordion.js", + "offset": 56894066, + "size": 16138, + "sha256": "c0a0455e47203df9ab5d067782272240c2185130220c616bc55a28c91699578a" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/accordion.min.js", + "offset": 56910204, + "size": 8860, + "sha256": "af349cb4f6cb39c65dcc5ee5468e4d4e7ecf3687387a856e38058ac8e68f1c04" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/autocomplete.js", + "offset": 56919064, + "size": 17530, + "sha256": "3d5733cbc39c03fcbd6010238ebf90e58452d11467f25661082c12628759f3b9" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/autocomplete.min.js", + "offset": 56936594, + "size": 8536, + "sha256": "3ab937ae57ef4a48601774b6a0cc701482a5c8bc126fcb677db4842b3a3c0003" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/button.js", + "offset": 56945130, + "size": 11706, + "sha256": "7346746435ec9f450c7e55245958e35193c8b73e56094ca5a7b13f6e10041384" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/button.min.js", + "offset": 56956836, + "size": 6154, + "sha256": "e72bdc75b51cc1bf733c75a20b62227961570815d34cd59d16bb752e19562a66" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/checkboxradio.js", + "offset": 56962990, + "size": 7589, + "sha256": "825f572edc175945c7e3c656f100539c04cb2766bb68bd9da38e83ff3c274797" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/checkboxradio.min.js", + "offset": 56970579, + "size": 4348, + "sha256": "75eb6f3d0dd7b56908fe1089a296589aeeddf66f854d5917474adeaf23137162" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/controlgroup.js", + "offset": 56974927, + "size": 8626, + "sha256": "ad2003de4142edcaa7d67bdaa9997cf2f073593bd6c94f638fbb8a18fb3543db" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/controlgroup.min.js", + "offset": 56983553, + "size": 4402, + "sha256": "1122f338bcc480927c422c0264769cde73c8c8ed0df6133d1cefd01af1c64926" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/core.js", + "offset": 56987955, + "size": 49888, + "sha256": "16b200f73cc5cf6bc342576b15b06369bb3979784bd53b46854e1a617cb24bfb" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/core.min.js", + "offset": 57037843, + "size": 21464, + "sha256": "699210a5ed06e497b4730ec83bb65ac4c2269ae4a0ee8af3f24aae7ee5b66b76" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/datepicker.js", + "offset": 57059307, + "size": 82523, + "sha256": "e72ab820991d4bbda45d80bc4b5edeb0d5a7275a863e27d83938dc0df34857db" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/datepicker.min.js", + "offset": 57141830, + "size": 36748, + "sha256": "2efe1ab1416a823fc89a7ce3001fa02d6038db18a9b730f16d1a8158e9bddf3f" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/dialog.js", + "offset": 57178578, + "size": 23896, + "sha256": "b718fbd2597beb7aa05802d97b9232bde6f2a90203032c5cf74489bc8ce1d80c" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/dialog.min.js", + "offset": 57202474, + "size": 13098, + "sha256": "63b9235c0294b04f20feb95db9650a048c1033a27dd880b36c47d9c50ecdd9c3" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/draggable.js", + "offset": 57215572, + "size": 35546, + "sha256": "c1135a4e533486be8ff6388c7ee516426d0dd7dbeb98ddcf7398e37c8c4166c6" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/draggable.min.js", + "offset": 57251118, + "size": 18420, + "sha256": "3507312c8d17b4ba1c93b83b0807b2aca82a89cf0f9d38fad1c6bcfba98437cc" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/droppable.js", + "offset": 57269538, + "size": 12899, + "sha256": "7cd1932545522347ef217bcbc7774c4c03553b0cb6369f2b9f9b32b5a717e5ce" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/droppable.min.js", + "offset": 57282437, + "size": 6665, + "sha256": "485d1c8198017b4cc89e59255af5d8542865dbc4f5ed50b01f31afdb030ba439" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-blind.js", + "offset": 57289102, + "size": 1644, + "sha256": "286df5e7b790c73738e3ac100968ef06e5a15168ef1fa18fdd3f0f971285ff90" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-blind.min.js", + "offset": 57290746, + "size": 880, + "sha256": "2a8cfec8c79789ee642bf0021fb2b00d5b5cdb2ab9cd2b81f325ce4037481322" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-bounce.js", + "offset": 57291626, + "size": 2660, + "sha256": "e5028e880cf725e392c39f26e2f271f51a91d383978174dfe294fe451871eabd" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-bounce.min.js", + "offset": 57294286, + "size": 991, + "sha256": "3869924d2d3801ea0066e1827f1ded7e6cf95694f771dc69cd041202a49cd6b3" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-clip.js", + "offset": 57295277, + "size": 1577, + "sha256": "eb06ad280915bb1239b83941a8d79466316cd426012ec325d4a2ccc401259288" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-clip.min.js", + "offset": 57296854, + "size": 796, + "sha256": "cd4baa40f8e289cad4d242966ab03f7d9450c08fa7f81bd74193094c20520827" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-drop.js", + "offset": 57297650, + "size": 1595, + "sha256": "ec09349be1ff785a2517f24dda568705b488ba890ad210d8aa76ceb03a19367c" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-drop.min.js", + "offset": 57299245, + "size": 753, + "sha256": "998c907ff28ab2440868816edf736766e878d222302f22f26f7e486d6c82e9e4" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-explode.js", + "offset": 57299998, + "size": 2924, + "sha256": "46f3c1bed4f4ca38963b6023b90fc0cf76cd1e5db28c21aff9e6f5335cbe9c70" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-explode.min.js", + "offset": 57302922, + "size": 1122, + "sha256": "e6e8945ed66ea76f79e686f5999ff7e0e807b60c1eef867353eed4bc6391e2f8" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-fade.js", + "offset": 57304044, + "size": 968, + "sha256": "18d47818814cf5843e680b9dcb1eff3eff9caa062ccb570d1535095188d7b39d" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-fade.min.js", + "offset": 57305012, + "size": 525, + "sha256": "26604ca868d6ee6df65a1d0cf2df3d14c0be0ec7b9c8529a630f357db6ce314e" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-fold.js", + "offset": 57305537, + "size": 2185, + "sha256": "b8dd3e322db05827d3dc5261ab5930538a831887f2c48ba31f6a4c3fec89b22c" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-fold.min.js", + "offset": 57307722, + "size": 1020, + "sha256": "af0d0bad3510db008ac0500b4ca867289f1c93429ae4302a6ad6f8882f416bd3" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-highlight.js", + "offset": 57308742, + "size": 1243, + "sha256": "acca188c7c865755fb76ceb119e5f89be3a9e087efcb1696f25e4adfe5559f4b" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-highlight.min.js", + "offset": 57309985, + "size": 648, + "sha256": "ef0db6421b0c850dc9b41f38b68b1aaa961719e9c95a160e7f763b0a3be03419" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-puff.js", + "offset": 57310633, + "size": 995, + "sha256": "c9f61e2a98085fc911966b4382b21c5e315bd472b2ac95776f085d21a55c32a9" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-puff.min.js", + "offset": 57311628, + "size": 510, + "sha256": "0a25b15547741fef40792b95e78f96d1852ec441af59676a1b4c5891bd4a224a" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-pulsate.js", + "offset": 57312138, + "size": 1567, + "sha256": "1db5831a8f972eb724fc3ba7eb6410d94693a15d538174535b2e692ac9879863" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-pulsate.min.js", + "offset": 57313705, + "size": 688, + "sha256": "4d8aeb4b625188450aedc8ea14b49b4eda316614d16f0c9a05c8e0ba4df19995" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-scale.js", + "offset": 57314393, + "size": 1373, + "sha256": "6711de6f9f9d168cdb1131c6298ec546a527537e8c04243e7d8400a5471b1138" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-scale.min.js", + "offset": 57315766, + "size": 723, + "sha256": "494e1cc4d8f1adbbb1fc837ab11d9bcca01b6787899d0a6fb1eb3da5b28d1819" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-shake.js", + "offset": 57316489, + "size": 1884, + "sha256": "d7dbc93cf62b2845ced0614d22a823cd60fd64053661caa0e775a4080a0743b0" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-shake.min.js", + "offset": 57318373, + "size": 846, + "sha256": "0cb945e907b6ec3824cb2f83e1b5283e4192d20e3bdeeef1a8796b2d697188db" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-size.js", + "offset": 57319219, + "size": 5418, + "sha256": "5fce35ea6ae5a9f30c8badc0f0adec845a42bb735a022caba022eca515a53b60" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-size.min.js", + "offset": 57324637, + "size": 2491, + "sha256": "8a87104ef5888f334a753593506c63cbd25d093246ac719c239a9d15820b1957" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-slide.js", + "offset": 57327128, + "size": 1967, + "sha256": "ea3d2fa7637f31214cf7602a3db068be037f3be03e204a3d4bd9f83bcdf6d94d" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-slide.min.js", + "offset": 57329095, + "size": 917, + "sha256": "f543270843c5b8dc38e2a5eed2df19603ac1cf30ced537bc8621520ac9fae7a9" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-transfer.js", + "offset": 57330012, + "size": 888, + "sha256": "a32bd62ec4efc5dd83f08cdb847d7c716dc838a88e8bc834ee8073ed9232fced" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect-transfer.min.js", + "offset": 57330900, + "size": 442, + "sha256": "84c1e4a8b26f8d515feb500bb036762ec4e44411b0ae305bddfdf9b91eb248fe" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect.js", + "offset": 57331342, + "size": 24613, + "sha256": "979dce306216ebee5a181e8f453e14978bc44d5f0ccfd2614c67dac5c18df6be" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/effect.min.js", + "offset": 57355955, + "size": 10332, + "sha256": "b7f28f2464e085279a304d2abee8f0c89f82077338dfe0dd44882ed0d53d018c" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/menu.js", + "offset": 57366287, + "size": 18960, + "sha256": "58a011569ad35ba9e1d02ebf78fda6bef1c4fdb7e87cedf2ceb01d9309a3cced" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/menu.min.js", + "offset": 57385247, + "size": 10199, + "sha256": "41788f27f61aab4174275eb5fbbbcaffde0f4f0f07e6900592affad38e09b154" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/mouse.js", + "offset": 57395446, + "size": 6227, + "sha256": "de9358a0f763e19c98b221c40feb45eb310179bdb6dbc6408c4bc8e24ac3eccd" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/mouse.min.js", + "offset": 57401673, + "size": 3428, + "sha256": "f36adc07db49e73c3fd3aeb4234d270725f07719706dd28dfc09657f2cffe9d6" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/progressbar.js", + "offset": 57405101, + "size": 4239, + "sha256": "0b3ac04f7be3acdbd734483f111953d649ebe89c454ed37917c9f2c66c96edd6" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/progressbar.min.js", + "offset": 57409340, + "size": 2558, + "sha256": "42817f25b2a7b28a9d4128034fa552eb78a5558844e171e1c9e3768dfde6b586" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/resizable.js", + "offset": 57411898, + "size": 30490, + "sha256": "8bad5da8db91181346a861f3dd5332b20131374a94fdccc19f0cbb5cb43f174f" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/resizable.min.js", + "offset": 57442388, + "size": 18824, + "sha256": "ea62820f208f0858cf777fc6babbf7f282b961922ff098a383e08d8b9e22338b" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/selectable.js", + "offset": 57461212, + "size": 8129, + "sha256": "47d95f4448bace9f2b3bb1480a6280727fa3f9388d1c24f7ac5241a1825015b2" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/selectable.min.js", + "offset": 57469341, + "size": 4506, + "sha256": "7f0906164e325a602df7db9826ef26bf21f61b87dd66f8243695428b022bfb2c" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/selectmenu.js", + "offset": 57473847, + "size": 16346, + "sha256": "3754610bac80d13d2ca920c5766d8577f67e54ea20687acda98856ef4a173c48" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/selectmenu.min.js", + "offset": 57490193, + "size": 9502, + "sha256": "89c3f51110f629231ae765385824fb6df90584e9063db539777b350f868eb859" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/slider.js", + "offset": 57499695, + "size": 19599, + "sha256": "a1952264c64f212cf42e2e4a40d1870b86821e8f701cdffa1716737d1b65b5b9" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/slider.min.js", + "offset": 57519294, + "size": 10759, + "sha256": "d9b268266af74b5c0834e2a519303ca97c5427c5fb7abb3b89ad29e07664be14" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/sortable.js", + "offset": 57530053, + "size": 47641, + "sha256": "ba779bd0e05c72f20387d38ebbc45c86a19ad2faf003e4d1681e294b853373bb" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/sortable.min.js", + "offset": 57577694, + "size": 25504, + "sha256": "8ff5de1b0b1d4922b0bf1eea21886efa9976d29655dd30c5ea435dc0b583aeee" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/spinner.js", + "offset": 57603198, + "size": 14434, + "sha256": "a5fe43c7a520194e037e63f6ce5e4d7d03781b92a0e7f44a2619ddc8e87e9ff3" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/spinner.min.js", + "offset": 57617632, + "size": 7676, + "sha256": "45253f10c4b1ecbea3f226a6fab2a65f47d1f09138a1e9f94aab583c90cff792" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/tabs.js", + "offset": 57625308, + "size": 23664, + "sha256": "80f010d72752e22368b9a37372bdcdeed3a9b93bc17f998fafe4074ebc2c4ed2" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/tabs.min.js", + "offset": 57648972, + "size": 12008, + "sha256": "a1ac109fb0da76a03eb39daecb548806675cda9793ad6bce4621c651746de08f" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/tooltip.js", + "offset": 57660980, + "size": 14476, + "sha256": "9c74b18910c7edaaa38821f4690082353e7de82d410065ae5c9e08fa042f1696" + }, + { + "path": "wordpress/wp-includes/js/jquery/ui/tooltip.min.js", + "offset": 57675456, + "size": 6244, + "sha256": "2cc9b42a2a4908d09be26a45fef3e9ef198a09ca804c981163ad3ad87b6fbc29" + }, + { + "path": "wordpress/wp-includes/js/json2.js", + "offset": 57681700, + "size": 31, + "sha256": "0e558db0429f82025c9b56238f0aa83c39931db8c922815405593f91bc441dad" + }, + { + "path": "wordpress/wp-includes/js/json2.min.js", + "offset": 57681731, + "size": 35, + "sha256": "c892b9c0828a176db25a693cb5709049f8a4eed8bdc7bf6595f0c7e02692892d" + }, + { + "path": "wordpress/wp-includes/js/masonry.min.js", + "offset": 57681766, + "size": 24138, + "sha256": "e00add38134eac2fb8e8e9c09cbfff7bbe57952b210322eb2eecb0a21fc055eb" + }, + { + "path": "wordpress/wp-includes/js/mce-view.js", + "offset": 57705904, + "size": 25849, + "sha256": "0c631e0e825173f071e5ae1b6fda6a1f5a8b75d8752bf4359769414f2bf3b52d" + }, + { + "path": "wordpress/wp-includes/js/mce-view.min.js", + "offset": 57731753, + "size": 9770, + "sha256": "494829bff2bfcdafccb5183f3772dda19032a35ea4b21053e73dbd25299bb637" + }, + { + "path": "wordpress/wp-includes/js/media-audiovideo.js", + "offset": 57741523, + "size": 24711, + "sha256": "a5514b4b66900d97a6533e8e7b1b869279189fdc04b7a3c82ac9af8dde22fb0f" + }, + { + "path": "wordpress/wp-includes/js/media-audiovideo.min.js", + "offset": 57766234, + "size": 12043, + "sha256": "26da6d187894d67249c55eab476abdeb04162182a99995b31836a6f599b448e5" + }, + { + "path": "wordpress/wp-includes/js/media-editor.js", + "offset": 57778277, + "size": 29460, + "sha256": "e27ab4d380e7936a1fa8fdc2d681cbe914d352691a51f0ca9883cbf885cf177b" + }, + { + "path": "wordpress/wp-includes/js/media-editor.min.js", + "offset": 57807737, + "size": 11092, + "sha256": "81ccfef9f90c6f32f76e8eebc7db023ea7cef2ad3680e77a0b00cae7b8155c4b" + }, + { + "path": "wordpress/wp-includes/js/media-grid.js", + "offset": 57818829, + "size": 26688, + "sha256": "ceb092381abd2f2a4825aaec0b0a142225c8dfeab1f01a108aa9f6e622ded880" + }, + { + "path": "wordpress/wp-includes/js/media-grid.min.js", + "offset": 57845517, + "size": 13300, + "sha256": "7643048b7e2fec1d6535d9ab21a059a033728e3c7cff757bd90c74d59e727e1a" + }, + { + "path": "wordpress/wp-includes/js/media-models.js", + "offset": 57858817, + "size": 43544, + "sha256": "1db86b277b10782dd3f9c5e0d9c9883b543f5dee2bc48992a036b4ae667523cf" + }, + { + "path": "wordpress/wp-includes/js/media-models.min.js", + "offset": 57902361, + "size": 13279, + "sha256": "3fd404d8ba6aae5ff6090120de77c6debfd32f3df327c61d82d41d866c226524" + }, + { + "path": "wordpress/wp-includes/js/media-views.js", + "offset": 57915640, + "size": 272888, + "sha256": "121fcf8d99572c20e3769aee7d11dd6be35746dd209b9af1d58f9a45f2c164ea" + }, + { + "path": "wordpress/wp-includes/js/media-views.min.js", + "offset": 58188528, + "size": 110657, + "sha256": "5e3908d7cc884b454d1d872df38aff1782ab30e2f4942bc40caac2cd52655471" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelement-and-player.js", + "offset": 58299185, + "size": 265827, + "sha256": "b00baaeb449ba438db52df65260b5f42518e5f9b6b7ef74d693832f6b5b7c679" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelement-and-player.min.js", + "offset": 58565012, + "size": 158005, + "sha256": "b15c3ea03d50c2430490e7416733a254feea4237bb60b54181bd3473ebe4149f" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelement-migrate.js", + "offset": 58723017, + "size": 2841, + "sha256": "df65c62320d0f886f29de1e27ed22ceee23d7c3f0d8737f494cce96648a88500" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelement-migrate.min.js", + "offset": 58725858, + "size": 1191, + "sha256": "6d161e98e47ae150b51211443eef37040fb6269dcf85ad2048548066dca99e6f" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelement.js", + "offset": 58727049, + "size": 122582, + "sha256": "b6ffbad0ca515c9e74ef69c792ed3c1bb4a72253bf792e24971eabbe54ad31e1" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelement.min.js", + "offset": 58849631, + "size": 68310, + "sha256": "53275076d38d9f190c8ee0f235312775a690ff1c7b81a7d4c4688a69eb6bd78b" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelementplayer-legacy.css", + "offset": 58917941, + "size": 15758, + "sha256": "048c9e20ef9be99957bce32dd2f093816cb57477452eaf8f3bac867d2e0bdf5a" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelementplayer-legacy.min.css", + "offset": 58933699, + "size": 11256, + "sha256": "b7908a015a567ec2363011df2475368dbff34360e9da3fdff50604d6395fb646" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelementplayer.css", + "offset": 58944955, + "size": 15907, + "sha256": "a6d238ce37631565babb1b7dd466401b843925c6c460e8049f2789473533876f" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mediaelementplayer.min.css", + "offset": 58960862, + "size": 11407, + "sha256": "8e2d5b7c96931a7cac72873b2f3715f72349cb9bc6289d1facedca268d686864" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mejs-controls.png", + "offset": 58972269, + "size": 2883, + "sha256": "39379d1e0e86e2f44e6d9b71e33578197a778d1b5e5440856e338513a469110c" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/mejs-controls.svg", + "offset": 58975152, + "size": 4598, + "sha256": "ad55816ac6c62f214e60a1913ff4f0215ab329034cbc7436a5514941449ca7b9" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/renderers/vimeo.js", + "offset": 58979750, + "size": 12416, + "sha256": "1201feb763c472f9d86a5022bb94c9ebf1a322482b44ac56ed6017054682d6b5" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/renderers/vimeo.min.js", + "offset": 58992166, + "size": 6464, + "sha256": "722a90d42ef2bd0ea38f0fdac6b4c0523aa4a027e9ffe889972100746e165582" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/wp-mediaelement.css", + "offset": 58998630, + "size": 4960, + "sha256": "6d9f061cba81145d9bab0964192d66cb2e13a71591482cdfaf5b718341171da1" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/wp-mediaelement.js", + "offset": 59003590, + "size": 2754, + "sha256": "ee5a169e310b36f6726275a551cae31385e0833fc84595c6c33b44b17c2da335" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/wp-mediaelement.min.css", + "offset": 59006344, + "size": 4186, + "sha256": "2e10d353ff038c2cad3492fc17801af3e6ef2669c9e9713bdb78b1dcb104c4fe" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/wp-mediaelement.min.js", + "offset": 59010530, + "size": 1107, + "sha256": "79cb399203843f65199bec32bc4abac5dfd20f141d3e4ec1424bf00c7108fa45" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/wp-playlist.js", + "offset": 59011637, + "size": 5410, + "sha256": "dc1f8bb2b6cce40ea217533d8ece48cbcf26a983d026535f591fdb592d609372" + }, + { + "path": "wordpress/wp-includes/js/mediaelement/wp-playlist.min.js", + "offset": 59017047, + "size": 3445, + "sha256": "19e36290a3e850500b210f81de946d6f7456c90f6f398ec0d05b9b5ec1cf3d71" + }, + { + "path": "wordpress/wp-includes/js/plupload/handlers.js", + "offset": 59020492, + "size": 20540, + "sha256": "41cd01da78a616da7e748bf637fed033331cf2196084f0b3827bc7423572d56e" + }, + { + "path": "wordpress/wp-includes/js/plupload/handlers.min.js", + "offset": 59041032, + "size": 12035, + "sha256": "0684c497dbe6a6817092183f01214b7f1bb665e05eb18f223616101c564ba355" + }, + { + "path": "wordpress/wp-includes/js/plupload/moxie.js", + "offset": 59053067, + "size": 254342, + "sha256": "279a419c26b8c3d178eac2ebbd85c6f515f2c1e3cffdd8067e0b6788c8d0e7b9" + }, + { + "path": "wordpress/wp-includes/js/plupload/moxie.min.js", + "offset": 59307409, + "size": 87394, + "sha256": "418c78089b8f4d2842b22a02eceda281056200977443b014cb7c553c0c4f37c9" + }, + { + "path": "wordpress/wp-includes/js/plupload/plupload.js", + "offset": 59394803, + "size": 60314, + "sha256": "815cef3fb7f36dacf06c38037ee997a1420a95b26e1ab3de075a44e8cdbedc99" + }, + { + "path": "wordpress/wp-includes/js/plupload/plupload.min.js", + "offset": 59455117, + "size": 15605, + "sha256": "b0069bc7672025c216451d39e7bfd4d74776acd2d44f48a3edb9aa8f37fa6b7f" + }, + { + "path": "wordpress/wp-includes/js/plupload/wp-plupload.js", + "offset": 59470722, + "size": 16705, + "sha256": "5afe280fc7a475be01ded55e00037c0a428db8892a773256273e6b5e17d66657" + }, + { + "path": "wordpress/wp-includes/js/plupload/wp-plupload.min.js", + "offset": 59487427, + "size": 6036, + "sha256": "a38530d25bc58981bdd5d9196149d06290318917dc9cc8edeb7c65d073383e02" + }, + { + "path": "wordpress/wp-includes/js/quicktags.js", + "offset": 59493463, + "size": 22601, + "sha256": "d2afa53fe0cf6eaa25ab5f4f303b773ed868c54bce47aeb5b1474db40d736539" + }, + { + "path": "wordpress/wp-includes/js/quicktags.min.js", + "offset": 59516064, + "size": 11132, + "sha256": "7fc1c384eed2bd0e96a526374f0e116e724f8d9dd160c1260e1a9713df9ff0e0" + }, + { + "path": "wordpress/wp-includes/js/shortcode.js", + "offset": 59527196, + "size": 10758, + "sha256": "e699224aae67bc262de1136ee64bbc1e892c196265dad33a45a121d690ab630b" + }, + { + "path": "wordpress/wp-includes/js/shortcode.min.js", + "offset": 59537954, + "size": 2643, + "sha256": "88d22d83af1b6a5a266b51048004fb4ad7e2ca34bef788d61182a9108d658a1d" + }, + { + "path": "wordpress/wp-includes/js/swfobject.js", + "offset": 59540597, + "size": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "wordpress/wp-includes/js/swfobject.min.js", + "offset": 59540597, + "size": 35, + "sha256": "c892b9c0828a176db25a693cb5709049f8a4eed8bdc7bf6595f0c7e02692892d" + }, + { + "path": "wordpress/wp-includes/js/swfupload/handlers.js", + "offset": 59540632, + "size": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "wordpress/wp-includes/js/swfupload/handlers.min.js", + "offset": 59540632, + "size": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "wordpress/wp-includes/js/swfupload/swfupload.js", + "offset": 59540632, + "size": 0, + "sha256": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" + }, + { + "path": "wordpress/wp-includes/js/thickbox/loadingAnimation.gif", + "offset": 59540632, + "size": 15238, + "sha256": "6a486bb6036ea984d293ab009566e99e522abc19f8833c5fd49630be7eba0135" + }, + { + "path": "wordpress/wp-includes/js/thickbox/macFFBgHack.png", + "offset": 59555870, + "size": 94, + "sha256": "8acef2f4c6b1aac2fa69ad3f4955fbcedf4d95e20d68a4ea433786108bcb03ae" + }, + { + "path": "wordpress/wp-includes/js/thickbox/thickbox.css", + "offset": 59555964, + "size": 2668, + "sha256": "d02e5c235b384bb58cd2e9cda0861fb6be2440a2f38ae85ee4626979f76124f8" + }, + { + "path": "wordpress/wp-includes/js/thickbox/thickbox.js", + "offset": 59558632, + "size": 13840, + "sha256": "885521c3e57c70bb161c4b6b592bdb945d01045452002d92026bbcddda1b6194" + }, + { + "path": "wordpress/wp-includes/js/tinymce/langs/wp-langs-en.js", + "offset": 59572472, + "size": 15529, + "sha256": "8af9e2730cc064da54c5f0e2b746f56bc7cd96e3eadcbfac009ebc668078cc1e" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/charmap/plugin.js", + "offset": 59588001, + "size": 23210, + "sha256": "ff3ed2bcd6d444849fe3d52399973c4050c30dd23e0a60c59436ac28949e24b6" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/charmap/plugin.min.js", + "offset": 59611211, + "size": 8601, + "sha256": "f7f129649dd03e78c621e54ce7cb2642acc80f52e8114b5dc3e3bb42b35e02b0" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/colorpicker/plugin.js", + "offset": 59619812, + "size": 3561, + "sha256": "385c08da4f466b753c803f6bc981498b5085803b67f4f3838c9434a3e891c193" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/colorpicker/plugin.min.js", + "offset": 59623373, + "size": 1349, + "sha256": "44b45ab8b7d470cb166bd4a3fde9994dbb7305d9274b4b470c91f98e4b14c9d1" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/compat3x/css/dialog.css", + "offset": 59624722, + "size": 8179, + "sha256": "48ff43089a1058bc227a219ede4a08ff89a20fdcebd07a916d508d7636caae31" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/compat3x/plugin.js", + "offset": 59632901, + "size": 9450, + "sha256": "c32aaf1dccfb6e82ffdce5a30bcedf6f30791418e48497317f9f93e77edd7ceb" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/compat3x/plugin.min.js", + "offset": 59642351, + "size": 4129, + "sha256": "d8cd42778a4a221b675737a295cb352cfe7cc874f6113e84dad416485ecc246e" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/directionality/plugin.js", + "offset": 59646480, + "size": 1892, + "sha256": "fb3bf589c5ebbff300f5e40ccf52f506a007467cf1eeb6ddface8425da4c4f51" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/directionality/plugin.min.js", + "offset": 59648372, + "size": 857, + "sha256": "f0aabb563087feac0b0cad11c6aaeed03489244d6a77d83c0d9deb81ff099c27" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/fullscreen/plugin.js", + "offset": 59649229, + "size": 5595, + "sha256": "5c39e559671ad99d8a213e78c1aba4de60fa4d76470f2675e1eba08ce5ae29a1" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/fullscreen/plugin.min.js", + "offset": 59654824, + "size": 2184, + "sha256": "22fdf660a5955b036bd037db797408d4c3461f4ac7c097a3b057240e9bfb4ba0" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/hr/plugin.js", + "offset": 59657008, + "size": 919, + "sha256": "257a8af0f16d52aadf6348c6caefa64ecf5683910b19bc718b652e9edf3e809e" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/hr/plugin.min.js", + "offset": 59657927, + "size": 428, + "sha256": "043194ef7f5efece27728e81e821df49586aaac13e006346236b4d5fa627d109" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/image/plugin.js", + "offset": 59658355, + "size": 40022, + "sha256": "4a6a22af778d6c5f81fdecf9a42c4d40507954d454d65f9f0724d189fd9b90c5" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/image/plugin.min.js", + "offset": 59698377, + "size": 15852, + "sha256": "a4a31f63fd4ca7b642c74adf53687dc80af5d172009cc4ad81d75ea485af337c" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/link/plugin.js", + "offset": 59714229, + "size": 24009, + "sha256": "988448af8f43f3a52d84f504994899e6421095387c45f6a58add680095b7c9e1" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/link/plugin.min.js", + "offset": 59738238, + "size": 8940, + "sha256": "b4e1a80902e2aa4cc79fd483a8b787687396419c12d7411e15b7135a69bb2b48" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/lists/plugin.js", + "offset": 59747178, + "size": 70415, + "sha256": "18161cce6c8bef17c17d44eff981dafedb64c50c13997971455db4dee394b2ef" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/lists/plugin.min.js", + "offset": 59817593, + "size": 26968, + "sha256": "b916e1ec06e7e50dea51b744991c5bd142a7e28fdeac451678bd6a8b56d2672f" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/media/plugin.js", + "offset": 59844561, + "size": 41338, + "sha256": "d9a4c1ee8989fb79ae0469bc6c5dde7aa41b3f3d6cd228c0572c65d2cf37b0b0" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/media/plugin.min.js", + "offset": 59885899, + "size": 16576, + "sha256": "dfcd9e6e1a24dfe869481a6dff0c1919439c9aa44d7487bdc823b36389ce57a6" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/paste/plugin.js", + "offset": 59902475, + "size": 82356, + "sha256": "c80a344c6de8360021a58182df651d140f28cacb937dba9c2f7e1c96a0e24e7f" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/paste/plugin.min.js", + "offset": 59984831, + "size": 30837, + "sha256": "26d131b5a88e878ed52573b651cf8c4c78ff40952920952b4eff3cd8e8d22265" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/tabfocus/plugin.js", + "offset": 60015668, + "size": 3722, + "sha256": "ad875c946f8f0a8c45c34e05bf830e3d374c72e61d41e17b2edf2eabaca7a40f" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/tabfocus/plugin.min.js", + "offset": 60019390, + "size": 1614, + "sha256": "19be5837567054ccc84016a2e1903bf5d4b7ab6c81e426be2272f2d67408e3fb" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/textcolor/plugin.js", + "offset": 60021004, + "size": 11310, + "sha256": "8b0f2ad648a2b55432d9ae1b52c4e22703eeb97bc5a05f1ba0b62ba4b28ee1eb" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/textcolor/plugin.min.js", + "offset": 60032314, + "size": 4927, + "sha256": "22462f794e6941a18a19c6e92f1192a2a4768524c2755c3fec4478ac51afdd1c" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wordpress/plugin.js", + "offset": 60037241, + "size": 34146, + "sha256": "aaebe2f2aba0d302c8c6ffc069d21f91a5318ec8583c0a5b057a20493cb63506" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wordpress/plugin.min.js", + "offset": 60071387, + "size": 16407, + "sha256": "018969ea13bb63c2137ccc9321d8a6443f559739af4f4c0608b4c3c3cc13a2d2" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpautoresize/plugin.js", + "offset": 60087794, + "size": 5988, + "sha256": "53b6d2d49321648859ee074135a5572e2e36add4bf89f9048715b07e48aca754" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpautoresize/plugin.min.js", + "offset": 60093782, + "size": 2344, + "sha256": "5a8d2a3ff3996469d0bb9ec5e04d795eb78e7817de1da773056128cf3ee804f0" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpdialogs/plugin.js", + "offset": 60096126, + "size": 2439, + "sha256": "61e367f4d42f030c74ea044cd7a0ea7f7b47c7d119eab04a38d0c241f90735ab" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpdialogs/plugin.min.js", + "offset": 60098565, + "size": 1322, + "sha256": "064c64133a716984815e5502e52548bd67b031494324c1fae92303f7b6dfd709" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpeditimage/plugin.js", + "offset": 60099887, + "size": 25642, + "sha256": "8838b632abb00b12078f3715bf6e8c41ce2e109d941ad580d3c4f26f7651b583" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpeditimage/plugin.min.js", + "offset": 60125529, + "size": 12067, + "sha256": "93be8e22fbe9f74eae800bc2837d79c2b108e604922782dc31dd50234b408571" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpemoji/plugin.js", + "offset": 60137596, + "size": 3580, + "sha256": "0d1dbfcfe975812d39edb297e05fb98d678a3feea2dc55892ac68343cb7f7c16" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpemoji/plugin.min.js", + "offset": 60141176, + "size": 1519, + "sha256": "e367c9292ae0c9fae81104c9d55416776c6dcb469eb8beb599c4f3d72cc7771f" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpgallery/plugin.js", + "offset": 60142695, + "size": 3183, + "sha256": "456fb448cbb9214d7b81511d0fb79d566a224b2502213363600fcfdd1483fc8c" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpgallery/plugin.min.js", + "offset": 60145878, + "size": 1623, + "sha256": "469195aa6ac77e4259b030adb5f8b82a872bb3991a633ce748de35090c3dccc4" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wplink/plugin.js", + "offset": 60147501, + "size": 17798, + "sha256": "2a2cf4a1aec30eb5168ad303ec2bb439daed7120b7ca6470ba7475f6f957f4ed" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wplink/plugin.min.js", + "offset": 60165299, + "size": 8988, + "sha256": "8d9cc235972820365475cb382b1c6837f506ac27bbcc76565fdd510b9115d75d" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wptextpattern/plugin.js", + "offset": 60174287, + "size": 8802, + "sha256": "809de15c4a7ff341085ed38a7862b85440bcfa2e856b47802908ebf4bc0172d9" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wptextpattern/plugin.min.js", + "offset": 60183089, + "size": 3121, + "sha256": "c162fce538ed2e99cf59fc46253ebe24229195af8a6a8c06b2878ac9908db06a" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpview/plugin.js", + "offset": 60186210, + "size": 6083, + "sha256": "4ab71979bcce4b2a59c76ee2b2d63d56004337751ffa4f3606ff89e416a4172d" + }, + { + "path": "wordpress/wp-includes/js/tinymce/plugins/wpview/plugin.min.js", + "offset": 60192293, + "size": 2902, + "sha256": "4582471b1fc16f466344b68eab004f571920a1c7e054f3051845730ef9ecbc4c" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/content.inline.min.css", + "offset": 60195195, + "size": 3611, + "sha256": "2a76959e34ca54b5c4e6324a83791e12d41277aa2fb0041f9375bb4049dc388c" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/content.min.css", + "offset": 60198806, + "size": 4017, + "sha256": "3da49497491a6c1ffbf956ca3e951075db847d42fdf315d19752035ddc0f6629" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.eot", + "offset": 60202823, + "size": 9492, + "sha256": "a10fc4343d95b716c16d77463d475be5c079599ea67e1cd2bd3a94d5e7f508f9" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.svg", + "offset": 60212315, + "size": 24727, + "sha256": "e7773001446ab937e1d8d4bd5e8dbd9b31d112037353a14b319e36dd010ed8ee" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.ttf", + "offset": 60237042, + "size": 9304, + "sha256": "2f657502906d6f5c3fc8df3a82969114ebe030addfdc061c60c974b0f515fd09" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce-small.woff", + "offset": 60246346, + "size": 9380, + "sha256": "d3efbb678ca6de5632902bd93772746ba2f8e4e2322b953936e12694a183aa31" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.eot", + "offset": 60255726, + "size": 18912, + "sha256": "d7d36b21dc4b2dd2e4252b2b71f7b57d2d828359f2936af6e3fe8e2f913a0498" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.svg", + "offset": 60274638, + "size": 46373, + "sha256": "e17f0683d5785645d10307971450b94e62b86f062e92fcea6a055c2443bdef5f" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.ttf", + "offset": 60321011, + "size": 18748, + "sha256": "8c8effbf1b373e7bd3f8f291264eaefeed933fea8fe91637d81f3fc9574c3592" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/fonts/tinymce.woff", + "offset": 60339759, + "size": 18824, + "sha256": "84d35201689d08969a92a8ccdc5a0002da01eac30651fef23e79e1993ace2ebf" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/img/anchor.gif", + "offset": 60358583, + "size": 53, + "sha256": "2861666fd107d278d4449970615136d06d7f746be9bb19072cf9c8f30e565e1e" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/img/loader.gif", + "offset": 60358636, + "size": 2608, + "sha256": "eb7cfd3d959b2e09c170f532e29f8b825f9bc770b2279fde58e595617753e244" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/img/object.gif", + "offset": 60361244, + "size": 152, + "sha256": "e6a15e52bc4a17b085073ba8debd4708ead6ae3d4cbeb3880c65cb7afc489777" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/img/trans.gif", + "offset": 60361396, + "size": 43, + "sha256": "9cf020d7c3bba7f5ab10cda54aabef934f906d4f9a3acf99e9e7dc6c98579635" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/lightgray/skin.min.css", + "offset": 60361439, + "size": 44010, + "sha256": "09aa34fa71f3d670ad40d8a7238a02411a7ee4e97614d166b1573300dcbd2709" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/audio.png", + "offset": 60405449, + "size": 412, + "sha256": "15ceab89286fa44c5918a6366f37ebf82360226eb5a9ec34aa908fcdea7a35e1" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/dashicon-edit.png", + "offset": 60405861, + "size": 368, + "sha256": "10902480df9e5b6003b65f1138b52ed14c6d5c5d94da161068110e56d66ed588" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/dashicon-no.png", + "offset": 60406229, + "size": 339, + "sha256": "2ae4a898d3f5c13f0d3cfa492ffd2424fb9ee95b63e90d2e4a35f312930b4dc9" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/embedded.png", + "offset": 60406568, + "size": 8177, + "sha256": "d571991e8b7e7803413dced3658bf5b6aab0567310be8dff996c7ddfe2841051" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/gallery-2x.png", + "offset": 60414745, + "size": 447, + "sha256": "411a4e3ebbddf2750107fc92af8197d45cbb59d77235e01a835592626b886280" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/gallery.png", + "offset": 60415192, + "size": 379, + "sha256": "4de5c698aa2f724d00a935f2959ae4a1dd8f74095807f852e8a3408267231007" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/more-2x.png", + "offset": 60415571, + "size": 603, + "sha256": "91a638095ea606358fce4fe872095287687c21cbaebfb890b51693ff47344075" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/more.png", + "offset": 60416174, + "size": 414, + "sha256": "5b1434ee310b36b5fd7f63e32a0d4a33d677acf068b2b81ef204b545fcecef3a" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/pagebreak-2x.png", + "offset": 60416588, + "size": 835, + "sha256": "63289e02808ca02df566508524fa9ec982c56e6e4b932c42ba6026b20c5a81b3" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/pagebreak.png", + "offset": 60417423, + "size": 1140, + "sha256": "cb43c8ce6939857315f0d1493edc2a281915b20818bc58583ad9963fcbe0cecc" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/playlist-audio.png", + "offset": 60418563, + "size": 440, + "sha256": "5f3e806a12f67c224120da81517ccf10de615055cbe9da76a908a0f8491ee7a0" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/playlist-video.png", + "offset": 60419003, + "size": 290, + "sha256": "3d476ba18eaa0dae0a9c76133e3bfd1580f126cb51c3b1ec1da50e1d111bcc70" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/script.svg", + "offset": 60419293, + "size": 930, + "sha256": "3f256d8be33988def7460cd97b128e54ae32431b2bbcf6958f1016f01f2d60c7" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/style.svg", + "offset": 60420223, + "size": 1516, + "sha256": "42a73e0f3f9a70ce4c0a7e0809bf2975611fb98c848d71c9616980d9efbb8b3c" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/images/video.png", + "offset": 60421739, + "size": 363, + "sha256": "9d1558585b0ef4653553fd95111ee010ddc27e6a016ec2908eb69380c666f8fa" + }, + { + "path": "wordpress/wp-includes/js/tinymce/skins/wordpress/wp-content.css", + "offset": 60422102, + "size": 8760, + "sha256": "59dbd092a17ab5b77ad76d9e20955838432e69cd7cc166e24da1ecd69f8500db" + }, + { + "path": "wordpress/wp-includes/js/tinymce/themes/inlite/theme.js", + "offset": 60430862, + "size": 320767, + "sha256": "f2e64b7c550a9d7758145b947e76ea9fb2294805c0b6340d73a1e0d308b94af3" + }, + { + "path": "wordpress/wp-includes/js/tinymce/themes/inlite/theme.min.js", + "offset": 60751629, + "size": 131875, + "sha256": "9d7bf1dcdd3e0b76dd3ff09d713b4df72a7fe33e3e35339fb4f3d59f06ae76cc" + }, + { + "path": "wordpress/wp-includes/js/tinymce/themes/modern/theme.js", + "offset": 60883504, + "size": 315475, + "sha256": "a0916a38795206b32215c30e90850bc868108799020788eb66d2e959c2375f78" + }, + { + "path": "wordpress/wp-includes/js/tinymce/themes/modern/theme.min.js", + "offset": 61198979, + "size": 130746, + "sha256": "cde16bbc126b91b95d00f6c87b736609fbc343f515bbee32d951b018e023c4e7" + }, + { + "path": "wordpress/wp-includes/js/tinymce/tiny_mce_popup.js", + "offset": 61329725, + "size": 15988, + "sha256": "077ef2ae2230f4b513674211342a7c38fd3c07a837d2b1dc0d06bf3d35a99a33" + }, + { + "path": "wordpress/wp-includes/js/tinymce/tinymce.min.js", + "offset": 61345713, + "size": 365570, + "sha256": "4a74739272c2951dc668dc3da0de287df3061623db5d04cfeead3214b3cfc206" + }, + { + "path": "wordpress/wp-includes/js/tinymce/utils/editable_selects.js", + "offset": 61711283, + "size": 2125, + "sha256": "893cddd26a55aec15932aa25799af427abbb0151704b004f7d5ee01727e41213" + }, + { + "path": "wordpress/wp-includes/js/tinymce/utils/form_utils.js", + "offset": 61713408, + "size": 6075, + "sha256": "8ca158f20dccb049342231727174cd584bde0824c577b6ea9a74596a8eac1bb3" + }, + { + "path": "wordpress/wp-includes/js/tinymce/utils/mctabs.js", + "offset": 61719483, + "size": 4160, + "sha256": "7d1d7284f6ad34791f6c03d3f04e3681ff711f3ff5610afce54c1d8024fa71ec" + }, + { + "path": "wordpress/wp-includes/js/tinymce/utils/validate.js", + "offset": 61723643, + "size": 6466, + "sha256": "138e9f73e6a3c3953783fca5894d396ee18a9a248ae87db1cb6bbe18bdf3e0a8" + }, + { + "path": "wordpress/wp-includes/js/tinymce/wp-tinymce.js", + "offset": 61730109, + "size": 671433, + "sha256": "0cba73dfc460f87eb60ca2ae2219a37b89473d8d140dbf80de807f8d96898db1" + }, + { + "path": "wordpress/wp-includes/js/tw-sack.js", + "offset": 62401542, + "size": 4970, + "sha256": "90eaf3d4e7e8607d72e84a95a3f2b25a92dba7e438ec271b38315d5a3bd6fb28" + }, + { + "path": "wordpress/wp-includes/js/tw-sack.min.js", + "offset": 62406512, + "size": 3288, + "sha256": "c5e0847c324fd07f5f10699edda1eb9795ddc663884efd6d7ad2f43b3568da75" + }, + { + "path": "wordpress/wp-includes/js/twemoji.js", + "offset": 62409800, + "size": 37180, + "sha256": "cd3eb07aa42b1958c00b03a9ac02e628564a7e9ba213f0e8b122c8ba2a14a4db" + }, + { + "path": "wordpress/wp-includes/js/twemoji.min.js", + "offset": 62446980, + "size": 19848, + "sha256": "d19b45e6d38bac16c7a14bb7d4b352912527aad1715fd0d13213c9a39b716ea6" + }, + { + "path": "wordpress/wp-includes/js/underscore.js", + "offset": 62466828, + "size": 68873, + "sha256": "2016a46bd913f035f47483af603dd99b35b967c369ea2f5a2bbf600fa434eb69" + }, + { + "path": "wordpress/wp-includes/js/underscore.min.js", + "offset": 62535701, + "size": 19012, + "sha256": "61ccb2ff314af4b300014f1715b9e9b29f6aede9f25151e00722d5a4455c0ff2" + }, + { + "path": "wordpress/wp-includes/js/utils.js", + "offset": 62554713, + "size": 4665, + "sha256": "678b77e0a869f12beedcb1328889aef2d07eb9980b3943621d460a8369b42784" + }, + { + "path": "wordpress/wp-includes/js/utils.min.js", + "offset": 62559378, + "size": 1864, + "sha256": "a2687fe8e299a3aad2d4701478f7a7ea3689ef4f470372e3484cf28b84b019b1" + }, + { + "path": "wordpress/wp-includes/js/wp-ajax-response.js", + "offset": 62561242, + "size": 3903, + "sha256": "5e6db0756be18cc3d09d640a42b6dddd9dcd4d7b0909ea1c5117a77ec80e05af" + }, + { + "path": "wordpress/wp-includes/js/wp-ajax-response.min.js", + "offset": 62565145, + "size": 2571, + "sha256": "8d275025fb9da7efc50b6858ac5845782d211c59c839fa09b279b2e3f5f53f71" + }, + { + "path": "wordpress/wp-includes/js/wp-api.js", + "offset": 62567716, + "size": 46983, + "sha256": "1babf057dbae37a7af9262c53ddbb18a0de0e68b6356ee6b5c19fa191522948d" + }, + { + "path": "wordpress/wp-includes/js/wp-api.min.js", + "offset": 62614699, + "size": 14682, + "sha256": "947960adcbb708c908d60c1fb55b6c617e11c93876ecf9f525f13accf7ddb591" + }, + { + "path": "wordpress/wp-includes/js/wp-auth-check.js", + "offset": 62629381, + "size": 4444, + "sha256": "2c290e5dfc42fd90809680a64e3fb0a1fbd1ef24964402d4d4df5a6243ca25a0" + }, + { + "path": "wordpress/wp-includes/js/wp-auth-check.min.js", + "offset": 62633825, + "size": 1689, + "sha256": "f49056ecb3532fa38333864975d05e63854d258f10e802ba0fcee2cbec613995" + }, + { + "path": "wordpress/wp-includes/js/wp-backbone.js", + "offset": 62635514, + "size": 15241, + "sha256": "0f6828419b617445e5eb00bb926942bc911aa5870f13ab00c145bb9c1c5cc4f3" + }, + { + "path": "wordpress/wp-includes/js/wp-backbone.min.js", + "offset": 62650755, + "size": 3039, + "sha256": "b6bee8b3a178c2bd515c16765604f7acdd2f5094bc4592d84b7122f84afc6f3c" + }, + { + "path": "wordpress/wp-includes/js/wp-custom-header.js", + "offset": 62653794, + "size": 10465, + "sha256": "524f4c6fdc5d636939aebbd9cd99c05ac987accb12d21024520628ee3148a923" + }, + { + "path": "wordpress/wp-includes/js/wp-custom-header.min.js", + "offset": 62664259, + "size": 4442, + "sha256": "d7830dc590b5ce4301a7095b042c92e7989bc53196cc23c0a298a401cc4f24c7" + }, + { + "path": "wordpress/wp-includes/js/wp-embed-template.js", + "offset": 62668701, + "size": 6779, + "sha256": "7f7056c344e76449dc36c07b1df601278232f042f12befd87bf1189369426cd6" + }, + { + "path": "wordpress/wp-includes/js/wp-embed-template.min.js", + "offset": 62675480, + "size": 3174, + "sha256": "9c380ac96470bc77b548a91d3f625791a2e4fd28b670f8cda346dd9e5170b103" + }, + { + "path": "wordpress/wp-includes/js/wp-embed.js", + "offset": 62678654, + "size": 3214, + "sha256": "ee9b5fcac9bb9f60a37f706a18796315313cce6cf182e1815027b876c6365cd1" + }, + { + "path": "wordpress/wp-includes/js/wp-embed.min.js", + "offset": 62681868, + "size": 1251, + "sha256": "51f2cdd4f7e97288a74d803275b0550ec9eb938210ad5ab7fb1409548acf7554" + }, + { + "path": "wordpress/wp-includes/js/wp-emoji-loader.js", + "offset": 62683119, + "size": 13203, + "sha256": "c11cb4989a70c515a1b27d4c2bc71dcaaebf96f6a6f466132c9b13a401215252" + }, + { + "path": "wordpress/wp-includes/js/wp-emoji-loader.min.js", + "offset": 62696322, + "size": 2890, + "sha256": "eb14c6387dfeedb3e8802eb27c8095e3f179aa8bdcf62486d948d8baedf37676" + }, + { + "path": "wordpress/wp-includes/js/wp-emoji-release.min.js", + "offset": 62699212, + "size": 22752, + "sha256": "063840b4b3f5869e0f0d078bab8bd29cfb3a59ee85e8e3edbbec35a8fe846fd3" + }, + { + "path": "wordpress/wp-includes/js/wp-emoji.js", + "offset": 62721964, + "size": 8792, + "sha256": "6cec30e87acdc37043fee00812d25fbfb764180e2e1b1147f6a37b238df8ec4d" + }, + { + "path": "wordpress/wp-includes/js/wp-emoji.min.js", + "offset": 62730756, + "size": 2855, + "sha256": "f91a66cbedd47953396d07e86f90d4b204a5b7866fcb095d6c2ffb1cbaad9a67" + }, + { + "path": "wordpress/wp-includes/js/wp-list-revisions.js", + "offset": 62733611, + "size": 970, + "sha256": "f520ca275ea9e27b63334e0e8fbc4a7fb516c2c854687defd3fe14716f718789" + }, + { + "path": "wordpress/wp-includes/js/wp-list-revisions.min.js", + "offset": 62734581, + "size": 597, + "sha256": "6ac247c57141fd8356a119456d2bda65454545e158e4d33cec76f05325951b6b" + }, + { + "path": "wordpress/wp-includes/js/wp-lists.js", + "offset": 62735178, + "size": 25315, + "sha256": "8a8d737212048f5840f00e6acfe146f30fa7e48cd169cfdcdb7339f9e651d2b2" + }, + { + "path": "wordpress/wp-includes/js/wp-lists.min.js", + "offset": 62760493, + "size": 7521, + "sha256": "15f508d89006508e3ed91a3c92e71bf52ada8f81deb39e33ca900f8ad05d3781" + }, + { + "path": "wordpress/wp-includes/js/wp-pointer.js", + "offset": 62768014, + "size": 10233, + "sha256": "5c8bfda5637335fd4102ba3551e9a1989531f517618b0529f2a0daa0da61bd12" + }, + { + "path": "wordpress/wp-includes/js/wp-pointer.min.js", + "offset": 62778247, + "size": 3621, + "sha256": "7688d6c0bc721a9708d4f280bfc926b6bb25e2386300a906a7fe5fa31a334bc7" + }, + { + "path": "wordpress/wp-includes/js/wp-sanitize.js", + "offset": 62781868, + "size": 1622, + "sha256": "4c351bb5a337776ad7c09f7b16bf790d4bbb917ab6875bb3dc6a57296124b14d" + }, + { + "path": "wordpress/wp-includes/js/wp-sanitize.min.js", + "offset": 62783490, + "size": 402, + "sha256": "ba6705498c0364e9aeb9416ab78db24b90d409677d21bfe9c9f5559fc958216f" + }, + { + "path": "wordpress/wp-includes/js/wp-util.js", + "offset": 62783892, + "size": 4689, + "sha256": "fc19d23c5e66c2c4e67dac2cbab059269d9712f8d31c198633f18872e1f32e4f" + }, + { + "path": "wordpress/wp-includes/js/wp-util.min.js", + "offset": 62788581, + "size": 1431, + "sha256": "900f2dd9f6b66295a51f90c15a9271145b7964d8032725b4647ac44b409ceffa" + }, + { + "path": "wordpress/wp-includes/js/wpdialog.js", + "offset": 62790012, + "size": 569, + "sha256": "24cb158aa88db68c4b20a8cb48bcdc4f7c036f49763bf0354e63a96d4bd78bd4" + }, + { + "path": "wordpress/wp-includes/js/wpdialog.min.js", + "offset": 62790581, + "size": 281, + "sha256": "a292306e96dfefb17262cb4141c6de2bf669255829652cc7783f33802d10d4f8" + }, + { + "path": "wordpress/wp-includes/js/wplink.js", + "offset": 62790862, + "size": 21240, + "sha256": "c2cc6a90640c42844077ae2198aa97abc4a644fa689ee733f2d232b116dedec4" + }, + { + "path": "wordpress/wp-includes/js/wplink.min.js", + "offset": 62812102, + "size": 11317, + "sha256": "2e992599818f292d88a444616f0dd89241fea6946697512d93948a1ff15f9232" + }, + { + "path": "wordpress/wp-includes/js/zxcvbn-async.js", + "offset": 62823419, + "size": 821, + "sha256": "0141b2e10c8214567c6e0766e9e78128ae2a5c4aab2b40af10b1e28dee063174" + }, + { + "path": "wordpress/wp-includes/js/zxcvbn-async.min.js", + "offset": 62824240, + "size": 351, + "sha256": "e93e18f2f34a865e27d2d839eaccca6bec750d357f1c937980026d6d25507c2c" + }, + { + "path": "wordpress/wp-includes/js/zxcvbn.min.js", + "offset": 62824591, + "size": 822237, + "sha256": "daa6634ed8d6376bfd22d8f68942d00e1b56db0fa8c9f90ba2af52734dd5593b" + } + ] +} diff --git a/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts b/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts index 1713ee9b..04d97528 100644 --- a/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts +++ b/packages/runtime-cloudflare/src/wordpress-runtime-corpus.ts @@ -33,8 +33,14 @@ export function wordpressStaticArchivePath(pathname: string): string | null { } if (!decoded.startsWith("/") || decoded.includes("\\") || decoded.split("/").some((segment) => segment === "." || segment === "..")) return null const relative = decoded.slice(1) - if (!STATIC_ARCHIVE_ROOTS.some((root) => relative.startsWith(root)) || !STATIC_ASSET_EXTENSION.test(relative)) return null - return `wordpress/${relative}` + const archivePath = `wordpress/${relative}` + return isWordPressStaticAsset(archivePath) ? archivePath : null +} + +export function isWordPressStaticAsset(path: string): boolean { + if (!path.startsWith("wordpress/") || path.endsWith("/") || path.endsWith(".map")) return false + const relative = path.slice("wordpress/".length) + return STATIC_ARCHIVE_ROOTS.some((root) => relative.startsWith(root)) && STATIC_ASSET_EXTENSION.test(relative) } export function wordpressStaticContentType(archivePath: string): string { diff --git a/packages/runtime-cloudflare/src/wordpress-static-artifact.ts b/packages/runtime-cloudflare/src/wordpress-static-artifact.ts new file mode 100644 index 00000000..c4205e7b --- /dev/null +++ b/packages/runtime-cloudflare/src/wordpress-static-artifact.ts @@ -0,0 +1,37 @@ +import { wordpressStaticArchivePath } from "./wordpress-runtime-corpus.js" + +export const WORDPRESS_STATIC_ARTIFACT_SCHEMA = "wp-codebox/wordpress-static-artifact/v1" +export const WORDPRESS_STATIC_MAX_FILES = 5000 +export const WORDPRESS_STATIC_MAX_BYTES = 128 * 1024 * 1024 + +export interface WordPressStaticArtifactManifest { + schema: string + key: string + blob: { sha256: string; size: number } + source: { url: string; version: string; identity?: string } + files: Array<{ path: string; offset: number; size: number; sha256: string }> +} + +export function wordpressStaticArtifactKey(sha256: string): string { + return `runtime/wordpress-static/${sha256}.bin` +} + +export function validateWordPressStaticArtifactManifest(manifest: WordPressStaticArtifactManifest): void { + if (manifest.schema !== WORDPRESS_STATIC_ARTIFACT_SCHEMA) throw new Error("WordPress static artifact schema is invalid.") + if (!/^[a-f0-9]{64}$/.test(manifest.blob.sha256) || manifest.key !== wordpressStaticArtifactKey(manifest.blob.sha256)) throw new Error("WordPress static artifact key is invalid.") + if (!Number.isSafeInteger(manifest.blob.size) || manifest.blob.size < 1 || manifest.blob.size > WORDPRESS_STATIC_MAX_BYTES) throw new Error("WordPress static artifact size is invalid.") + if (!manifest.source.url.startsWith("https://") || !manifest.source.version) throw new Error("WordPress static artifact source is invalid.") + if (!Array.isArray(manifest.files) || !manifest.files.length || manifest.files.length > WORDPRESS_STATIC_MAX_FILES) throw new Error("WordPress static artifact file count is invalid.") + let expectedOffset = 0 + let previousPath = "" + for (const file of manifest.files) { + const pathname = file.path.startsWith("wordpress/") ? `/${file.path.slice("wordpress/".length)}` : "" + if (!pathname || wordpressStaticArchivePath(pathname) !== file.path || file.path <= previousPath || !Number.isSafeInteger(file.offset) + || file.offset !== expectedOffset || !Number.isSafeInteger(file.size) || file.size < 0 || !/^[a-f0-9]{64}$/.test(file.sha256)) { + throw new Error("WordPress static artifact contains an invalid file.") + } + expectedOffset += file.size + previousPath = file.path + } + if (expectedOffset !== manifest.blob.size) throw new Error("WordPress static artifact file ranges do not cover the blob.") +} diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 80fd5fcb..8fd4d3fa 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -1,5 +1,5 @@ import { loadPHPRuntime, PHP, type PHPRequestHandler } from "@php-wasm/universal" -import { decodeRemoteZip, decodeZip } from "@php-wasm/stream-compression" +import { decodeZip } from "@php-wasm/stream-compression" import { bootWordPressAndRequestHandler, type WordPressInstallMode } from "@wp-playground/wordpress" // The PHP-WASM package publishes this Emscripten loader without TypeScript declarations. // @ts-expect-error The adjacent Wasm declaration covers the compiled binary import. @@ -13,6 +13,7 @@ import { R2_UPLOAD_OBJECT_PREFIX, validateUploadManifestFiles, validateUploadMet import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" import { materializeWordPressRuntimeArtifact, type WordPressRuntimeArtifactManifest } from "./wordpress-runtime-artifact.js" +import { validateWordPressStaticArtifactManifest, type WordPressStaticArtifactManifest } from "./wordpress-static-artifact.js" import type { MarkdownPointer } from "./state-coordinator.js" export { WordPressStateCoordinator } from "./state-coordinator.js" import markdownDatabaseIntegrationRuntime from "../assets/markdown-database-integration-runtime.zip" @@ -21,10 +22,12 @@ import canonicalMarkdownSeedManifest from "../assets/markdown-database-integrati import markdownPrimaryBootstrapIndex from "../assets/markdown-primary-bootstrap-index.sqlite" import wordpressInstallSeed from "../assets/wordpress-install-seed.sqlite" import wordpressRuntimeArtifactManifest from "../assets/wordpress-runtime-artifact.json" with { type: "json" } +import wordpressStaticArtifactManifest from "../assets/wordpress-static-artifact.json" with { type: "json" } const PHP_VERSION = "8.5.8" -// Browser assets must come from the same immutable WordPress release as the server corpus. -const WORDPRESS_ARCHIVE_URL = (wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest).source.url +const wordpressStaticArtifact = wordpressStaticArtifactManifest as WordPressStaticArtifactManifest +validateWordPressStaticArtifactManifest(wordpressStaticArtifact) +const wordpressStaticFiles = new Map(wordpressStaticArtifact.files.map((file) => [file.path, file])) const SQLITE_INTEGRATION_ARCHIVE_URL = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip" const MARKDOWN_DATABASE_INTEGRATION_REVISION = "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c" const SITE_URL = "https://wp-codebox-runtime.invalid" @@ -85,7 +88,7 @@ interface Env { export default { async fetch(request: Request, env: Env): Promise { - const staticResponse = await serveWordPressStaticAsset(request) + const staticResponse = await serveWordPressStaticAsset(request, env.WORDPRESS_STATE_BUCKET) if (staticResponse) return staticResponse const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") @@ -679,16 +682,16 @@ async function packagedCanonicalMarkdownSeed(): Promise { async function runBootProbe(phase: string, bucket: R2Bucket): Promise { if (phase === "wordpress-archive" || phase === "sqlite-archive") { - const archive = phase === "wordpress-archive" - ? await fetchArchive(WORDPRESS_ARCHIVE_URL, "wordpress.zip") - : await fetchArchive(SQLITE_INTEGRATION_ARCHIVE_URL, "sqlite-database-integration.zip") - return probeResponse(phase, { archiveBytes: archive.size }) + const archiveBytes = phase === "wordpress-archive" + ? (await readWordPressRuntimeArtifact(bucket)).byteLength + : (await fetchArchive(SQLITE_INTEGRATION_ARCHIVE_URL, "sqlite-database-integration.zip")).size + return probeResponse(phase, { archiveBytes }) } if (phase === "archives") { - const wordpressZip = await fetchArchive(WORDPRESS_ARCHIVE_URL, "wordpress.zip") + const wordpressBytes = await readWordPressRuntimeArtifact(bucket) const sqliteZip = await fetchArchive(SQLITE_INTEGRATION_ARCHIVE_URL, "sqlite-database-integration.zip") - return probeResponse(phase, { wordpressArchiveBytes: wordpressZip.size, sqliteArchiveBytes: sqliteZip.size }) + return probeResponse(phase, { wordpressArchiveBytes: wordpressBytes.byteLength, sqliteArchiveBytes: sqliteZip.size }) } if (phase === "php") { @@ -702,14 +705,12 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } if (phase === "php-wordpress-archive" || phase === "wordpress-archive-php") { - const archive = phase === "wordpress-archive-php" - ? await fetchArchive(WORDPRESS_ARCHIVE_URL, "wordpress.zip") - : undefined + const archive = phase === "wordpress-archive-php" ? await readWordPressRuntimeArtifact(bucket) : undefined const php = new PHP(await createPhpRuntime()) try { - const wordpressZip = archive ?? await fetchArchive(WORDPRESS_ARCHIVE_URL, "wordpress.zip") + const wordpressZip = archive ?? await readWordPressRuntimeArtifact(bucket) const phpVersion = (await php.run({ code: " const runtime = await bootWordPressRuntime( phase === "full" || phase === "streamed-wordpress" ? "install-from-existing-files" : "do-not-attempt-installing", phase !== "wordpress-files", - phase === "streamed-sqlite" || phase === "streamed-wordpress", + true, undefined, undefined, undefined, @@ -877,7 +878,7 @@ require '/wordpress/wp-load.php';` async function bootWordPressRuntime( wordpressInstallMode: WordPressInstallMode = "install-from-existing-files", includeSqlite = true, - streamWordPressFiles = false, + streamWordPressFiles = true, databaseSeed?: Uint8Array, markdownFiles?: RuntimeFile[], markdownIndexSeed?: Uint8Array, @@ -932,7 +933,7 @@ async function bootWordPressRuntime( maxPhpInstances: 1, phpVersion: "8.5", siteUrl, - wordPressZip: streamWordPressFiles ? undefined : fetchArchive(WORDPRESS_ARCHIVE_URL, "wordpress.zip"), + wordPressZip: undefined, sqliteIntegrationPluginZip: includeSqlite ? fetchArchive(SQLITE_INTEGRATION_ARCHIVE_URL, "sqlite-database-integration.zip") : undefined, wordpressInstallMode, }) @@ -1078,6 +1079,15 @@ async function materializeWordPressServerFiles(php: PHP, bucket: R2Bucket | unde return materializeWordPressRuntimeArtifact(php, bucket, wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest) } +async function readWordPressRuntimeArtifact(bucket: R2Bucket): Promise { + const manifest = wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest + const object = await bucket.get(manifest.key) + if (!object) throw new Error("WordPress runtime corpus artifact is unavailable.") + const bytes = new Uint8Array(await object.arrayBuffer()) + if (bytes.byteLength !== manifest.archive.size || await sha256Hex(bytes) !== manifest.archive.sha256) throw new Error("WordPress runtime corpus artifact integrity check failed.") + return bytes +} + async function serveWordPressUpload(request: Request, bucket: R2Bucket, coordinator: DurableObjectStub): Promise { if (request.method !== "GET" && request.method !== "HEAD") return null const url = new URL(request.url) @@ -1159,41 +1169,44 @@ function wordPressUploadContentType(path: string): string { } as Record)[extension] ?? "application/octet-stream" } -async function serveWordPressStaticAsset(request: Request): Promise { +async function serveWordPressStaticAsset(request: Request, bucket: R2Bucket): Promise { if (request.method !== "GET" && request.method !== "HEAD") return null const archivePath = wordpressStaticArchivePath(new URL(request.url).pathname) if (!archivePath) return null + const file = wordpressStaticFiles.get(archivePath) + if (!file) return new Response("Not found", { status: 404 }) const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default + const cacheUrl = new URL(request.url) + cacheUrl.searchParams.set("__wp_codebox_static_artifact", wordpressStaticArtifact.blob.sha256) + const cacheRequest = new Request(cacheUrl, request) if (request.method === "GET" && cache) { try { - const cached = await cache.match(request) + const cached = await cache.match(cacheRequest) if (cached) return cached } catch { // Cache availability is an optimization, never a dependency. } } - const decoder = new TextDecoder() - const stream = await decodeRemoteZip(WORDPRESS_ARCHIVE_URL, (entry: { path: Uint8Array }) => decoder.decode(entry.path) === archivePath) - const reader = stream.getReader() - const { done, value: entry } = await reader.read() - if (done || !entry) return new Response("Not found", { status: 404 }) - const path = entry instanceof File ? entry.name : decoder.decode(entry.path) - if (path !== archivePath) return new Response("Not found", { status: 404 }) - const bytes = entry instanceof File ? new Uint8Array(await entry.arrayBuffer()) : entry.bytes + const object = file.size ? await bucket.get(wordpressStaticArtifact.key, { range: { offset: file.offset, length: file.size } }) : undefined + if (file.size && !object) return new Response("WordPress static artifact is unavailable.", { status: 503 }) + const bytes = object ? new Uint8Array(await object.arrayBuffer()) : new Uint8Array() + if (bytes.byteLength !== file.size || await sha256Hex(bytes) !== file.sha256) return new Response("WordPress static artifact integrity check failed.", { status: 502 }) const response = new Response(request.method === "HEAD" ? null : bytes, { headers: { - "cache-control": "public, max-age=3600", + "cache-control": "public, max-age=31536000, immutable", + "content-length": String(file.size), "content-type": wordpressStaticContentType(archivePath), - "x-wp-codebox-static": "wordpress-archive", + "etag": `"${file.sha256}"`, + "x-wp-codebox-static": "r2-range", }, }) if (request.method === "GET" && cache) { try { - await cache.put(request, response.clone()) + await cache.put(cacheRequest, response.clone()) } catch { - // A full or unavailable Worker cache must not affect the archive response. + // A full or unavailable Worker cache must not affect the R2 response. } } return response diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 874c8388..c2ae0fd7 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -1,4 +1,5 @@ import { spawn } from "node:child_process" +import { createHash } from "node:crypto" import { mkdtemp, rm } from "node:fs/promises" import { tmpdir } from "node:os" import { join } from "node:path" @@ -269,20 +270,25 @@ async function assertLinkedAssets(html, label) { const body = await response.text() assertNoPhpDiagnostics(body, `${label} asset ${match[1]}`) if (!response.ok || !body.length) throw new Error(`Missing ${label} asset ${match[1]}: ${response.status}`) - if (response.headers.get("x-wp-codebox-static") !== "wordpress-archive") throw new Error(`${label} asset ${match[1]} did not bypass PHP through the WordPress archive static path.`) + if (response.headers.get("x-wp-codebox-static") !== "r2-range") throw new Error(`${label} asset ${match[1]} did not bypass PHP through the WordPress R2 range path.`) } } async function assertStaticResponseSemantics() { const asset = `${origin}/wp-includes/js/jquery/jquery.min.js?ver=3.7.1` const get = await request(asset) - if (!get.ok || !get.headers.get("content-type")?.includes("javascript") || get.headers.get("x-wp-codebox-static") !== "wordpress-archive" || !get.headers.get("cache-control")?.includes("max-age")) throw new Error(`Unexpected static asset response: ${get.status}`) + if (!get.ok || !get.headers.get("content-type")?.includes("javascript") || get.headers.get("x-wp-codebox-static") !== "r2-range" || !get.headers.get("cache-control")?.includes("immutable") || !get.headers.get("etag")) throw new Error(`Unexpected static asset response: ${get.status}`) + const getBody = Buffer.from(await get.arrayBuffer()) + if (get.headers.get("etag") !== `"${createHash("sha256").update(getBody).digest("hex")}"`) throw new Error("Static GET ETag did not match the served bytes.") const head = await request(asset, { method: "HEAD" }) - if (!head.ok || head.headers.get("x-wp-codebox-static") !== "wordpress-archive" || (await head.text()) !== "") throw new Error("Static HEAD did not preserve headers with an empty body.") + const headBody = await head.text() + if (!head.ok || head.headers.get("x-wp-codebox-static") !== "r2-range" || head.headers.get("content-length") !== String(getBody.byteLength) || head.headers.get("etag") !== get.headers.get("etag") || headBody !== "") { + throw new Error(`Static HEAD did not preserve headers with an empty body: status=${head.status} source=${head.headers.get("x-wp-codebox-static")} get-bytes=${getBody.byteLength} head-length=${head.headers.get("content-length")} body=${headBody.length}.`) + } const missing = await request(`${origin}/wp-includes/js/does-not-exist.min.js`) if (missing.status !== 404) throw new Error(`Missing static archive asset returned ${missing.status}, not 404.`) const source = await request(`${origin}/wp-includes/version.php`) - if (source.headers.get("x-wp-codebox-static") === "wordpress-archive") throw new Error("Static handler exposed a PHP source file.") + if (source.headers.get("x-wp-codebox-static") === "r2-range") throw new Error("Static handler exposed a PHP source file.") } async function request(target, options = {}) { diff --git a/scripts/generate-cloudflare-wordpress-runtime-corpus.ts b/scripts/generate-cloudflare-wordpress-runtime-corpus.ts index 88bfed4e..036e7d5f 100644 --- a/scripts/generate-cloudflare-wordpress-runtime-corpus.ts +++ b/scripts/generate-cloudflare-wordpress-runtime-corpus.ts @@ -2,13 +2,16 @@ import { createHash } from "node:crypto" import { mkdir, writeFile } from "node:fs/promises" import { dirname, resolve } from "node:path" import { decodeZip, encodeZip } from "@php-wasm/stream-compression" -import { isWordPressRuntimeFile } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" +import { isWordPressRuntimeFile, isWordPressStaticAsset } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" import { WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, wordpressRuntimeArtifactKey, type WordPressRuntimeArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" +import { WORDPRESS_STATIC_ARTIFACT_SCHEMA, validateWordPressStaticArtifactManifest, wordpressStaticArtifactKey, type WordPressStaticArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-static-artifact.js" const sourceUrl = process.env.WORDPRESS_RUNTIME_ARCHIVE_URL ?? "https://downloads.wordpress.org/release/wordpress-7.0.2.zip" const sourceVersion = process.env.WORDPRESS_RUNTIME_VERSION ?? "7.0.2" const output = resolve(process.env.WORDPRESS_RUNTIME_ARTIFACT_OUTPUT ?? "artifacts/cloudflare-wordpress-runtime-corpus.zip") const manifestOutput = resolve("packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json") +const staticOutput = resolve(process.env.WORDPRESS_STATIC_ARTIFACT_OUTPUT ?? "artifacts/cloudflare-wordpress-static-corpus.bin") +const staticManifestOutput = resolve("packages/runtime-cloudflare/assets/wordpress-static-artifact.json") const response = await fetch(sourceUrl) if (!response.ok || !response.body) throw new Error(`Unable to download WordPress archive: ${response.status}.`) const identity = response.headers.get("etag") ?? response.headers.get("last-modified") ?? undefined @@ -20,6 +23,8 @@ const selected = files .map((file) => new File([file], file.name, { lastModified: 0 })) selected.sort((left, right) => left.name.localeCompare(right.name)) if (!selected.length) throw new Error("WordPress archive did not yield a runtime corpus.") +const staticSelected = files.filter((file) => isWordPressStaticAsset(file.name)).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0) +if (!staticSelected.length) throw new Error("WordPress archive did not yield a static asset corpus.") const archive = new Uint8Array(await new Response(encodeZip(selected)).arrayBuffer()) const archiveSha256 = sha256Hex(archive) const manifest: WordPressRuntimeArtifactManifest = { @@ -32,7 +37,33 @@ const manifest: WordPressRuntimeArtifactManifest = { await mkdir(dirname(output), { recursive: true }) await writeFile(output, archive) await writeFile(manifestOutput, `${JSON.stringify(manifest, null, 2)}\n`) -console.log(JSON.stringify({ key: manifest.key, archiveBytes: archive.byteLength, files: manifest.files.length, sha256: archiveSha256, source: manifest.source })) + +const staticChunks: Uint8Array[] = [] +const staticFiles: WordPressStaticArtifactManifest["files"] = [] +let staticOffset = 0 +for (const file of staticSelected) { + const bytes = new Uint8Array(await file.arrayBuffer()) + staticFiles.push({ path: file.name, offset: staticOffset, size: bytes.byteLength, sha256: sha256Hex(bytes) }) + if (bytes.byteLength) staticChunks.push(bytes) + staticOffset += bytes.byteLength +} +const staticBlob = new Uint8Array(await new Blob(staticChunks).arrayBuffer()) +const staticSha256 = sha256Hex(staticBlob) +const staticManifest: WordPressStaticArtifactManifest = { + schema: WORDPRESS_STATIC_ARTIFACT_SCHEMA, + key: wordpressStaticArtifactKey(staticSha256), + blob: { sha256: staticSha256, size: staticBlob.byteLength }, + source: { url: sourceUrl, version: sourceVersion, ...(identity ? { identity } : {}) }, + files: staticFiles, +} +validateWordPressStaticArtifactManifest(staticManifest) +await writeFile(staticOutput, staticBlob) +await writeFile(staticManifestOutput, `${JSON.stringify(staticManifest, null, 2)}\n`) +console.log(JSON.stringify({ + runtime: { key: manifest.key, bytes: archive.byteLength, files: manifest.files.length, sha256: archiveSha256 }, + static: { key: staticManifest.key, bytes: staticBlob.byteLength, files: staticManifest.files.length, sha256: staticSha256 }, + source: manifest.source, +})) function sha256Hex(bytes: Uint8Array): string { return createHash("sha256").update(bytes).digest("hex") diff --git a/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs b/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs index 55b266ab..28c6690f 100644 --- a/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs +++ b/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs @@ -13,12 +13,21 @@ const manifest = JSON.parse(await readFile("packages/runtime-cloudflare/assets/w const archive = await readFile("artifacts/cloudflare-wordpress-runtime-corpus.zip") const actual = createHash("sha256").update(archive).digest("hex") if (actual !== manifest.archive.sha256 || archive.byteLength !== manifest.archive.size || manifest.key !== `runtime/wordpress/${actual}.zip`) throw new Error("Local WordPress runtime artifact does not match its content-addressed manifest.") +const staticManifest = JSON.parse(await readFile("packages/runtime-cloudflare/assets/wordpress-static-artifact.json", "utf8")) +const staticBlob = await readFile("artifacts/cloudflare-wordpress-static-corpus.bin") +const staticActual = createHash("sha256").update(staticBlob).digest("hex") +if (staticActual !== staticManifest.blob.sha256 || staticBlob.byteLength !== staticManifest.blob.size || staticManifest.key !== `runtime/wordpress-static/${staticActual}.bin`) throw new Error("Local WordPress static artifact does not match its content-addressed manifest.") -const command = ["exec", "--", "wrangler", "r2", "object", "put", `wp-codebox-runtime-chubes/${manifest.key}`, "--file", "artifacts/cloudflare-wordpress-runtime-corpus.zip", local ? "--local" : "--remote"] -if (persistTo) command.push("--persist-to", persistTo) -await new Promise((resolve, reject) => { - const child = spawn("npm", command, { cwd: process.cwd(), stdio: "inherit" }) - child.on("error", reject) - child.on("exit", (code) => code === 0 ? resolve(undefined) : reject(new Error(`Wrangler R2 provisioning failed with status ${code}.`))) -}) -console.log(`Provisioned ${manifest.key} (${manifest.archive.size} bytes).`) +for (const artifact of [ + { key: manifest.key, size: manifest.archive.size, file: "artifacts/cloudflare-wordpress-runtime-corpus.zip" }, + { key: staticManifest.key, size: staticManifest.blob.size, file: "artifacts/cloudflare-wordpress-static-corpus.bin" }, +]) { + const command = ["exec", "--", "wrangler", "r2", "object", "put", `wp-codebox-runtime-chubes/${artifact.key}`, "--file", artifact.file, local ? "--local" : "--remote"] + if (persistTo) command.push("--persist-to", persistTo) + await new Promise((resolve, reject) => { + const child = spawn("npm", command, { cwd: process.cwd(), stdio: "inherit" }) + child.on("error", reject) + child.on("exit", (code) => code === 0 ? resolve(undefined) : reject(new Error(`Wrangler R2 provisioning failed with status ${code}.`))) + }) + console.log(`Provisioned ${artifact.key} (${artifact.size} bytes).`) +} diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 1f1ea70d..740a5476 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -14,6 +14,7 @@ import { WordPressStateCoordinator } from "../packages/runtime-cloudflare/src/st import { MAX_UPLOAD_FILE_BYTES, R2_UPLOAD_OBJECT_PREFIX, validateUploadManifestFiles, validateUploadMetadata } from "../packages/runtime-cloudflare/src/upload-persistence.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" import { materializeWordPressRuntimeArtifact, WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, wordpressRuntimeArtifactKey, type WordPressRuntimeArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" +import { validateWordPressStaticArtifactManifest, WORDPRESS_STATIC_ARTIFACT_SCHEMA, wordpressStaticArtifactKey, type WordPressStaticArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-static-artifact.js" const execFileAsync = promisify(execFile) @@ -256,15 +257,17 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /sites\/default\/markdown\/revisions\/\$\{candidate\.revision\}\.json/) assert.match(worker, /canonicalWordPressAuthConstants\(env\)/) assert.match(worker, /authConstants/) - assert.match(worker, /const staticResponse = await serveWordPressStaticAsset\(request\)/) - assert.ok(worker.indexOf("const staticResponse = await serveWordPressStaticAsset(request)") < worker.indexOf("const coordinator = env.WORDPRESS_STATE.getByName")) + assert.match(worker, /const staticResponse = await serveWordPressStaticAsset\(request, env\.WORDPRESS_STATE_BUCKET\)/) + assert.ok(worker.indexOf("const staticResponse = await serveWordPressStaticAsset(request, env.WORDPRESS_STATE_BUCKET)") < worker.indexOf("const coordinator = env.WORDPRESS_STATE.getByName")) assert.match(worker, /CONCATENATE_SCRIPTS: false/) assert.match(worker, /SCRIPT_DEBUG: false/) - assert.match(worker, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL, \(entry: \{ path: Uint8Array \}\) => decoder\.decode\(entry\.path\) === archivePath\)/) + assert.doesNotMatch(worker, /decodeRemoteZip|WORDPRESS_ARCHIVE_URL/) + assert.match(worker, /bucket\.get\(wordpressStaticArtifact\.key, \{ range: \{ offset: file\.offset, length: file\.size \} \}\)/) + assert.match(worker, /await sha256Hex\(bytes\) !== file\.sha256/) assert.match(worker, /request\.method === "HEAD" \? null : bytes/) - assert.match(worker, /"x-wp-codebox-static": "wordpress-archive"/) - assert.match(worker, /cache\.match\(request\)/) - assert.match(worker, /cache\.put\(request, response\.clone\(\)\)/) + assert.match(worker, /"x-wp-codebox-static": "r2-range"/) + assert.match(worker, /cache\.match\(cacheRequest\)/) + assert.match(worker, /cache\.put\(cacheRequest, response\.clone\(\)\)/) assert.match(worker, /coordinatorCall\(coordinator, request\.url, "state"\)/) assert.ok(worker.indexOf('coordinatorCall(coordinator, request.url, "state")') < worker.indexOf("let lease = await acquireLease")) assert.match(worker, /matchWordPressPageCache\(request, lease\.pointer, env\.WORDPRESS_STATE_BUCKET\)/) @@ -364,20 +367,39 @@ test("WordPress runtime artifact validation rejects path traversal and invalid b assert.equal(writes, 0, "unsafe manifests are rejected before any archive write") }) +test("WordPress static artifacts require complete, ordered, content-addressed ranges", () => { + const sha256 = "a".repeat(64) + const manifest: WordPressStaticArtifactManifest = { + schema: WORDPRESS_STATIC_ARTIFACT_SCHEMA, + key: wordpressStaticArtifactKey(sha256), + blob: { sha256, size: 3 }, + source: { url: "https://downloads.wordpress.org/release/wordpress-7.0.2.zip", version: "7.0.2" }, + files: [{ path: "wordpress/wp-includes/js/a.js", offset: 0, size: 3, sha256: "b".repeat(64) }], + } + assert.doesNotThrow(() => validateWordPressStaticArtifactManifest(manifest)) + assert.throws(() => validateWordPressStaticArtifactManifest({ ...manifest, files: [{ ...manifest.files[0], path: "wordpress/../secret.js" }] }), /invalid file/) + assert.throws(() => validateWordPressStaticArtifactManifest({ ...manifest, files: [{ ...manifest.files[0], offset: 1 }] }), /invalid file/) + assert.throws(() => validateWordPressStaticArtifactManifest({ ...manifest, blob: { ...manifest.blob, size: 4 } }), /do not cover/) +}) + test("WordPress runtime corpus generator keeps the ZIP outside the Worker bundle", async () => { const generator = await readFile(new URL("../scripts/generate-cloudflare-wordpress-runtime-corpus.ts", import.meta.url), "utf8") const artifact = await readFile(new URL("../packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts", import.meta.url), "utf8") const manifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json", import.meta.url), "utf8")) as WordPressRuntimeArtifactManifest + const staticManifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-static-artifact.json", import.meta.url), "utf8")) as WordPressStaticArtifactManifest assert.match(generator, /const response = await fetch\(sourceUrl\)/) assert.match(generator, /decodeZip\(response\.body\)/) assert.doesNotMatch(generator, /decodeRemoteZip/) assert.match(generator, /encodeZip\(selected\)/) assert.match(generator, /lastModified: 0/) assert.match(generator, /artifacts\/cloudflare-wordpress-runtime-corpus\.zip/) + assert.match(generator, /artifacts\/cloudflare-wordpress-static-corpus\.bin/) assert.doesNotMatch(artifact, /decodeZip/) assert.match(artifact, /php\.writeFile\(WORDPRESS_RUNTIME_ARCHIVE_TEMP_PATH, archiveBytes\)/) assert.equal(manifest.key, wordpressRuntimeArtifactKey(manifest.archive.sha256)) assert.ok(manifest.files.length > 0) + assert.doesNotThrow(() => validateWordPressStaticArtifactManifest(staticManifest)) + assert.ok(staticManifest.files.length > 0) }) test("Cloudflare coordinator serializes leases, promotes with CAS, and recovers stale leases", async () => { diff --git a/tests/cloudflare-wordpress-archive-corpus.test.ts b/tests/cloudflare-wordpress-archive-corpus.test.ts index 3d77993d..85154715 100644 --- a/tests/cloudflare-wordpress-archive-corpus.test.ts +++ b/tests/cloudflare-wordpress-archive-corpus.test.ts @@ -1,8 +1,9 @@ import assert from "node:assert/strict" import test from "node:test" import { decodeZip } from "@php-wasm/stream-compression" -import { isWordPressRuntimeFile, summarizeWordPressRuntimeCorpus, type WordPressArchiveEntry } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" +import { isWordPressRuntimeFile, isWordPressStaticAsset, summarizeWordPressRuntimeCorpus, type WordPressArchiveEntry } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" import { WORDPRESS_RUNTIME_MAX_FILES, WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" +import { WORDPRESS_STATIC_MAX_BYTES, WORDPRESS_STATIC_MAX_FILES } from "../packages/runtime-cloudflare/src/wordpress-static-artifact.js" const WORDPRESS_ARCHIVE_URL = "https://wordpress.org/latest.zip" @@ -19,6 +20,8 @@ test("WordPress production runtime corpus stays within the Worker materializatio const archivePaths = new Set(entries.map((entry) => entry.path)) const legacy = entries.filter((entry) => !entry.isDirectory && (entry.path.startsWith("wordpress/wp-admin/") || /\.(?:php|json|crt|html|css|js|mjs|woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/.test(entry.path))) const excluded = entries.filter((entry) => !entry.isDirectory && !isWordPressRuntimeFile(entry.path, archivePaths)) + const staticAssets = entries.filter((entry) => !entry.isDirectory && isWordPressStaticAsset(entry.path)) + const staticBytes = staticAssets.reduce((total, entry) => total + entry.uncompressedSize, 0) const byGroup = (files: WordPressArchiveEntry[]) => Object.entries(files.reduce>((groups, entry) => { const group = entry.path.split("/").slice(0, 3).join("/") const current = groups[group] ?? { files: 0, bytes: 0 } @@ -32,6 +35,8 @@ test("WordPress production runtime corpus stays within the Worker materializatio archiveEntries: entries.length, selectedFiles: corpus.selectedFiles, selectedBytes: corpus.selectedBytes, + staticFiles: staticAssets.length, + staticBytes, legacyFiles: legacy.length, legacyBytes: legacy.reduce((total, entry) => total + entry.uncompressedSize, 0), largestExcludedGroups: byGroup(excluded), @@ -40,4 +45,6 @@ test("WordPress production runtime corpus stays within the Worker materializatio assert.ok(corpus.selectedFiles > 0, "WordPress archive did not yield a runtime corpus.") assert.ok(corpus.selectedFiles <= WORDPRESS_RUNTIME_MAX_FILES, `Selected ${corpus.selectedFiles} files exceeds the ${WORDPRESS_RUNTIME_MAX_FILES}-file runtime corpus budget.`) assert.ok(corpus.selectedBytes <= WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES, `Selected ${corpus.selectedBytes} bytes exceeds the ${WORDPRESS_RUNTIME_MAX_UNCOMPRESSED_BYTES}-byte runtime corpus budget.`) + assert.ok(staticAssets.length > 0 && staticAssets.length <= WORDPRESS_STATIC_MAX_FILES, `Selected ${staticAssets.length} static files exceeds the ${WORDPRESS_STATIC_MAX_FILES}-file budget.`) + assert.ok(staticBytes <= WORDPRESS_STATIC_MAX_BYTES, `Selected ${staticBytes} static bytes exceeds the ${WORDPRESS_STATIC_MAX_BYTES}-byte budget.`) }) From cee31d52b59279066225c97cf6ca17fe4be9b511 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 18:51:08 -0400 Subject: [PATCH 85/87] Load Cloudflare runtime dependencies from R2 --- packages/runtime-cloudflare/README.md | 6 +-- .../sqlite-database-integration-artifact.json | 14 +++++++ .../src/runtime-archive-artifact.ts | 38 +++++++++++++++++++ packages/runtime-cloudflare/src/worker.ts | 24 ++++++------ ...ate-cloudflare-wordpress-runtime-corpus.ts | 21 ++++++++++ ...on-cloudflare-wordpress-runtime-corpus.mjs | 5 +++ tests/cloudflare-runtime.test.ts | 23 +++++++++++ 7 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 packages/runtime-cloudflare/assets/sqlite-database-integration-artifact.json create mode 100644 packages/runtime-cloudflare/src/runtime-archive-artifact.ts diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index de745464..3f4eda36 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -16,15 +16,15 @@ The Worker forwards browser cookies directly to Playground and disables Playgrou The bundled MDI source is pinned to immutable commit `2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c` from [MDI PR #139](https://github.com/Automattic/markdown-database-integration/pull/139). The bundle generator, worker provenance, and source-contract test use the same revision. -WordPress server files and browser assets are separate deployment artifacts, never Worker-module imports. `generate:cloudflare-wordpress-runtime-corpus` downloads the pinned WordPress release once. It emits a deterministic server ZIP containing only `isWordPressRuntimeFile()` entries and one concatenated browser-asset blob containing the complete supported `wp-admin`, `wp-includes`, and bundled-theme surface. Checked-in manifests record the content-addressed R2 keys, hashes, budgets, and each browser asset's exact byte range. Cold boot validates and streams the server ZIP into PHP MEMFS. Browser requests use one bounded R2 range read, validate the selected bytes, and populate an immutable artifact-versioned Worker cache without fetching or decoding the WordPress release at request time. +WordPress server files, browser assets, and pinned runtime dependencies are separate deployment artifacts, never Worker-module imports. `generate:cloudflare-wordpress-runtime-corpus` emits a deterministic server ZIP containing only `isWordPressRuntimeFile()` entries, one concatenated browser-asset blob containing the complete supported `wp-admin`, `wp-includes`, and bundled-theme surface, and the pinned SQLite integration ZIP. Checked-in manifests record content-addressed R2 keys, hashes, budgets, provenance, and each browser asset's exact byte range. Cold boot validates and streams the server ZIP into PHP MEMFS while loading SQLite integration from R2. Browser requests use one bounded R2 range read, validate the selected bytes, and populate an immutable artifact-versioned Worker cache. Runtime requests never fetch WordPress or SQLite integration release archives from third-party origins. Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpress/wp-settings.php` after the R2 WordPress corpus is materialized and before WordPress executes. The disabled-cron scheduling policy requires exactly one canonical `do_action( 'init' );` needle and fails closed otherwise. When `DISABLE_WP_CRON` is true, it removes only core's `wp_cron`, `wp_schedule_delete_old_privacy_export_files`, and `wp_schedule_update_checks` callbacks through public `remove_action()` before executing the original `init` call once. This prevents browser requests from recreating cron work that cannot execute in the PHP-WASM/MDI shutdown path. Durable cron execution belongs to a separate explicit queue or scheduled-Worker contract. ## Verification 1. Run `npm run generate:cloudflare-canonical-mdi-seed` and `npm run generate:cloudflare-wordpress-runtime-corpus` to regenerate deterministic runtime artifacts and manifests. -2. Run `npm run provision:cloudflare-wordpress-runtime-corpus -- --local --persist-to ` to verify and upload both exact content-addressed artifacts into isolated local R2 storage. For an authorized deployment, run the provisioner with `--remote` and require both uploads to succeed before deploying the Worker that imports their manifests. +2. Run `npm run provision:cloudflare-wordpress-runtime-corpus -- --local --persist-to ` to verify and upload all exact content-addressed artifacts into isolated local R2 storage. For an authorized deployment, run the provisioner with `--remote` and require every upload to succeed before deploying the Worker that imports their manifests. 3. Run `npm run test:cloudflare-runtime` for routing, canonical-state, artifact validation, source contract, and TypeScript coverage. 4. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. -5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions both artifacts before workerd starts, then injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. +5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions all artifacts before workerd starts, then injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/assets/sqlite-database-integration-artifact.json b/packages/runtime-cloudflare/assets/sqlite-database-integration-artifact.json new file mode 100644 index 00000000..6c46dc12 --- /dev/null +++ b/packages/runtime-cloudflare/assets/sqlite-database-integration-artifact.json @@ -0,0 +1,14 @@ +{ + "schema": "wp-codebox/runtime-archive-artifact/v1", + "name": "sqlite-database-integration", + "key": "runtime/archives/sqlite-database-integration/f3bf0ac0bd80d374456c3742c3e2f987d060e8e35f4fe99925de2404ce05cbfb.zip", + "archive": { + "sha256": "f3bf0ac0bd80d374456c3742c3e2f987d060e8e35f4fe99925de2404ce05cbfb", + "size": 267173 + }, + "source": { + "url": "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip", + "version": "2.2.23", + "identity": "\"0x8DE9712B1086587\"" + } +} diff --git a/packages/runtime-cloudflare/src/runtime-archive-artifact.ts b/packages/runtime-cloudflare/src/runtime-archive-artifact.ts new file mode 100644 index 00000000..9309d144 --- /dev/null +++ b/packages/runtime-cloudflare/src/runtime-archive-artifact.ts @@ -0,0 +1,38 @@ +export const RUNTIME_ARCHIVE_ARTIFACT_SCHEMA = "wp-codebox/runtime-archive-artifact/v1" +export const RUNTIME_ARCHIVE_MAX_BYTES = 8 * 1024 * 1024 + +export interface RuntimeArchiveArtifactManifest { + schema: typeof RUNTIME_ARCHIVE_ARTIFACT_SCHEMA + name: string + key: string + archive: { sha256: string; size: number } + source: { url: string; version?: string; identity?: string } +} + +export function runtimeArchiveArtifactKey(name: string, sha256: string): string { + if (!/^[a-z0-9]+(?:-[a-z0-9]+)*$/.test(name)) throw new Error("Runtime archive artifact name is invalid.") + if (!/^[a-f0-9]{64}$/.test(sha256)) throw new Error("Runtime archive artifact hash must be a SHA-256 digest.") + return `runtime/archives/${name}/${sha256}.zip` +} + +export function validateRuntimeArchiveArtifactManifest(manifest: RuntimeArchiveArtifactManifest): void { + if (manifest.schema !== RUNTIME_ARCHIVE_ARTIFACT_SCHEMA) throw new Error("Runtime archive artifact schema is invalid.") + if (manifest.key !== runtimeArchiveArtifactKey(manifest.name, manifest.archive.sha256)) throw new Error("Runtime archive artifact key is not content addressed.") + if (!Number.isSafeInteger(manifest.archive.size) || manifest.archive.size < 1 || manifest.archive.size > RUNTIME_ARCHIVE_MAX_BYTES) throw new Error("Runtime archive artifact size is outside the allowed budget.") + if (!manifest.source.url.startsWith("https://")) throw new Error("Runtime archive artifact source URL is invalid.") +} + +export async function readRuntimeArchiveArtifact(bucket: R2Bucket, manifest: RuntimeArchiveArtifactManifest): Promise { + validateRuntimeArchiveArtifactManifest(manifest) + const object = await bucket.get(manifest.key) + if (!object) throw new Error(`Runtime archive artifact is unavailable: ${manifest.key}`) + if (object.size !== manifest.archive.size) throw new Error("Runtime archive artifact size does not match its manifest.") + const bytes = new Uint8Array(await object.arrayBuffer()) + if (await sha256Hex(bytes) !== manifest.archive.sha256) throw new Error("Runtime archive artifact hash does not match its manifest.") + return bytes +} + +async function sha256Hex(bytes: Uint8Array): Promise { + const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes).buffer) + return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("") +} diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 8fd4d3fa..ea2ff020 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -14,6 +14,7 @@ import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./word import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" import { materializeWordPressRuntimeArtifact, type WordPressRuntimeArtifactManifest } from "./wordpress-runtime-artifact.js" import { validateWordPressStaticArtifactManifest, type WordPressStaticArtifactManifest } from "./wordpress-static-artifact.js" +import { readRuntimeArchiveArtifact, type RuntimeArchiveArtifactManifest } from "./runtime-archive-artifact.js" import type { MarkdownPointer } from "./state-coordinator.js" export { WordPressStateCoordinator } from "./state-coordinator.js" import markdownDatabaseIntegrationRuntime from "../assets/markdown-database-integration-runtime.zip" @@ -23,12 +24,12 @@ import markdownPrimaryBootstrapIndex from "../assets/markdown-primary-bootstrap- import wordpressInstallSeed from "../assets/wordpress-install-seed.sqlite" import wordpressRuntimeArtifactManifest from "../assets/wordpress-runtime-artifact.json" with { type: "json" } import wordpressStaticArtifactManifest from "../assets/wordpress-static-artifact.json" with { type: "json" } +import sqliteIntegrationArtifactManifest from "../assets/sqlite-database-integration-artifact.json" with { type: "json" } const PHP_VERSION = "8.5.8" const wordpressStaticArtifact = wordpressStaticArtifactManifest as WordPressStaticArtifactManifest validateWordPressStaticArtifactManifest(wordpressStaticArtifact) const wordpressStaticFiles = new Map(wordpressStaticArtifact.files.map((file) => [file.path, file])) -const SQLITE_INTEGRATION_ARCHIVE_URL = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip" const MARKDOWN_DATABASE_INTEGRATION_REVISION = "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c" const SITE_URL = "https://wp-codebox-runtime.invalid" const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite" @@ -684,13 +685,12 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise if (phase === "wordpress-archive" || phase === "sqlite-archive") { const archiveBytes = phase === "wordpress-archive" ? (await readWordPressRuntimeArtifact(bucket)).byteLength - : (await fetchArchive(SQLITE_INTEGRATION_ARCHIVE_URL, "sqlite-database-integration.zip")).size + : (await readSqliteIntegrationArtifact(bucket)).size return probeResponse(phase, { archiveBytes }) } if (phase === "archives") { - const wordpressBytes = await readWordPressRuntimeArtifact(bucket) - const sqliteZip = await fetchArchive(SQLITE_INTEGRATION_ARCHIVE_URL, "sqlite-database-integration.zip") + const [wordpressBytes, sqliteZip] = await Promise.all([readWordPressRuntimeArtifact(bucket), readSqliteIntegrationArtifact(bucket)]) return probeResponse(phase, { wordpressArchiveBytes: wordpressBytes.byteLength, sqliteArchiveBytes: sqliteZip.size }) } @@ -888,6 +888,8 @@ async function bootWordPressRuntime( shouldPatchCanonicalRuntimePoliciesAtInit = false, uploadFiles?: RuntimeFile[], ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { + if (includeSqlite && !runtimeBucket) throw new Error("SQLite integration artifact requires WORDPRESS_STATE_BUCKET.") + const sqliteIntegrationPluginZip = includeSqlite ? readSqliteIntegrationArtifact(runtimeBucket!) : undefined const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, constants: { @@ -934,7 +936,7 @@ async function bootWordPressRuntime( phpVersion: "8.5", siteUrl, wordPressZip: undefined, - sqliteIntegrationPluginZip: includeSqlite ? fetchArchive(SQLITE_INTEGRATION_ARCHIVE_URL, "sqlite-database-integration.zip") : undefined, + sqliteIntegrationPluginZip, wordpressInstallMode, }) const php = await requestHandler.getPrimaryPhp() @@ -1088,6 +1090,12 @@ async function readWordPressRuntimeArtifact(bucket: R2Bucket): Promise { + const manifest = sqliteIntegrationArtifactManifest as RuntimeArchiveArtifactManifest + const bytes = await readRuntimeArchiveArtifact(bucket, manifest) + return new File([Uint8Array.from(bytes).buffer], "sqlite-database-integration.zip", { type: "application/zip" }) +} + async function serveWordPressUpload(request: Request, bucket: R2Bucket, coordinator: DurableObjectStub): Promise { if (request.method !== "GET" && request.method !== "HEAD") return null const url = new URL(request.url) @@ -1220,12 +1228,6 @@ function createPhpRuntime() { ) } -async function fetchArchive(url: string, name: string): Promise { - const response = await fetch(url) - if (!response.ok) throw new Error(`Unable to fetch ${name}: ${response.status}.`) - return new File([await response.arrayBuffer()], name, { type: "application/zip" }) -} - function instantiatePrecompiledWasm(module: WebAssembly.Module) { return (imports: WebAssembly.Imports, receiveInstance: (instance: WebAssembly.Instance, wasmModule: WebAssembly.Module) => void) => receiveInstance(new WebAssembly.Instance(module, imports), module) } diff --git a/scripts/generate-cloudflare-wordpress-runtime-corpus.ts b/scripts/generate-cloudflare-wordpress-runtime-corpus.ts index 036e7d5f..bc158458 100644 --- a/scripts/generate-cloudflare-wordpress-runtime-corpus.ts +++ b/scripts/generate-cloudflare-wordpress-runtime-corpus.ts @@ -5,6 +5,7 @@ import { decodeZip, encodeZip } from "@php-wasm/stream-compression" import { isWordPressRuntimeFile, isWordPressStaticAsset } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" import { WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, wordpressRuntimeArtifactKey, type WordPressRuntimeArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" import { WORDPRESS_STATIC_ARTIFACT_SCHEMA, validateWordPressStaticArtifactManifest, wordpressStaticArtifactKey, type WordPressStaticArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-static-artifact.js" +import { RUNTIME_ARCHIVE_ARTIFACT_SCHEMA, runtimeArchiveArtifactKey, validateRuntimeArchiveArtifactManifest, type RuntimeArchiveArtifactManifest } from "../packages/runtime-cloudflare/src/runtime-archive-artifact.js" const sourceUrl = process.env.WORDPRESS_RUNTIME_ARCHIVE_URL ?? "https://downloads.wordpress.org/release/wordpress-7.0.2.zip" const sourceVersion = process.env.WORDPRESS_RUNTIME_VERSION ?? "7.0.2" @@ -12,6 +13,9 @@ const output = resolve(process.env.WORDPRESS_RUNTIME_ARTIFACT_OUTPUT ?? "artifac const manifestOutput = resolve("packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json") const staticOutput = resolve(process.env.WORDPRESS_STATIC_ARTIFACT_OUTPUT ?? "artifacts/cloudflare-wordpress-static-corpus.bin") const staticManifestOutput = resolve("packages/runtime-cloudflare/assets/wordpress-static-artifact.json") +const sqliteSourceUrl = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip" +const sqliteOutput = resolve("artifacts/cloudflare-sqlite-database-integration.zip") +const sqliteManifestOutput = resolve("packages/runtime-cloudflare/assets/sqlite-database-integration-artifact.json") const response = await fetch(sourceUrl) if (!response.ok || !response.body) throw new Error(`Unable to download WordPress archive: ${response.status}.`) const identity = response.headers.get("etag") ?? response.headers.get("last-modified") ?? undefined @@ -59,9 +63,26 @@ const staticManifest: WordPressStaticArtifactManifest = { validateWordPressStaticArtifactManifest(staticManifest) await writeFile(staticOutput, staticBlob) await writeFile(staticManifestOutput, `${JSON.stringify(staticManifest, null, 2)}\n`) + +const sqliteResponse = await fetch(sqliteSourceUrl) +if (!sqliteResponse.ok) throw new Error(`Unable to download SQLite integration archive: ${sqliteResponse.status}.`) +const sqliteArchive = new Uint8Array(await sqliteResponse.arrayBuffer()) +const sqliteSha256 = sha256Hex(sqliteArchive) +const sqliteIdentity = sqliteResponse.headers.get("etag") ?? sqliteResponse.headers.get("last-modified") ?? undefined +const sqliteManifest: RuntimeArchiveArtifactManifest = { + schema: RUNTIME_ARCHIVE_ARTIFACT_SCHEMA, + name: "sqlite-database-integration", + key: runtimeArchiveArtifactKey("sqlite-database-integration", sqliteSha256), + archive: { sha256: sqliteSha256, size: sqliteArchive.byteLength }, + source: { url: sqliteSourceUrl, version: "2.2.23", ...(sqliteIdentity ? { identity: sqliteIdentity } : {}) }, +} +validateRuntimeArchiveArtifactManifest(sqliteManifest) +await writeFile(sqliteOutput, sqliteArchive) +await writeFile(sqliteManifestOutput, `${JSON.stringify(sqliteManifest, null, 2)}\n`) console.log(JSON.stringify({ runtime: { key: manifest.key, bytes: archive.byteLength, files: manifest.files.length, sha256: archiveSha256 }, static: { key: staticManifest.key, bytes: staticBlob.byteLength, files: staticManifest.files.length, sha256: staticSha256 }, + sqlite: { key: sqliteManifest.key, bytes: sqliteArchive.byteLength, sha256: sqliteSha256 }, source: manifest.source, })) diff --git a/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs b/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs index 28c6690f..d0c7eb20 100644 --- a/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs +++ b/scripts/provision-cloudflare-wordpress-runtime-corpus.mjs @@ -17,10 +17,15 @@ const staticManifest = JSON.parse(await readFile("packages/runtime-cloudflare/as const staticBlob = await readFile("artifacts/cloudflare-wordpress-static-corpus.bin") const staticActual = createHash("sha256").update(staticBlob).digest("hex") if (staticActual !== staticManifest.blob.sha256 || staticBlob.byteLength !== staticManifest.blob.size || staticManifest.key !== `runtime/wordpress-static/${staticActual}.bin`) throw new Error("Local WordPress static artifact does not match its content-addressed manifest.") +const sqliteManifest = JSON.parse(await readFile("packages/runtime-cloudflare/assets/sqlite-database-integration-artifact.json", "utf8")) +const sqliteArchive = await readFile("artifacts/cloudflare-sqlite-database-integration.zip") +const sqliteActual = createHash("sha256").update(sqliteArchive).digest("hex") +if (sqliteActual !== sqliteManifest.archive.sha256 || sqliteArchive.byteLength !== sqliteManifest.archive.size || sqliteManifest.key !== `runtime/archives/sqlite-database-integration/${sqliteActual}.zip`) throw new Error("Local SQLite integration artifact does not match its content-addressed manifest.") for (const artifact of [ { key: manifest.key, size: manifest.archive.size, file: "artifacts/cloudflare-wordpress-runtime-corpus.zip" }, { key: staticManifest.key, size: staticManifest.blob.size, file: "artifacts/cloudflare-wordpress-static-corpus.bin" }, + { key: sqliteManifest.key, size: sqliteManifest.archive.size, file: "artifacts/cloudflare-sqlite-database-integration.zip" }, ]) { const command = ["exec", "--", "wrangler", "r2", "object", "put", `wp-codebox-runtime-chubes/${artifact.key}`, "--file", artifact.file, local ? "--local" : "--remote"] if (persistTo) command.push("--persist-to", persistTo) diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 740a5476..105d1d5f 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -15,6 +15,7 @@ import { MAX_UPLOAD_FILE_BYTES, R2_UPLOAD_OBJECT_PREFIX, validateUploadManifestF import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "../packages/runtime-cloudflare/src/wordpress-runtime-corpus.js" import { materializeWordPressRuntimeArtifact, WORDPRESS_RUNTIME_ARTIFACT_SCHEMA, wordpressRuntimeArtifactKey, type WordPressRuntimeArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-runtime-artifact.js" import { validateWordPressStaticArtifactManifest, WORDPRESS_STATIC_ARTIFACT_SCHEMA, wordpressStaticArtifactKey, type WordPressStaticArtifactManifest } from "../packages/runtime-cloudflare/src/wordpress-static-artifact.js" +import { readRuntimeArchiveArtifact, RUNTIME_ARCHIVE_ARTIFACT_SCHEMA, runtimeArchiveArtifactKey, type RuntimeArchiveArtifactManifest } from "../packages/runtime-cloudflare/src/runtime-archive-artifact.js" const execFileAsync = promisify(execFile) @@ -262,6 +263,8 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /CONCATENATE_SCRIPTS: false/) assert.match(worker, /SCRIPT_DEBUG: false/) assert.doesNotMatch(worker, /decodeRemoteZip|WORDPRESS_ARCHIVE_URL/) + assert.doesNotMatch(worker, /SQLITE_INTEGRATION_ARCHIVE_URL|fetchArchive\(/) + assert.match(worker, /readRuntimeArchiveArtifact\(bucket, manifest\)/) assert.match(worker, /bucket\.get\(wordpressStaticArtifact\.key, \{ range: \{ offset: file\.offset, length: file\.size \} \}\)/) assert.match(worker, /await sha256Hex\(bytes\) !== file\.sha256/) assert.match(worker, /request\.method === "HEAD" \? null : bytes/) @@ -382,11 +385,29 @@ test("WordPress static artifacts require complete, ordered, content-addressed ra assert.throws(() => validateWordPressStaticArtifactManifest({ ...manifest, blob: { ...manifest.blob, size: 4 } }), /do not cover/) }) +test("runtime archive dependencies are content-addressed and verified from R2", async () => { + const bytes = new TextEncoder().encode("immutable archive") + const sha256 = createHash("sha256").update(bytes).digest("hex") + const manifest: RuntimeArchiveArtifactManifest = { + schema: RUNTIME_ARCHIVE_ARTIFACT_SCHEMA, + name: "sqlite-database-integration", + key: runtimeArchiveArtifactKey("sqlite-database-integration", sha256), + archive: { sha256, size: bytes.byteLength }, + source: { url: "https://example.com/sqlite.zip", version: "1.0.0" }, + } + const bucket = { get: async () => ({ size: bytes.byteLength, arrayBuffer: async () => bytes.buffer }) } as never + assert.deepEqual(await readRuntimeArchiveArtifact(bucket, manifest), bytes) + await assert.rejects(readRuntimeArchiveArtifact({ get: async () => null } as never, manifest), /unavailable/) + await assert.rejects(readRuntimeArchiveArtifact({ get: async () => ({ size: bytes.byteLength, arrayBuffer: async () => new Uint8Array(bytes.byteLength).buffer }) } as never, manifest), /hash does not match/) + assert.throws(() => runtimeArchiveArtifactKey("../sqlite", sha256), /name is invalid/) +}) + test("WordPress runtime corpus generator keeps the ZIP outside the Worker bundle", async () => { const generator = await readFile(new URL("../scripts/generate-cloudflare-wordpress-runtime-corpus.ts", import.meta.url), "utf8") const artifact = await readFile(new URL("../packages/runtime-cloudflare/src/wordpress-runtime-artifact.ts", import.meta.url), "utf8") const manifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-runtime-artifact.json", import.meta.url), "utf8")) as WordPressRuntimeArtifactManifest const staticManifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/wordpress-static-artifact.json", import.meta.url), "utf8")) as WordPressStaticArtifactManifest + const sqliteManifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/sqlite-database-integration-artifact.json", import.meta.url), "utf8")) as RuntimeArchiveArtifactManifest assert.match(generator, /const response = await fetch\(sourceUrl\)/) assert.match(generator, /decodeZip\(response\.body\)/) assert.doesNotMatch(generator, /decodeRemoteZip/) @@ -394,12 +415,14 @@ test("WordPress runtime corpus generator keeps the ZIP outside the Worker bundle assert.match(generator, /lastModified: 0/) assert.match(generator, /artifacts\/cloudflare-wordpress-runtime-corpus\.zip/) assert.match(generator, /artifacts\/cloudflare-wordpress-static-corpus\.bin/) + assert.match(generator, /artifacts\/cloudflare-sqlite-database-integration\.zip/) assert.doesNotMatch(artifact, /decodeZip/) assert.match(artifact, /php\.writeFile\(WORDPRESS_RUNTIME_ARCHIVE_TEMP_PATH, archiveBytes\)/) assert.equal(manifest.key, wordpressRuntimeArtifactKey(manifest.archive.sha256)) assert.ok(manifest.files.length > 0) assert.doesNotThrow(() => validateWordPressStaticArtifactManifest(staticManifest)) assert.ok(staticManifest.files.length > 0) + assert.equal(sqliteManifest.key, runtimeArchiveArtifactKey(sqliteManifest.name, sqliteManifest.archive.sha256)) }) test("Cloudflare coordinator serializes leases, promotes with CAS, and recovers stale leases", async () => { From d11d4ed9ee90caa3d33302e36491c3812cb15dfe Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Tue, 21 Jul 2026 19:08:03 -0400 Subject: [PATCH 86/87] Exercise canonical Cloudflare boot probe --- packages/runtime-cloudflare/src/worker.ts | 27 ++++++++++++++++++++--- scripts/cloudflare-local-gate.mjs | 14 +++++++++++- tests/cloudflare-runtime.test.ts | 2 ++ 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index ea2ff020..73f915f1 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -812,9 +812,30 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (phase === "wordpress-files" || phase === "sqlite" || phase === "full" || phase === "streamed-sqlite" || phase === "streamed-wordpress") { + if (phase === "full") { const runtime = await bootWordPressRuntime( - phase === "full" || phase === "streamed-wordpress" ? "install-from-existing-files" : "do-not-attempt-installing", + "do-not-attempt-installing", + true, + true, + undefined, + await packagedCanonicalMarkdownSeed(), + new Uint8Array(markdownPrimaryBootstrapIndex), + SITE_URL, + {}, + bucket, + true, + ) + try { + const phpVersion = (await runtime.php.run({ code: " const phpVersion = (await runtime.php.run({ code: " Date: Tue, 21 Jul 2026 19:42:04 -0400 Subject: [PATCH 87/87] Persist mutable WordPress content in R2 --- packages/runtime-cloudflare/README.md | 4 +- packages/runtime-cloudflare/src/worker.ts | 225 ++++++++++++++++-- .../src/wp-content-persistence.ts | 53 +++++ scripts/cloudflare-local-gate.mjs | 88 ++++++- tests/cloudflare-runtime.test.ts | 33 ++- 5 files changed, 375 insertions(+), 28 deletions(-) create mode 100644 packages/runtime-cloudflare/src/wp-content-persistence.ts diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 3f4eda36..741c7c0e 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -12,6 +12,8 @@ Canonical browser, health, and mutation boots require the separately managed `WO After each mutating HTTP request, the entry runtime invokes MDI's explicit request-boundary flush, collects canonical files, stores immutable content-addressed R2 objects and a revision manifest, then commits the new pointer through the held lease. GET, HEAD, and asset requests release without promotion. Failed requests abort their leases; stale leases recover by token/version/expiry checks. The entry isolate can cache one runtime only for the exact acquired pointer revision and exits it after promotion or when another isolate advances the pointer. It does not persist SQLite. Existing manifests are reused when canonical file hashes have not changed. +The same revision transaction persists bounded user-managed files under `wp-content/plugins`, `themes`, `languages`, and `mu-plugins`. Runtime-owned MDI, SQLite integration, and Codebox adapter files remain reconstructable artifacts and are excluded. Unchanged bundled-theme files are omitted by release hash, while modified files become canonical overrides. Public canonical plugin and theme assets serve directly from revision-addressed R2 cache entries before the immutable release corpus. Nonce-protected `wp-admin` GET actions such as plugin activation are classified as mutations so their filesystem and MDI option changes commit atomically. Existing revisions without `wpContent` remain valid. + The Worker forwards browser cookies directly to Playground and disables Playground's internal cookie store, preventing an empty per-isolate store from replacing a valid browser session after cold restart. The bundled MDI source is pinned to immutable commit `2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c` from [MDI PR #139](https://github.com/Automattic/markdown-database-integration/pull/139). The bundle generator, worker provenance, and source-contract test use the same revision. @@ -25,6 +27,6 @@ Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpres 2. Run `npm run provision:cloudflare-wordpress-runtime-corpus -- --local --persist-to ` to verify and upload all exact content-addressed artifacts into isolated local R2 storage. For an authorized deployment, run the provisioner with `--remote` and require every upload to succeed before deploying the Worker that imports their manifests. 3. Run `npm run test:cloudflare-runtime` for routing, canonical-state, artifact validation, source contract, and TypeScript coverage. 4. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. -5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions all artifacts before workerd starts, then injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. +5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions all artifacts before workerd starts, then injects stable test-only admin-password and auth-secret values, uploads and activates a real plugin ZIP, verifies that plugin's REST route before and after Worker restart, and covers login, authenticated REST publication, media, public rendering, representative frontend/admin/editor assets, PHP diagnostics, session recovery, and a fresh login after restart. This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 73f915f1..c48f9902 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -15,6 +15,7 @@ import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticCont import { materializeWordPressRuntimeArtifact, type WordPressRuntimeArtifactManifest } from "./wordpress-runtime-artifact.js" import { validateWordPressStaticArtifactManifest, type WordPressStaticArtifactManifest } from "./wordpress-static-artifact.js" import { readRuntimeArchiveArtifact, type RuntimeArchiveArtifactManifest } from "./runtime-archive-artifact.js" +import { isCanonicalWpContentPath, MAX_WP_CONTENT_FILES, MAX_WP_CONTENT_FILE_BYTES, MAX_WP_CONTENT_TOTAL_BYTES, R2_WP_CONTENT_OBJECT_PREFIX, validateWpContentDeletedPaths, validateWpContentManifestFiles, validateWpContentMetadata } from "./wp-content-persistence.js" import type { MarkdownPointer } from "./state-coordinator.js" export { WordPressStateCoordinator } from "./state-coordinator.js" import markdownDatabaseIntegrationRuntime from "../assets/markdown-database-integration-runtime.zip" @@ -30,6 +31,16 @@ const PHP_VERSION = "8.5.8" const wordpressStaticArtifact = wordpressStaticArtifactManifest as WordPressStaticArtifactManifest validateWordPressStaticArtifactManifest(wordpressStaticArtifact) const wordpressStaticFiles = new Map(wordpressStaticArtifact.files.map((file) => [file.path, file])) +const wordpressWpContentBaselineHashes = new Map([ + ...(wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest).files, + ...wordpressStaticArtifact.files, +].filter((file) => file.path.startsWith("wordpress/wp-content/")) + .map((file) => [file.path.slice("wordpress/wp-content/".length), file.sha256] as const) + .filter(([path]) => isCanonicalWpContentPath(path))) +const wordpressWpContentRuntimeBaselinePaths = new Set((wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest).files + .filter((file) => file.path.startsWith("wordpress/wp-content/")) + .map((file) => file.path.slice("wordpress/wp-content/".length)) + .filter(isCanonicalWpContentPath)) const MARKDOWN_DATABASE_INTEGRATION_REVISION = "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c" const SITE_URL = "https://wp-codebox-runtime.invalid" const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite" @@ -42,6 +53,7 @@ const R2_MARKDOWN_REVISION_PREFIX = "sites/default/markdown/revisions" const R2_MARKDOWN_OBJECT_PREFIX = "sites/default/markdown/objects" const R2_WORDPRESS_PAGE_PREFIX = "sites/default/pages" const WORDPRESS_PAGE_CACHE_SCHEMA = "v2" +const PUBLIC_WP_CONTENT_EXTENSION = /\.(?:css|js|mjs|json|txt|xml|woff2?|ttf|otf|eot|svg|png|jpe?g|gif|webp|avif|ico)$/i const SERIALIZED_MARKDOWN_MUTATION_CODE = ` { + const coordinator = env.WORDPRESS_STATE.getByName("default") + const wpContentResponse = await serveWordPressWpContent(request, env.WORDPRESS_STATE_BUCKET, coordinator) + if (wpContentResponse) return wpContentResponse const staticResponse = await serveWordPressStaticAsset(request, env.WORDPRESS_STATE_BUCKET) if (staticResponse) return staticResponse const route = routeWorkerRequest(request) - const coordinator = env.WORDPRESS_STATE.getByName("default") const uploadResponse = await serveWordPressUpload(request, env.WORDPRESS_STATE_BUCKET, coordinator) if (uploadResponse) return uploadResponse if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) @@ -118,6 +132,8 @@ interface MarkdownManifestFile { interface MarkdownManifest extends MarkdownPointer { files: MarkdownManifestFile[] uploads?: MarkdownManifestFile[] + wpContent?: MarkdownManifestFile[] + wpContentDeleted?: string[] } interface RuntimeFile { @@ -352,7 +368,10 @@ function pageCacheResponse(response: Response, head: boolean, status: "hit" | "m } function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate"): boolean { - return route === "r2-mutate" || !["GET", "HEAD", "OPTIONS"].includes(request.method) + if (route === "r2-mutate" || !["GET", "HEAD", "OPTIONS"].includes(request.method)) return true + if (route !== "wordpress" || request.method !== "GET") return false + const url = new URL(request.url) + return url.pathname.startsWith("/wp-admin/") && !!url.searchParams.get("action") && url.searchParams.get("action") !== "-1" } function coordinatorUrl(requestUrl: string, action: string): string { @@ -444,7 +463,7 @@ async function discardRuntime(runtime: Runtime): Promise { async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: string, authConstants: Record): Promise { const revision = await readCanonicalRevision(bucket, pointer) - return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, revision.markdown, new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants, bucket, true, revision.uploads), pointer } + return { ...await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, revision.markdown, new Uint8Array(markdownPrimaryBootstrapIndex), origin, authConstants, bucket, true, revision.uploads, revision.wpContent, revision.wpContentDeleted), pointer } } async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { @@ -502,7 +521,8 @@ async function canonicalWordPressAuthConstants(env: Env): Promise { validateMarkdownChanges(changes) const changedPaths = [...changes.created, ...changes.changed].sort((left, right) => left.localeCompare(right)) - return persistMarkdownRevision(bucket, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT, changedPaths), runtime.pointer, changes, await collectUploadFiles(runtime.php)) + const [uploads, wpContent] = await Promise.all([collectUploadFiles(runtime.php), collectWpContentFiles(runtime.php)]) + return persistMarkdownRevision(bucket, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT, changedPaths), runtime.pointer, changes, uploads, wpContent.files, wpContent.deleted) } function readCanonicalChanges(php: PHP): MarkdownChanges { @@ -551,15 +571,19 @@ function isCanonicalRelativePath(path: string): boolean { return path.length > 0 && !path.startsWith("/") && !path.includes("\\") && !path.split("/").includes("..") } -async function readCanonicalRevision(bucket: R2Bucket, pointer: MarkdownPointer): Promise<{ markdown: RuntimeFile[]; uploads: RuntimeFile[] }> { +async function readCanonicalRevision(bucket: R2Bucket, pointer: MarkdownPointer): Promise<{ markdown: RuntimeFile[]; uploads: RuntimeFile[]; wpContent: RuntimeFile[]; wpContentDeleted: string[] }> { const manifestObject = await bucket.get(pointer.manifestKey) if (!manifestObject) throw new Error(`R2 Markdown manifest is missing: ${pointer.manifestKey}`) const manifest = await manifestObject.json() validateUploadManifestFiles(manifest.uploads ?? []) - return { - markdown: await readManifestFiles(bucket, manifest.files, "Markdown"), - uploads: await readManifestFiles(bucket, manifest.uploads ?? [], "upload"), - } + validateWpContentManifestFiles(manifest.wpContent ?? []) + validateWpContentDeletedPaths(manifest.wpContentDeleted ?? []) + const [markdown, uploads, wpContent] = await Promise.all([ + readManifestFiles(bucket, manifest.files, "Markdown"), + readManifestFiles(bucket, manifest.uploads ?? [], "upload"), + readManifestFiles(bucket, manifest.wpContent ?? [], "wp-content"), + ]) + return { markdown, uploads, wpContent, wpContentDeleted: manifest.wpContentDeleted ?? [] } } async function readManifestFiles(bucket: R2Bucket, files: MarkdownManifestFile[], label: string): Promise { @@ -572,14 +596,21 @@ async function readManifestFiles(bucket: R2Bucket, files: MarkdownManifestFile[] })) } -async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], current?: MarkdownPointer, changes?: MarkdownChanges, uploads: RuntimeFile[] = []): Promise { +async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], current?: MarkdownPointer, changes?: MarkdownChanges, uploads: RuntimeFile[] = [], wpContent: RuntimeFile[] = [], wpContentDeleted: string[] = []): Promise { const currentManifest = current ? await readMarkdownManifest(bucket, current) : null if (current && !currentManifest) throw new Error(`R2 Markdown manifest is missing: ${current.manifestKey}`) + validateUploadManifestFiles(currentManifest?.uploads ?? []) + validateWpContentManifestFiles(currentManifest?.wpContent ?? []) + validateWpContentDeletedPaths(currentManifest?.wpContentDeleted ?? []) + validateWpContentDeletedPaths(wpContentDeleted) const uploadManifestFiles = await persistUploadObjects(bucket, uploads, currentManifest?.uploads ?? []) + const wpContentManifestFiles = await persistWpContentObjects(bucket, wpContent, currentManifest?.wpContent ?? []) const uploadsUnchanged = JSON.stringify(currentManifest?.uploads ?? []) === JSON.stringify(uploadManifestFiles) + const wpContentUnchanged = JSON.stringify(currentManifest?.wpContent ?? []) === JSON.stringify(wpContentManifestFiles) + const wpContentDeletedUnchanged = JSON.stringify(currentManifest?.wpContentDeleted ?? []) === JSON.stringify(wpContentDeleted) if (current && currentManifest && changes) { validateMarkdownChanges(changes) - if (!changes.created.length && !changes.changed.length && !changes.deleted.length && uploadsUnchanged) return current + if (!changes.created.length && !changes.changed.length && !changes.deleted.length && uploadsUnchanged && wpContentUnchanged && wpContentDeletedUnchanged) return current const manifestFiles = new Map(currentManifest.files.map((file) => [file.path, file])) for (const path of changes.deleted) manifestFiles.delete(path) const filesByPath = new Map(files.map((file) => [file.path, file])) @@ -591,7 +622,7 @@ async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], c await bucket.put(objectKey, file.bytes) manifestFiles.set(path, { path, objectKey, sha256, size: file.bytes.byteLength }) } - return persistMarkdownManifest(bucket, [...manifestFiles.values()].sort((left, right) => left.path.localeCompare(right.path)), uploadManifestFiles) + return persistMarkdownManifest(bucket, [...manifestFiles.values()].sort((left, right) => left.path.localeCompare(right.path)), uploadManifestFiles, wpContentManifestFiles, wpContentDeleted) } const currentFiles = new Map(currentManifest?.files.map((file) => [file.path, file]) ?? []) const manifestFiles = await Promise.all(files.map(async (file): Promise => { @@ -603,10 +634,10 @@ async function persistMarkdownRevision(bucket: R2Bucket, files: RuntimeFile[], c return { path: file.path, objectKey, sha256, size: file.bytes.byteLength } })) if (current && currentManifest) { - if (JSON.stringify(currentManifest.files) === JSON.stringify(manifestFiles) && uploadsUnchanged) return current + if (JSON.stringify(currentManifest.files) === JSON.stringify(manifestFiles) && uploadsUnchanged && wpContentUnchanged && wpContentDeletedUnchanged) return current } - return persistMarkdownManifest(bucket, manifestFiles, uploadManifestFiles) + return persistMarkdownManifest(bucket, manifestFiles, uploadManifestFiles, wpContentManifestFiles, wpContentDeleted) } async function persistUploadObjects(bucket: R2Bucket, files: RuntimeFile[], current: MarkdownManifestFile[]): Promise { @@ -627,16 +658,34 @@ async function persistUploadObjects(bucket: R2Bucket, files: RuntimeFile[], curr return persisted } +async function persistWpContentObjects(bucket: R2Bucket, files: RuntimeFile[], current: MarkdownManifestFile[]): Promise { + validateWpContentMetadata(files.map((file) => ({ path: file.path, size: file.bytes.byteLength }))) + const currentFiles = new Map(current.map((file) => [file.path, file])) + const persisted: MarkdownManifestFile[] = [] + for (const file of files) { + const sha256 = await sha256Hex(file.bytes) + const existing = currentFiles.get(file.path) + if (existing?.sha256 === sha256 && existing.size === file.bytes.byteLength) { + persisted.push(existing) + continue + } + const objectKey = `${R2_WP_CONTENT_OBJECT_PREFIX}/${sha256}` + if (!await bucket.head(objectKey)) await bucket.put(objectKey, file.bytes) + persisted.push({ path: file.path, objectKey, sha256, size: file.bytes.byteLength }) + } + return persisted +} + function validateUploadFiles(files: RuntimeFile[]): void { validateUploadMetadata(files.map((file) => ({ path: file.path, size: file.bytes.byteLength }))) } -async function persistMarkdownManifest(bucket: R2Bucket, files: MarkdownManifestFile[], uploads: MarkdownManifestFile[] = []): Promise { +async function persistMarkdownManifest(bucket: R2Bucket, files: MarkdownManifestFile[], uploads: MarkdownManifestFile[] = [], wpContent: MarkdownManifestFile[] = [], wpContentDeleted: string[] = []): Promise { const revision = crypto.randomUUID() const manifestKey = `${R2_MARKDOWN_REVISION_PREFIX}/${revision}.json` const persistedAt = new Date().toISOString() const pointer: MarkdownPointer = { revision, manifestKey, persistedAt } - const manifest: MarkdownManifest = { ...pointer, files, uploads } + const manifest: MarkdownManifest = { ...pointer, files, uploads, wpContent, wpContentDeleted } await bucket.put(manifestKey, JSON.stringify(manifest), { httpMetadata: { contentType: "application/json" }, }) @@ -908,8 +957,11 @@ async function bootWordPressRuntime( runtimeBucket?: R2Bucket, shouldPatchCanonicalRuntimePoliciesAtInit = false, uploadFiles?: RuntimeFile[], + wpContentFiles?: RuntimeFile[], + wpContentDeleted: string[] = [], ): Promise<{ php: PHP; requestHandler: PHPRequestHandler; wordpressVersion: string }> { if (includeSqlite && !runtimeBucket) throw new Error("SQLite integration artifact requires WORDPRESS_STATE_BUCKET.") + validateWpContentDeletedPaths(wpContentDeleted) const sqliteIntegrationPluginZip = includeSqlite ? readSqliteIntegrationArtifact(runtimeBucket!) : undefined const requestHandler = await bootWordPressAndRequestHandler({ createPhpRuntime, @@ -933,8 +985,8 @@ async function bootWordPressRuntime( // Browser cookies are carried by the Worker Fetch request; Playground's // internal store would overwrite that header after an isolate restart. cookieStore: false, - hooks: streamWordPressFiles || databaseSeed || markdownFiles || uploadFiles?.length ? { - beforeWordPressFiles: streamWordPressFiles || markdownFiles || uploadFiles?.length ? async (php: PHP) => { + hooks: streamWordPressFiles || databaseSeed || markdownFiles || uploadFiles?.length || wpContentFiles?.length || wpContentDeleted.length ? { + beforeWordPressFiles: streamWordPressFiles || markdownFiles || uploadFiles?.length || wpContentFiles?.length || wpContentDeleted.length ? async (php: PHP) => { if (streamWordPressFiles) await materializeWordPressServerFiles(php, runtimeBucket) if (markdownFiles) { await materializeMarkdownDatabaseIntegration(php) @@ -942,6 +994,8 @@ async function bootWordPressRuntime( materializeRuntimeFiles(php, MARKDOWN_ROOT, markdownFiles) if (markdownIndexSeed) php.writeFile(MARKDOWN_RESOLVED_INDEX_PATH, markdownIndexSeed) } + if (wpContentFiles?.length) materializeRuntimeFiles(php, "/wordpress/wp-content", wpContentFiles) + if (wpContentDeleted.length) await materializeWpContentTombstones(php, wpContentDeleted) if (uploadFiles?.length) materializeRuntimeFiles(php, UPLOADS_ROOT, uploadFiles) if (shouldPatchCanonicalRuntimePoliciesAtInit) { patchCanonicalRuntimePoliciesAtInit(php) @@ -986,6 +1040,18 @@ function materializeRuntimeFiles(php: PHP, root: string, files: RuntimeFile[]): } } +async function materializeWpContentTombstones(php: PHP, paths: string[]): Promise { + validateWpContentDeletedPaths(paths) + const encodedPaths = JSON.stringify(paths).replace(/ ({ path: file.path, bytes: php.readFileAsBuffer(`${UPLOADS_ROOT}/${file.path}`) })) } +async function collectWpContentFiles(php: PHP): Promise<{ files: RuntimeFile[]; deleted: string[] }> { + const output = (await php.run({ code: `isFile()) continue; + $absolute = str_replace('\\\\', '/', $file->getPathname()); + $path = $root . '/' . substr($absolute, strlen($directory) + 1); + if (str_starts_with($path, 'plugins/markdown-database-integration/') || str_starts_with($path, 'plugins/sqlite-database-integration/') || str_starts_with($path, 'mu-plugins/wp-codebox-cloudflare-canonical-changes.php')) continue; + $size = $file->getSize(); + $total += $size; + if ($size > ${MAX_WP_CONTENT_FILE_BYTES} || $total > ${MAX_WP_CONTENT_TOTAL_BYTES} || count($files) >= ${MAX_WP_CONTENT_FILES}) { + throw new RuntimeException('Canonical wp-content files exceed their budget.'); + } + $files[] = array('path' => $path, 'size' => $size); + } +} +usort($files, static fn($left, $right) => strcmp($left['path'], $right['path'])); +echo json_encode($files, JSON_UNESCAPED_SLASHES | JSON_THROW_ON_ERROR);` })).text.trim() + const metadata: unknown = JSON.parse(output) + validateWpContentMetadata(metadata) + const files = metadata.map((file) => ({ path: file.path, bytes: php.readFileAsBuffer(`/wordpress/wp-content/${file.path}`) })) + const persistent: RuntimeFile[] = [] + for (const file of files) { + const baselineSha256 = wordpressWpContentBaselineHashes.get(file.path) + if (!baselineSha256 || await sha256Hex(file.bytes) !== baselineSha256) persistent.push(file) + } + const currentPaths = new Set(metadata.map((file) => file.path)) + const deleted = new Set([...wordpressWpContentRuntimeBaselinePaths].filter((path) => { + const absolute = `/wordpress/wp-content/${path}` + return !php.isDir(absolute) && !currentPaths.has(path) + })) + const baselineThemes = new Set([...wordpressWpContentBaselineHashes.keys()].filter((path) => path.startsWith("themes/")).map((path) => path.split("/")[1])) + for (const theme of baselineThemes) { + if (!php.isDir(`/wordpress/wp-content/themes/${theme}`)) { + for (const path of wordpressWpContentBaselineHashes.keys()) if (path.startsWith(`themes/${theme}/`)) deleted.add(path) + } + } + const sortedDeleted = [...deleted].sort((left, right) => left.localeCompare(right)) + validateWpContentDeletedPaths(sortedDeleted) + return { files: persistent, deleted: sortedDeleted } +} + async function sha256Hex(bytes: Uint8Array): Promise { const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes).buffer) return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("") @@ -1117,6 +1231,61 @@ async function readSqliteIntegrationArtifact(bucket: R2Bucket): Promise { return new File([Uint8Array.from(bytes).buffer], "sqlite-database-integration.zip", { type: "application/zip" }) } +async function serveWordPressWpContent(request: Request, bucket: R2Bucket, coordinator: DurableObjectStub): Promise { + if (request.method !== "GET" && request.method !== "HEAD") return null + const url = new URL(request.url) + if (!url.pathname.startsWith("/wp-content/") || !PUBLIC_WP_CONTENT_EXTENSION.test(url.pathname)) return null + let path: string + try { + path = decodeURIComponent(url.pathname.slice("/wp-content/".length)) + } catch { + return new Response("Invalid WordPress content path.", { status: 400 }) + } + if (!isCanonicalWpContentPath(path)) return null + const state = await coordinatorCall(coordinator, request.url, "state") + if (!state.pointer) return null + const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default + const cacheKey = wordPressRevisionCacheKey(request, state.pointer) + if (request.method === "GET" && cache) { + try { + const cached = await cache.match(cacheKey) + if (cached) return cached + } catch { + // R2 remains authoritative when the edge cache is unavailable. + } + } + const manifest = await readMarkdownManifest(bucket, state.pointer) + validateWpContentManifestFiles(manifest?.wpContent ?? []) + validateWpContentDeletedPaths(manifest?.wpContentDeleted ?? []) + if (manifest?.wpContentDeleted?.includes(path)) return new Response("WordPress content not found.", { status: 404 }) + const file = manifest?.wpContent?.find((candidate) => candidate.path === path) + if (!file) return null + const object = await bucket.get(file.objectKey) + if (!object || object.size !== file.size) throw new Error(`R2 wp-content object is missing or inconsistent: ${file.objectKey}`) + let body: Uint8Array | null = null + if (request.method === "GET") { + body = new Uint8Array(await object.arrayBuffer()) + if (await sha256Hex(body) !== file.sha256) throw new Error(`R2 wp-content object failed integrity validation: ${file.objectKey}`) + } + const response = new Response(body ? Uint8Array.from(body).buffer : null, { + headers: { + "cache-control": "public, max-age=60", + "content-length": String(file.size), + "content-type": wordPressContentType(path), + etag: `"${file.sha256}"`, + "x-wp-codebox-static": "r2-wp-content", + }, + }) + if (request.method === "GET" && cache) { + try { + await cache.put(cacheKey, response.clone()) + } catch { + // R2 remains authoritative when the edge cache is unavailable. + } + } + return response +} + async function serveWordPressUpload(request: Request, bucket: R2Bucket, coordinator: DurableObjectStub): Promise { if (request.method !== "GET" && request.method !== "HEAD") return null const url = new URL(request.url) @@ -1131,7 +1300,7 @@ async function serveWordPressUpload(request: Request, bucket: R2Bucket, coordina const state = await coordinatorCall(coordinator, request.url, "state") if (!state.pointer) return null const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default - const cacheKey = wordPressUploadCacheKey(request, state.pointer) + const cacheKey = wordPressRevisionCacheKey(request, state.pointer) if (request.method === "GET" && cache) { try { const cached = await cache.match(cacheKey) @@ -1155,7 +1324,7 @@ async function serveWordPressUpload(request: Request, bucket: R2Bucket, coordina const headers = new Headers({ "cache-control": "public, max-age=60", "content-length": String(file.size), - "content-type": wordPressUploadContentType(path), + "content-type": wordPressContentType(path), etag: `"${file.sha256}"`, "x-wp-codebox-static": "r2-upload", }) @@ -1170,30 +1339,39 @@ async function serveWordPressUpload(request: Request, bucket: R2Bucket, coordina return response } -function wordPressUploadCacheKey(request: Request, pointer: MarkdownPointer): Request { +function wordPressRevisionCacheKey(request: Request, pointer: MarkdownPointer): Request { const url = new URL(request.url) url.searchParams.set("__wp_codebox_revision", pointer.revision) return new Request(url, { method: "GET" }) } -function wordPressUploadContentType(path: string): string { +function wordPressContentType(path: string): string { const extension = path.slice(path.lastIndexOf(".") + 1).toLowerCase() return ({ avif: "image/avif", + css: "text/css; charset=utf-8", + eot: "application/vnd.ms-fontobject", gif: "image/gif", + ico: "image/x-icon", jpeg: "image/jpeg", jpg: "image/jpeg", json: "application/json", + js: "text/javascript; charset=utf-8", + mjs: "text/javascript; charset=utf-8", mp3: "audio/mpeg", mp4: "video/mp4", ogg: "audio/ogg", + otf: "font/otf", pdf: "application/pdf", png: "image/png", svg: "image/svg+xml", txt: "text/plain; charset=utf-8", + ttf: "font/ttf", wav: "audio/wav", webm: "video/webm", webp: "image/webp", + woff: "font/woff", + woff2: "font/woff2", xml: "application/xml", } as Record)[extension] ?? "application/octet-stream" } @@ -1203,7 +1381,8 @@ async function serveWordPressStaticAsset(request: Request, bucket: R2Bucket): Pr const archivePath = wordpressStaticArchivePath(new URL(request.url).pathname) if (!archivePath) return null const file = wordpressStaticFiles.get(archivePath) - if (!file) return new Response("Not found", { status: 404 }) + // Mutable custom-theme assets are served by Playground from canonical wp-content. + if (!file) return null const cache = typeof caches === "undefined" ? undefined : (caches as CacheStorage & { default?: Cache }).default const cacheUrl = new URL(request.url) diff --git a/packages/runtime-cloudflare/src/wp-content-persistence.ts b/packages/runtime-cloudflare/src/wp-content-persistence.ts new file mode 100644 index 00000000..be15eb6a --- /dev/null +++ b/packages/runtime-cloudflare/src/wp-content-persistence.ts @@ -0,0 +1,53 @@ +export const R2_WP_CONTENT_OBJECT_PREFIX = "sites/default/wp-content/objects" +export const MAX_WP_CONTENT_FILES = 5000 +export const MAX_WP_CONTENT_FILE_BYTES = 8 * 1024 * 1024 +export const MAX_WP_CONTENT_TOTAL_BYTES = 64 * 1024 * 1024 + +export interface WpContentFileMetadata { + path: string + size: number +} + +export interface WpContentManifestFile extends WpContentFileMetadata { + objectKey: string + sha256: string +} + +export function validateWpContentMetadata(value: unknown): asserts value is WpContentFileMetadata[] { + if (!Array.isArray(value) || value.length > MAX_WP_CONTENT_FILES) throw new Error("Canonical wp-content file count is invalid.") + let total = 0 + const paths = new Set() + for (const file of value) { + if (!file || typeof file !== "object" || typeof file.path !== "string" || !isCanonicalWpContentPath(file.path) || paths.has(file.path) + || !Number.isSafeInteger(file.size) || file.size < 0 || file.size > MAX_WP_CONTENT_FILE_BYTES) { + throw new Error("Canonical wp-content metadata contains an invalid file.") + } + paths.add(file.path) + total += file.size + } + if (total > MAX_WP_CONTENT_TOTAL_BYTES) throw new Error("Canonical wp-content files exceed their byte budget.") +} + +export function validateWpContentManifestFiles(value: unknown): asserts value is WpContentManifestFile[] { + validateWpContentMetadata(value) + for (const file of value as WpContentManifestFile[]) { + if (!/^[a-f0-9]{64}$/.test(file.sha256) || file.objectKey !== `${R2_WP_CONTENT_OBJECT_PREFIX}/${file.sha256}`) { + throw new Error("Canonical wp-content manifest contains an invalid file.") + } + } +} + +export function validateWpContentDeletedPaths(value: unknown): asserts value is string[] { + if (!Array.isArray(value) || value.length > MAX_WP_CONTENT_FILES || value.some((path) => typeof path !== "string" || !isCanonicalWpContentPath(path))) { + throw new Error("Canonical wp-content tombstones are invalid.") + } + if (value.some((path, index) => index > 0 && value[index - 1] >= path)) throw new Error("Canonical wp-content tombstones are non-deterministic.") +} + +export function isCanonicalWpContentPath(path: string): boolean { + if (!/^(?:plugins|themes|languages|mu-plugins)\//.test(path) || path.includes("\\") || path.split("/").some((segment) => !segment || segment === "." || segment === "..")) return false + return !path.startsWith("plugins/markdown-database-integration/") + && !path.startsWith("plugins/sqlite-database-integration/") + && path !== "mu-plugins/wp-codebox-cloudflare-canonical-changes.php" + && !path.startsWith("mu-plugins/wp-codebox-cloudflare-canonical-changes.php/") +} diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 0cc5027f..142a674d 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -4,6 +4,7 @@ import { mkdtemp, rm } from "node:fs/promises" import { tmpdir } from "node:os" import { join } from "node:path" import { stripVTControlCharacters } from "node:util" +import { encodeZip } from "@php-wasm/stream-compression" const port = 8792 const origin = `http://127.0.0.1:${port}` @@ -28,6 +29,7 @@ try { } console.log(`Explanatory homepage timing: cold=${coldHome.elapsedMs}ms warm=${warmHome.elapsedMs}ms.`) const adminHtml = await login() + const deletedThemePath = await installDurablePlugin(adminHtml) const editorHtml = await assertPostNewEditor() const media = await createMedia(adminHtml) await assertMediaFile(media, "uploaded media") @@ -47,10 +49,13 @@ try { assertIncludes(restartedPost, post.title, "post after cold restart") await assertMediaFile(media, "media after cold restart") await assertMediaMetadata(media, "media metadata after cold restart") + await assertDurablePlugin("plugin after cold restart") + await assertDurablePluginAsset("plugin asset after cold restart") + await assertDeletedThemeFile(deletedThemePath, "bundled theme tombstone after cold restart") await assertLinkedAssets(restartedAdmin, "admin after cold restart") cookies.length = 0 await login() - console.log("Cloudflare local runtime gate passed: canonical full-boot probe, explanatory homepage, complete block styles, revision page cache, login, dashboard, post editor, concurrent canonical mutations, authenticated REST post and media creation, direct R2 upload serving, frontend/admin/editor assets, and cold-restart content, media, and session persistence.") + console.log("Cloudflare local runtime gate passed: canonical full-boot probe, explanatory homepage, complete block styles, revision page cache, login, dashboard, post editor, concurrent canonical mutations, authenticated REST post and media creation, plugin ZIP installation and activation, direct R2 upload serving, frontend/admin/editor assets, and cold-restart content, media, plugin, and session persistence.") } finally { await stopWorker() await rm(stateDirectory, { recursive: true, force: true }) @@ -185,6 +190,87 @@ async function assertPostNewEditor() { return body } +async function installDurablePlugin(adminHtml) { + const uploadPage = await request(`${origin}/wp-admin/plugin-install.php?tab=upload`) + const uploadHtml = await uploadPage.text() + const nonce = uploadHtml.match(/name=["']_wpnonce["'][^>]*value=["']([^"']+)["']/i)?.[1] + if (!uploadPage.ok || !nonce) throw new Error("Plugin upload page did not expose its installation nonce.") + const source = ` 'GET', + 'callback' => static fn() => rest_ensure_response( array( 'durable' => true, 'source' => 'canonical-wp-content' ) ), + 'permission_callback' => '__return_true', + ) ); + register_rest_route( 'wp-codebox/v1', '/delete-bundled-theme-file', array( + 'methods' => 'POST', + 'callback' => static function () { + foreach ( wp_get_themes() as $stylesheet => $theme ) { + if ( $stylesheet === get_stylesheet() ) continue; + $relative = 'themes/' . $stylesheet . '/style.css'; + $absolute = WP_CONTENT_DIR . '/' . $relative; + if ( is_file( $absolute ) && unlink( $absolute ) ) return rest_ensure_response( array( 'deleted' => $relative ) ); + } + return new WP_Error( 'no_inactive_theme_file', 'No inactive bundled theme file was available.', array( 'status' => 409 ) ); + }, + 'permission_callback' => static fn() => current_user_can( 'delete_themes' ), + ) ); +} );` + const archive = new Uint8Array(await new Response(encodeZip([ + new File([source], "cloudflare-durable-proof/cloudflare-durable-proof.php", { lastModified: 0, type: "application/x-httpd-php" }), + new File([".cloudflare-durable-proof{display:block}"], "cloudflare-durable-proof/proof.css", { lastModified: 0, type: "text/css" }), + ])).arrayBuffer()) + const form = new FormData() + form.set("_wpnonce", nonce) + form.set("_wp_http_referer", "/wp-admin/plugin-install.php?tab=upload") + form.set("pluginzip", new File([archive], "cloudflare-durable-proof.zip", { type: "application/zip" })) + form.set("install-plugin-submit", "Install Now") + const installed = await request(`${origin}/wp-admin/update.php?action=upload-plugin`, { method: "POST", body: form }) + const installedHtml = await installed.text() + assertNoPhpDiagnostics(installedHtml, "plugin installation") + const activationHref = installedHtml.match(/href=["']([^"']*plugins\.php\?action=activate[^"']+)["'][^>]*>Activate Plugin validateUploadManifestFiles([{ ...valid[0], sha256: "not-a-digest" }]), /invalid file/) }) +test("Cloudflare wp-content manifests allow bounded user code and reject runtime-owned files", () => { + const sha256 = "a".repeat(64) + const valid = [{ path: "plugins/example/example.php", size: 128, sha256, objectKey: `${R2_WP_CONTENT_OBJECT_PREFIX}/${sha256}` }] + assert.doesNotThrow(() => validateWpContentManifestFiles(valid)) + assert.doesNotThrow(() => validateWpContentMetadata([{ path: "themes/example/style.css", size: 0 }, { path: "languages/example.mo", size: 1 }, { path: "mu-plugins/example.php", size: 1 }])) + assert.throws(() => validateWpContentMetadata([{ path: "plugins/../secret.php", size: 1 }]), /invalid file/) + assert.throws(() => validateWpContentMetadata([{ path: "plugins/markdown-database-integration/owned.php", size: 1 }]), /invalid file/) + assert.throws(() => validateWpContentMetadata([{ path: "plugins/sqlite-database-integration/owned.php", size: 1 }]), /invalid file/) + assert.throws(() => validateWpContentMetadata([{ path: "mu-plugins/wp-codebox-cloudflare-canonical-changes.php", size: 1 }]), /invalid file/) + assert.throws(() => validateWpContentMetadata([{ path: "mu-plugins/wp-codebox-cloudflare-canonical-changes.php/child.php", size: 1 }]), /invalid file/) + assert.throws(() => validateWpContentMetadata([{ path: "themes/example/large.css", size: MAX_WP_CONTENT_FILE_BYTES + 1 }]), /invalid file/) + assert.throws(() => validateWpContentManifestFiles([{ ...valid[0], objectKey: "sites/default/uploads/objects/foreign" }]), /invalid file/) + assert.doesNotThrow(() => validateWpContentDeletedPaths(["themes/example/style.css"])) + assert.throws(() => validateWpContentDeletedPaths(["themes/z/style.css", "themes/a/style.css"]), /non-deterministic/) +}) + test("Cloudflare health response preserves the Codebox execution envelope", async () => { const health = { schema: CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, @@ -215,8 +232,8 @@ test("Cloudflare canonical runtime patches the unique init call with runtime per assert.match(patcher, /return \(string\) _wp_array_get\( \$this->theme_json, array\( 'styles', 'css' \), '' \)/) assert.doesNotMatch(patcher, /mu-plugins|add_action\(|WP_Site_Health|wp_schedule_site_health_cron|\$GLOBALS\['wp_filter'\]/) assert.doesNotMatch(worker, /materializeCanonicalCronAdapter|wp-codebox-canonical-cron-policy/) - assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalRuntimePoliciesAtInit = false,\n uploadFiles\?: RuntimeFile\[\],/) - assert.match(worker, /authConstants, bucket, true, revision\.uploads\), pointer/) + assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalRuntimePoliciesAtInit = false,\n uploadFiles\?: RuntimeFile\[\],\n wpContentFiles\?: RuntimeFile\[\],/) + assert.match(worker, /authConstants, bucket, true, revision\.uploads, revision\.wpContent, revision\.wpContentDeleted\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonicalBootstrapPasswordCode/) assert.match(worker, /canonicalBootstrapUrlCode/) @@ -258,8 +275,10 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /sites\/default\/markdown\/revisions\/\$\{candidate\.revision\}\.json/) assert.match(worker, /canonicalWordPressAuthConstants\(env\)/) assert.match(worker, /authConstants/) + assert.match(worker, /const wpContentResponse = await serveWordPressWpContent\(request, env\.WORDPRESS_STATE_BUCKET, coordinator\)/) assert.match(worker, /const staticResponse = await serveWordPressStaticAsset\(request, env\.WORDPRESS_STATE_BUCKET\)/) - assert.ok(worker.indexOf("const staticResponse = await serveWordPressStaticAsset(request, env.WORDPRESS_STATE_BUCKET)") < worker.indexOf("const coordinator = env.WORDPRESS_STATE.getByName")) + assert.ok(worker.indexOf("const wpContentResponse = await serveWordPressWpContent(request, env.WORDPRESS_STATE_BUCKET, coordinator)") < worker.indexOf("const staticResponse = await serveWordPressStaticAsset(request, env.WORDPRESS_STATE_BUCKET)")) + assert.ok(worker.indexOf("const staticResponse = await serveWordPressStaticAsset(request, env.WORDPRESS_STATE_BUCKET)") < worker.indexOf("const route = routeWorkerRequest(request)")) assert.match(worker, /CONCATENATE_SCRIPTS: false/) assert.match(worker, /SCRIPT_DEBUG: false/) assert.doesNotMatch(worker, /decodeRemoteZip|WORDPRESS_ARCHIVE_URL/) @@ -289,6 +308,14 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(worker, /collectUploadFiles\(runtime\.php\)/) assert.match(worker, /materializeRuntimeFiles\(php, UPLOADS_ROOT, uploadFiles\)/) assert.match(worker, /"x-wp-codebox-static": "r2-upload"/) + assert.match(worker, /collectWpContentFiles\(runtime\.php\)/) + assert.match(worker, /persistWpContentObjects\(bucket, wpContent/) + assert.match(worker, /validateWpContentManifestFiles\(manifest\.wpContent \?\? \[\]\)/) + assert.match(worker, /validateWpContentDeletedPaths\(manifest\.wpContentDeleted \?\? \[\]\)/) + assert.match(worker, /materializeWpContentTombstones\(php, wpContentDeleted\)/) + assert.match(worker, /"x-wp-codebox-static": "r2-wp-content"/) + assert.match(worker, /url\.pathname\.startsWith\("\/wp-admin\/"\).*url\.searchParams\.get\("action"\)/) + assert.match(worker, /if \(!file\) return null/) assert.equal((worker.match(/validateUploadManifestFiles\(manifest\??\.uploads \?\? \[\]\)/g) ?? []).length, 2) assert.match(materializer, /materializeWordPressRuntimeArtifact\(php, bucket, wordpressRuntimeArtifactManifest/) assert.doesNotMatch(materializer, /decodeRemoteZip\(WORDPRESS_ARCHIVE_URL/)

    0a z2(8n7VOQ8@N6+K@s12$p9koN%GB;Omnl2QxKin<8);`aMKq*FTTibr5;sQl}9+ew8 z7|b=fa#b9RqqgB-Vk{H9eFpr{y&AL`D!-=cb90que#=I$z%Vth4q*qEElsNC7#mlr zT?YJoah6=dM`IH3|6fm2-OD*_0qF>O!{#kH`7yHPE?17=1Y| zye{g_5Nwcq9(90;7~pH97GJ@4g{FdRG$!th;fP8l#9y^(Ec+_mqJDj75GDMf*dEu> z8w3ika>dgVCPI92oN;Z2B*vHQC@*E`62I#v!JtGB+9cF{W2^Mdl);NgfElW2_{9d! z!vrvam?Pd~0G4qLYJh})}79{Hni4D(BUY{=K_As7B4zU?-l@! zG{p3wXP2lZj+fmQJ%?B*>=f5vZbef%-Xkuro@PCC@;E4%FRpF+u>kvO^)~$)a&bJe zJsnFjoOxkj@uT<+(RDmqiL-4FQz#5W;@jv2<>7NXU5r79i2maGa1Of;lE13R|BmJM50WyK*|18Mh zZ599qSknT)02l-ZS^z8|kJ(f=~IZ3QrdY?K4m^4S*J$Zw|l)7uozPUk$j|24Ds` Yul=7Fp3c7o>)8U(VfFO^|1$jl0rC+3p#T5? delta 12043 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 diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 7ec2bf47..8df8cf9a 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -25,7 +25,7 @@ const PHP_VERSION = "8.5.8" // Browser assets must come from the same immutable WordPress release as the server corpus. const WORDPRESS_ARCHIVE_URL = (wordpressRuntimeArtifactManifest as WordPressRuntimeArtifactManifest).source.url const SQLITE_INTEGRATION_ARCHIVE_URL = "https://github.com/WordPress/sqlite-database-integration/releases/download/v2.2.23/plugin-sqlite-database-integration.zip" -const MARKDOWN_DATABASE_INTEGRATION_REVISION = "7cf025f2d64aa933d937f1a18a129e278c231783" +const MARKDOWN_DATABASE_INTEGRATION_REVISION = "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c" const SITE_URL = "https://wp-codebox-runtime.invalid" const DATABASE_PATH = "/wordpress/wp-content/database/.ht.sqlite" const MARKDOWN_ROOT = "/wordpress/wp-content/markdown" diff --git a/scripts/build-cloudflare-canonical-mdi-seed.php b/scripts/build-cloudflare-canonical-mdi-seed.php index 4b660420..b841adb8 100644 --- a/scripts/build-cloudflare-canonical-mdi-seed.php +++ b/scripts/build-cloudflare-canonical-mdi-seed.php @@ -1,7 +1,7 @@ assert.equal(markdownIndex.subarray(0, 16).toString(), "SQLite format 3\0") assert.equal(markdownRuntime.subarray(0, 4).toString("hex"), "504b0304") assert.equal(canonicalSeed.subarray(0, 4).toString("hex"), "504b0304") - assert.equal(canonicalManifest.markdownDatabaseIntegrationRevision, "7cf025f2d64aa933d937f1a18a129e278c231783") + assert.equal(canonicalManifest.markdownDatabaseIntegrationRevision, "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c") assert.equal(canonicalManifest.wordpressInstallSeedSha256, createHash("sha256").update(sqliteInput).digest("hex")) assert.equal(canonicalManifest.archiveSha256, createHash("sha256").update(canonicalSeed).digest("hex")) assert.ok(canonicalManifest.files.some((file) => file.path.endsWith(".md"))) @@ -152,7 +152,7 @@ test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => }) test("Cloudflare runtime pins and bundles the public constrained MDI runtime", async () => { - const revision = "7cf025f2d64aa933d937f1a18a129e278c231783" + const revision = "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c" const jsonMachineRevision = "8bf0b0ff6ff60ab480778eaa5ad7d505b442c2d4" const generator = await readFile(new URL("../scripts/build-cloudflare-mdi-runtime-bundle.mjs", import.meta.url), "utf8") const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") From 9c7bbab7ab8e98404bbe89ba84df8162535ca55b Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 18:57:29 -0400 Subject: [PATCH 66/87] Revert "Persist mutation stage evidence" This reverts commit 7ffdeb726b63eed5677c20377c1cd4f35eab3619. --- packages/runtime-cloudflare/src/worker.ts | 58 ++--------------------- tests/cloudflare-runtime.test.ts | 2 - 2 files changed, 3 insertions(+), 57 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 8df8cf9a..1a3735a8 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -262,20 +262,13 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi response = await canonicalAuthProbe(runtime, request) } else { if (isMutation(request, route)) runtime.php.writeFile(MARKDOWN_CHANGES_PATH, new TextEncoder().encode(JSON.stringify({ created: [], changed: [], deleted: [] }))) - if (isMutation(request, route)) await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "php-started") response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) - if (isMutation(request, route)) { - await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "php-completed") - canonicalChanges = readCanonicalChanges(runtime.php) - await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "changes-collected", { changes: canonicalChanges }) - } + if (isMutation(request, route)) canonicalChanges = readCanonicalChanges(runtime.php) } if (isMutation(request, route)) { if (!canonicalChanges) throw new Error("Canonical mutation completed without an MDI change set.") const next = await persistRuntime(env.WORDPRESS_STATE_BUCKET, runtime, canonicalChanges) - await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "revision-persisted", { revision: next.revision }) await commitLease(coordinator, request.url, lease, next) - await recordMutationStage(env.WORDPRESS_STATE_BUCKET, request, "lease-committed", { revision: next.revision }) } else { await releaseLease(coordinator, request.url, lease) } @@ -288,10 +281,6 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi } } -function recordMutationStage(bucket: R2Bucket, request: Request, stage: string, details: Record = {}): Promise { - return bucket.put("diagnostics/mutation-stage.json", JSON.stringify({ schema: "wp-codebox/cloudflare-mutation-stage/v1", method: request.method, path: new URL(request.url).pathname, stage, recordedAt: new Date().toISOString(), ...details })) -} - function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate" | "canonical-auth"): boolean { return route === "r2-mutate" || !["GET", "HEAD", "OPTIONS"].includes(request.method) } @@ -730,45 +719,12 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-wordpress", "mdi-option", "mdi-insert", "mdi-insert-no-post-flush", "mdi-insert-no-index-rebuild", "mdi-insert-no-shutdown-flush", "mdi-insert-no-writes", "mdi-insert-dirty-inventory"].includes(phase) || phase.startsWith("mdi-insert-flush-")) { + if (phase === "mdi-wordpress" || phase === "mdi-option" || phase === "mdi-insert") { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { - const optionTarget = { - "mdi-insert-flush-option-rewrite-rules": "rewrite_rules", - "mdi-insert-flush-option-calendar": "wp_calendar_block_has_published_posts", - "mdi-insert-flush-option-fresh-site": "fresh_site", - }[phase] - const flushTarget = optionTarget ? "options" : { - "mdi-insert-flush-options": "options", - "mdi-insert-flush-options-no-index": "options", - "mdi-insert-flush-options-fetch-only": "options", - "mdi-insert-flush-options-no-tracking": "options", - "mdi-insert-flush-options-shared-temp-path": "options", - "mdi-insert-flush-taxonomy": "term_taxonomy", - "mdi-insert-flush-relationships": "term_relationships_non_markdown", - "mdi-insert-flush-postmeta": "postmeta_non_markdown", - }[phase] - if (flushTarget) { - patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") - patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "foreach ( array_keys( $this->dirty ) as $table_suffix ) {", `foreach ( array_keys( $this->dirty ) as $table_suffix ) { if ( $table_suffix !== '${flushTarget}' ) continue;`) - } - if (phase === "mdi-insert-flush-options-no-index") { - patchMdiProbe(runtime.php, "inc/class-wp-markdown-driver.php", "public function upsert_options_index( array $rows ): void {", "public function upsert_options_index( array $rows ): void { return;") - patchMdiProbe(runtime.php, "inc/class-wp-markdown-driver.php", "public function remove_from_options_index( array $option_names ): void {", "public function remove_from_options_index( array $option_names ): void { return;") - } - if (phase === "mdi-insert-flush-options-fetch-only") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "$index_deletes = array();\n\n\t\tforeach ( $names as $name ) {", "$index_deletes = array();\n\n\t\tforeach ( array() as $name ) {") - if (phase === "mdi-insert-flush-options-no-tracking") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "public function track_canonical_mutation( string $absolute_path ): void {", "public function track_canonical_mutation( string $absolute_path ): void { return;") - if (phase === "mdi-insert-flush-options-shared-temp-path") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "$tmp = $abs . '.tmp.' . getmypid() . '.' . substr( md5( uniqid( '', true ) ), 0, 8 );", "$tmp = $this->json_tmp_path( $abs );") - if (optionTarget) patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", ": array_keys( $this->dirty_option_names );\n\n\t\tif ( empty( $names ) ) {", `: array_keys( $this->dirty_option_names );\n\n\t\t$names = array('${optionTarget}');\n\n\t\tif ( empty( $names ) ) {`) - if (phase === "mdi-insert-no-post-flush") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function persist_single_post( int $post_id ): bool {", "private function persist_single_post( int $post_id ): bool { return false;") - if (phase === "mdi-insert-no-index-rebuild") patchMdiProbe(runtime.php, "inc/class-wp-markdown-storage.php", "if ( null === $previous_path ) {", "if ( false && null === $previous_path ) {") - if (phase === "mdi-insert-no-shutdown-flush" || phase === "mdi-insert-dirty-inventory") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "private function ensure_shutdown_registered(): void {", "private function ensure_shutdown_registered(): void { return;") - if (phase === "mdi-insert-no-writes") patchMdiProbe(runtime.php, "inc/class-wp-markdown-write-engine.php", "public function persist_write( string $query, string $table, string $op_type ): void {", "public function persist_write( string $query, string $table, string $op_type ): void { return;") const operation = phase === "mdi-option" ? "$updated = update_option('wp_codebox_mdi_probe', 1); $result = ['updated' => $updated];" - : phase === "mdi-insert-dirty-inventory" - ? "$post_id = wp_insert_post(['post_title' => 'MDI Probe', 'post_name' => 'mdi-probe', 'post_content' => 'MDI probe body.', 'post_status' => 'publish', 'post_type' => 'post'], true); if (is_wp_error($post_id)) { throw new Exception($post_id->get_error_message()); } global $wpdb; $driver_ref = new ReflectionObject($wpdb->dbh); $engine_prop = $driver_ref->getProperty('write_engine'); $engine = $engine_prop->getValue($wpdb->dbh); $engine_ref = new ReflectionObject($engine); $dirty_prop = $engine_ref->getProperty('dirty'); $posts_prop = $engine_ref->getProperty('dirty_posts'); $options_prop = $engine_ref->getProperty('dirty_option_names'); $result = ['postId' => $post_id, 'dirty' => array_keys($dirty_prop->getValue($engine)), 'dirtyPosts' => array_keys($posts_prop->getValue($engine)), 'dirtyOptions' => array_keys($options_prop->getValue($engine))];" - : phase.startsWith("mdi-insert") + : phase === "mdi-insert" ? "$post_id = wp_insert_post(['post_title' => 'MDI Probe', 'post_name' => 'mdi-probe', 'post_content' => 'MDI probe body.', 'post_status' => 'publish', 'post_type' => 'post'], true); if (is_wp_error($post_id)) { throw new Exception($post_id->get_error_message()); } $result = ['postId' => $post_id];" : "$result = [];" const evidence = (await runtime.php.run({ @@ -888,14 +844,6 @@ echo json_encode([ return new Response(`Unknown boot probe phase: ${phase}`, { status: 400 }) } -function patchMdiProbe(php: PHP, relativePath: string, needle: string, replacement: string): void { - const path = `/wordpress/wp-content/plugins/markdown-database-integration/${relativePath}` - const source = new TextDecoder().decode(php.readFileAsBuffer(path)) - const index = source.indexOf(needle) - if (index === -1 || index !== source.lastIndexOf(needle)) throw new Error(`MDI probe marker is not unique: ${relativePath}`) - php.writeFile(path, new TextEncoder().encode(`${source.slice(0, index)}${replacement}${source.slice(index + needle.length)}`)) -} - function wordpressProbeCode(phase: string): string { if (phase === "seeded-shortinit") { return " $wp_version, 'shortInit' => true]);" diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index c20b7ce5..70fbaa99 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -632,8 +632,6 @@ test("serialized Cloudflare mutations use MDI flush paths and complete canonical assert.match(source, /packagedCanonicalMarkdownSeed/) assert.match(source, /update_option\('siteurl'/) assert.match(source, /WORDPRESS_ADMIN_PASSWORD is required/) - for (const stage of ["php-started", "php-completed", "changes-collected", "revision-persisted", "lease-committed"]) assert.match(source, new RegExp(`recordMutationStage\\([^,]+, request, "${stage}"`)) - assert.match(source, /diagnostics\/mutation-stage\.json/) }) test("canonical MDI seed generator is reproducible and validates its pinned inputs", async () => { From 01a552a7fec37e673458331f073b7f62707bb734 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 18:58:37 -0400 Subject: [PATCH 67/87] Revert "Persist canonical bootstrap stage evidence" This reverts commit b7cc8aea47edb6cdc2d590eb1bd1009e975d0214. --- .../runtime-cloudflare/src/request-routing.ts | 4 ++-- packages/runtime-cloudflare/src/worker.ts | 24 ++++--------------- tests/cloudflare-runtime.test.ts | 4 +--- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 55f35133..7ca4688b 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -8,7 +8,7 @@ export type WorkerRequestRoute = | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-global-skip-custom-css" | "settings-before-global-skip-theme-styles" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "after-init-script" | "before-admin-header" | "after-admin-header" | "block-editor" | "footer-before-scripts" | "footer-after-admin-footer" | "footer-after-print-scripts" +export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-global-skip-custom-css" | "settings-before-global-skip-theme-styles" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "block-editor" export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") @@ -20,7 +20,7 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "operator-reset") return { kind: "operator-reset" } if (phase?.startsWith("editor-memory-")) { const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "after-init-script", "before-admin-header", "after-admin-header", "block-editor", "footer-before-scripts", "footer-after-admin-footer", "footer-after-print-scripts"].includes(editorPhase)) { + if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"].includes(editorPhase)) { return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 1a3735a8..65b10ba6 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -150,13 +150,7 @@ function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { "settings-before-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableResolvedAssets']", before: true }, "settings-after-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableIsBlockBasedTheme']", before: true }, "after-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: false }, - "after-init-script": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "if ( (int) get_option( 'page_for_posts' ) === $post->ID ) {", before: true }, - "before-admin-header": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "require_once ABSPATH . 'wp-admin/admin-header.php';", before: true }, - "after-admin-header": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "require_once ABSPATH . 'wp-admin/admin-header.php';", before: false }, "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, - "footer-before-scripts": { path: "/wordpress/wp-admin/admin-footer.php", marker: "do_action( 'admin_footer', '' );", before: true }, - "footer-after-admin-footer": { path: "/wordpress/wp-admin/admin-footer.php", marker: "do_action( \"admin_print_footer_scripts-{$hook_suffix}\" );", before: true }, - "footer-after-print-scripts": { path: "/wordpress/wp-admin/admin-footer.php", marker: "do_action( \"admin_footer-{$hook_suffix}\" );", before: true }, } const stop = stops[phase] let source = new TextDecoder().decode(php.readFileAsBuffer(stop.path)) @@ -379,27 +373,24 @@ async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: s async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { if (!env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") const origin = new URL(requestUrl).origin - await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "boot-started") + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "boot-started" })) const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env), env.WORDPRESS_STATE_BUCKET, true) - await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "boot-completed") + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "boot-completed" })) try { const passwordFile = "/tmp/wordpress-admin-password" runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) const passwordOutput = (await runtime.php.run({ code: canonicalBootstrapPasswordCode(passwordFile) })).text.trim() if (passwordOutput !== "password-updated") throw new Error("Canonical bootstrap did not update the admin password.") - await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "password-updated") const urlOutput = (await runtime.php.run({ code: canonicalBootstrapUrlCode(origin) })).text.trim() if (urlOutput !== "urls-updated") throw new Error("Canonical bootstrap did not update the site URLs.") - await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "urls-updated") const flushOutput = (await runtime.php.run({ code: canonicalBootstrapFlushCode() })).text.trim() if (flushOutput !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") - await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "setup-completed") const files = collectRuntimeFiles(runtime.php, MARKDOWN_ROOT) - await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "files-collected", { files: files.length }) + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "files-collected", files: files.length })) const pointer = await persistMarkdownRevision(env.WORDPRESS_STATE_BUCKET, files) - await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "revision-persisted", { revision: pointer.revision }) + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "revision-persisted", revision: pointer.revision })) await commitLease(coordinator, requestUrl, lease, pointer) - await recordBootstrapStage(env.WORDPRESS_STATE_BUCKET, "lease-committed", { revision: pointer.revision }) + console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "lease-committed", revision: pointer.revision })) return { ...runtime, pointer } } catch (error) { runtime.php.exit() @@ -433,11 +424,6 @@ require '/wordpress/wp-load.php'; $GLOBALS['wpdb']->flush_canonical_writes(); echo 'flushed';` } - -function recordBootstrapStage(bucket: R2Bucket, stage: string, details: Record = {}): Promise { - return bucket.put("diagnostics/bootstrap-stage.json", JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage, recordedAt: new Date().toISOString(), ...details })) -} - async function canonicalWordPressAuthConstants(env: Env): Promise> { return deriveWordPressAuthConstants(env.WORDPRESS_AUTH_SECRET ?? "", "default") } diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 70fbaa99..67ebbbad 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -118,7 +118,7 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "after-init-script", "before-admin-header", "after-admin-header", "block-editor", "footer-before-scripts", "footer-after-admin-footer", "footer-after-print-scripts"]) assert.match(probe, new RegExp(`"?${phase}"?`)) + for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) assert.match(probe, /memory_get_peak_usage/) @@ -427,11 +427,9 @@ test("Cloudflare canonical runtime patches the unique init call with runtime per assert.match(worker, /authConstants, bucket, true\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) - for (const stage of ["boot-started", "boot-completed", "password-updated", "urls-updated", "setup-completed", "files-collected", "revision-persisted", "lease-committed"]) assert.match(worker, new RegExp(`recordBootstrapStage\\([^,]+, "${stage}"`)) assert.match(worker, /canonicalBootstrapPasswordCode/) assert.match(worker, /canonicalBootstrapUrlCode/) assert.match(worker, /canonicalBootstrapFlushCode/) - assert.match(worker, /diagnostics\/bootstrap-stage\.json/) assert.match(worker, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) assert.match(worker, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) assert.match(worker, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/) From 5355fbda8785d342ef54d007eb8ade7d458c331e Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 18:59:25 -0400 Subject: [PATCH 68/87] Revert "Log canonical bootstrap stages" This reverts commit 2e4948d1abedbbcd92f2bb0c0dc365e138679817. --- packages/runtime-cloudflare/src/worker.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 65b10ba6..366fbac5 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -373,9 +373,7 @@ async function bootRuntime(bucket: R2Bucket, pointer: MarkdownPointer, origin: s async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStub, requestUrl: string, lease: Lease): Promise { if (!env.WORDPRESS_ADMIN_PASSWORD) throw new Error("WORDPRESS_ADMIN_PASSWORD is required to bootstrap a complete canonical WordPress revision.") const origin = new URL(requestUrl).origin - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "boot-started" })) const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), origin, await canonicalWordPressAuthConstants(env), env.WORDPRESS_STATE_BUCKET, true) - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "boot-completed" })) try { const passwordFile = "/tmp/wordpress-admin-password" runtime.php.writeFile(passwordFile, new TextEncoder().encode(env.WORDPRESS_ADMIN_PASSWORD)) @@ -385,12 +383,8 @@ async function bootstrapCanonicalRuntime(env: Env, coordinator: DurableObjectStu if (urlOutput !== "urls-updated") throw new Error("Canonical bootstrap did not update the site URLs.") const flushOutput = (await runtime.php.run({ code: canonicalBootstrapFlushCode() })).text.trim() if (flushOutput !== "flushed") throw new Error("MDI did not confirm canonical bootstrap flush.") - const files = collectRuntimeFiles(runtime.php, MARKDOWN_ROOT) - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "files-collected", files: files.length })) - const pointer = await persistMarkdownRevision(env.WORDPRESS_STATE_BUCKET, files) - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "revision-persisted", revision: pointer.revision })) + const pointer = await persistMarkdownRevision(env.WORDPRESS_STATE_BUCKET, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT)) await commitLease(coordinator, requestUrl, lease, pointer) - console.log(JSON.stringify({ schema: "wp-codebox/cloudflare-bootstrap-stage/v1", stage: "lease-committed", revision: pointer.revision })) return { ...runtime, pointer } } catch (error) { runtime.php.exit() From 78f28097efe3d69a312a09ec79a73fef4597c7c7 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 19:00:31 -0400 Subject: [PATCH 69/87] Revert "Probe MDI auto-draft persistence" This reverts commit a56125c56f51d31414e850525ae718813da9b51e. --- .../runtime-cloudflare/src/request-routing.ts | 9 --- packages/runtime-cloudflare/src/worker.ts | 73 +------------------ tests/cloudflare-runtime.test.ts | 10 --- 3 files changed, 1 insertion(+), 91 deletions(-) diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 7ca4688b..51326da6 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -5,11 +5,8 @@ export type WorkerRequestRoute = | { kind: "r2-mutate" } | { kind: "canonical-auth" } | { kind: "operator-reset" } - | { kind: "editor-memory-probe"; phase: EditorMemoryProbePhase } | { kind: "probe"; phase: string } -export type EditorMemoryProbePhase = "admin" | "before-insert" | "after-insert" | "after-get-post" | "before-hooks" | "after-hooks" | "before-preload-paths" | "before-rest-preload" | "before-rest-preload-skip-global-styles" | "after-rest-preload" | "after-block-definitions" | "before-editor-settings" | "settings-before-styles" | "settings-after-presets" | "settings-after-block-classes" | "settings-before-global" | "settings-before-global-skip-custom-css" | "settings-before-global-skip-theme-styles" | "settings-before-assets" | "settings-after-assets" | "after-editor-settings" | "block-editor" - export function routeWorkerRequest(request: Request): WorkerRequestRoute { const phase = new URL(request.url).searchParams.get("phase") if (phase === null) return { kind: "wordpress" } @@ -18,11 +15,5 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "r2-mutate") return { kind: "r2-mutate" } if (phase === "canonical-auth") return { kind: "canonical-auth" } if (phase === "operator-reset") return { kind: "operator-reset" } - if (phase?.startsWith("editor-memory-")) { - const editorPhase = phase.slice("editor-memory-".length) - if (["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"].includes(editorPhase)) { - return { kind: "editor-memory-probe", phase: editorPhase as EditorMemoryProbePhase } - } - } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 366fbac5..27f30a73 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -7,7 +7,7 @@ import { dependenciesTotalSize, init } from "../../../node_modules/@php-wasm/web import phpWasmModule from "../../../node_modules/@php-wasm/web-8-5/asyncify/8_5_8/php_8_5.wasm" import { CLOUDFLARE_RUNTIME_HEALTH_MARKER, CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, cloudflareRuntimeHealthResponse } from "./health-envelope.js" import { leaseRetryDelayMs } from "./lease-retry.js" -import { routeWorkerRequest, type EditorMemoryProbePhase } from "./request-routing.js" +import { routeWorkerRequest } from "./request-routing.js" import { toFetchResponse, toPHPRequest } from "./request-translation.js" import { deriveWordPressAuthConstants, type WordPressAuthConstant } from "./wordpress-auth.js" import { isWordPressRuntimeFile, wordpressStaticArchivePath, wordpressStaticContentType } from "./wordpress-runtime-corpus.js" @@ -86,7 +86,6 @@ export default { const route = routeWorkerRequest(request) const coordinator = env.WORDPRESS_STATE.getByName("default") if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) - if (route.kind === "editor-memory-probe") return runEditorMemoryProbe(request, env, coordinator, route.phase) if (route.kind === "probe") { if (route.phase === "canonical-session") return canonicalSessionProbe(env.WORDPRESS_STATE_BUCKET, await coordinatorCall<{ pointer: MarkdownPointer | null }>(coordinator, request.url, "state")) return runBootProbe(route.phase, env.WORDPRESS_STATE_BUCKET) @@ -100,76 +99,6 @@ export default { }, } -async function runEditorMemoryProbe(request: Request, env: Env, coordinator: DurableObjectStub, phase: EditorMemoryProbePhase): Promise { - const lease = await acquireLease(coordinator, request.url) - let finalized = false - try { - if (!lease.pointer) throw new Error("Canonical WordPress must be initialized before running an editor memory probe.") - const runtime = await getRuntime(env, lease.pointer, new URL(request.url).origin) - patchEditorMemoryStop(runtime.php, phase) - const response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) - await releaseLease(coordinator, request.url, lease) - finalized = true - await discardCachedRuntime() - return response - } catch (error) { - if (!finalized) await abortLease(coordinator, request.url, lease) - await discardCachedRuntime() - throw error - } -} - -function patchEditorMemoryStop(php: PHP, phase: EditorMemoryProbePhase): void { - const stops: Record = { - admin: { path: "/wordpress/wp-admin/post-new.php", marker: "require_once __DIR__ . '/admin.php';", before: false }, - "before-insert": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( $create_in_db ) {", before: false }, - "after-insert": { path: "/wordpress/wp-admin/includes/post.php", marker: `$post_id = wp_insert_post( - array( - 'post_title' => post_type_supports( $post_type, 'title' ) ? __( 'Auto Draft' ) : '', - 'post_type' => $post_type, - 'post_status' => 'auto-draft', - ), - true, - false - );`, before: false }, - "after-get-post": { path: "/wordpress/wp-admin/includes/post.php", marker: "if ( current_theme_supports( 'post-formats' )", before: true }, - "before-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "wp_after_insert_post( $post, false, null );", before: true }, - "after-hooks": { path: "/wordpress/wp-admin/includes/post.php", marker: "// Schedule auto-draft cleanup.", before: true }, - "before-preload-paths": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$preload_paths = array(", before: true }, - "before-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: true }, - "before-rest-preload-skip-global-styles": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: true }, - "after-rest-preload": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "block_editor_rest_api_preload( $preload_paths, $block_editor_context );", before: false }, - "after-block-definitions": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "// Preload server-registered block bindings sources.", before: true }, - "before-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: true }, - "settings-before-styles": { path: "/wordpress/wp-includes/block-editor.php", marker: "$global_styles = array();", before: true }, - "settings-after-presets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$block_classes = array(", before: true }, - "settings-after-block-classes": { path: "/wordpress/wp-includes/block-editor.php", marker: "// Get any additional css from the customizer", before: true }, - "settings-before-global": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__experimentalFeatures'] = wp_get_global_settings();", before: true }, - "settings-before-global-skip-custom-css": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__experimentalFeatures'] = wp_get_global_settings();", before: true }, - "settings-before-global-skip-theme-styles": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__experimentalFeatures'] = wp_get_global_settings();", before: true }, - "settings-before-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableResolvedAssets']", before: true }, - "settings-after-assets": { path: "/wordpress/wp-includes/block-editor.php", marker: "$editor_settings['__unstableIsBlockBasedTheme']", before: true }, - "after-editor-settings": { path: "/wordpress/wp-admin/edit-form-blocks.php", marker: "$editor_settings = get_block_editor_settings( $editor_settings, $block_editor_context );", before: false }, - "block-editor": { path: "/wordpress/wp-admin/post-new.php", marker: "require_once ABSPATH . 'wp-admin/admin-footer.php';", before: true }, - } - const stop = stops[phase] - let source = new TextDecoder().decode(php.readFileAsBuffer(stop.path)) - if (phase === "before-rest-preload-skip-global-styles") { - const lookup = "WP_Theme_JSON_Resolver::get_user_global_styles_post_id()" - if (source.split(lookup).length - 1 !== 2) throw new Error("WordPress global styles lookup count changed.") - source = source.replaceAll(lookup, "0") - } - if (phase === "settings-before-global-skip-custom-css") { - source = source.replace("wp_get_custom_css()", "''").replace("wp_get_global_stylesheet( array( 'custom-css' ) )", "''") - } - if (phase === "settings-before-global-skip-theme-styles") source = source.replace("get_block_editor_theme_styles()", "array()") - const index = source.indexOf(stop.marker) - if (index === -1 || index !== source.lastIndexOf(stop.marker)) throw new Error(`WordPress editor memory marker is not unique: ${phase}`) - const insertion = stop.before ? index : index + stop.marker.length - const output = `\necho wp_json_encode( array( 'schema' => 'wp-codebox/cloudflare-editor-memory/v1', 'phase' => '${phase}', 'memoryBytes' => memory_get_usage( true ), 'peakMemoryBytes' => memory_get_peak_usage( true ) ) ); exit;\n` - php.writeFile(stop.path, new TextEncoder().encode(`${source.slice(0, insertion)}${output}${source.slice(insertion)}`)) -} - interface MarkdownManifestFile { path: string objectKey: string diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 67ebbbad..7f11b921 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -43,7 +43,6 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-mutate")), { kind: "r2-mutate" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=canonical-auth")), { kind: "canonical-auth" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=operator-reset")), { kind: "operator-reset" }) - assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/wp-admin/post-new.php?phase=editor-memory-after-insert")), { kind: "editor-memory-probe", phase: "after-insert" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) @@ -115,15 +114,6 @@ test("Cloudflare lease contention honors Retry-After without exceeding the acqui assert.equal(leaseRetryDelayMs(Number.NaN, 500), 500) }) -test("Cloudflare editor memory probes stop at bounded lifecycle markers and discard their runtime", async () => { - const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const probe = worker.slice(worker.indexOf("async function runEditorMemoryProbe"), worker.indexOf("async function resetCanonicalWordPress")) - for (const phase of ["admin", "before-insert", "after-insert", "after-get-post", "before-hooks", "after-hooks", "before-preload-paths", "before-rest-preload", "before-rest-preload-skip-global-styles", "after-rest-preload", "after-block-definitions", "before-editor-settings", "settings-before-styles", "settings-after-presets", "settings-after-block-classes", "settings-before-global", "settings-before-global-skip-custom-css", "settings-before-global-skip-theme-styles", "settings-before-assets", "settings-after-assets", "after-editor-settings", "block-editor"]) assert.match(probe, new RegExp(`"?${phase}"?`)) - assert.match(probe, /source\.replaceAll\(lookup, "0"\)/) - assert.equal((probe.match(/await discardCachedRuntime\(\)/g) ?? []).length, 2) - assert.match(probe, /memory_get_peak_usage/) -}) - test("Cloudflare runtime packages a provenanced canonical MDI seed", async () => { const config = JSON.parse((await readFile(new URL("../packages/runtime-cloudflare/wrangler.jsonc", import.meta.url), "utf8")).replace(/^\s*\/\/.*\n/, "")) as { rules?: Array<{ type?: string; globs?: string[] }> From 0ca5b64c1c03fd1d2d09cfebcd5ddbe8081e91ba Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 19:01:13 -0400 Subject: [PATCH 70/87] Revert "Probe canonical lifecycle boundaries" This reverts commit ba72370793f690967196f0e31233e132503e4bf6. --- packages/runtime-cloudflare/README.md | 2 - packages/runtime-cloudflare/src/worker.ts | 129 +--------------------- scripts/cloudflare-local-gate.mjs | 30 +---- tests/cloudflare-runtime.test.ts | 54 --------- 4 files changed, 2 insertions(+), 213 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index c23d6eea..1b509863 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -26,8 +26,6 @@ Every `mdi-*` lifecycle diagnostic materializes the validated packaged canonical `?phase=canonical-wordpress` performs a full normal WordPress boot with that exact seed and artifact outside the coordinator, then returns WordPress version, memory, and non-sensitive table counts. Its widget-state count includes the `widget_*` options and `sidebars_widgets`. `?phase=canonical-bootstrap-setup` additionally runs the normal bootstrap setup with a probe-only internal password and fixed probe origin, flushes only the disposable runtime, and returns changed-path counts. Neither probe returns password, option, user, or content values. -The fixed bounded lifecycle probes `canonical-current-user`, `canonical-init`, `canonical-site-status`, and `canonical-wp-loaded` use the packaged canonical MDI seed, R2 WordPress corpus, and the same disabled-cron scheduling patch as the production boot. Each applies a second unique `wp-settings.php` stop injection to the disposable MEMFS copy, so `init` and `wp_loaded` each execute at most once. They return only completion, phase, WordPress version, memory, and the three disabled-cron callback attachment booleans. - The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-wp-cron`, `mdi-init-exclude-privacy-schedule`, `mdi-init-exclude-update-schedule`, `mdi-init-exclude-wp-cron-privacy-schedule`, `mdi-init-exclude-wp-cron-update-schedule`, `mdi-init-exclude-privacy-update-schedule`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group through WordPress's public `remove_action()` API, runs `init` once, and returns the exact removed names, completion, and memory measurements before subsequent WordPress lifecycle work. The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` reports only fixed widget option names, grouped by `get_option()` presence and direct `$wpdb` SQLite-row presence; it returns no option values. `mdi-widgets-get-settings` calls only `get_settings()` on each default factory object, with `mdi-widgets-get-settings-first` and `mdi-widgets-get-settings-second` as fixed halves; each reports completed classes and array-shape dimensions only. `mdi-widgets-register-one` preloads the fixed `widget_text` option, calls that object's `get_settings()`, then calls only `_register_one()` for its fixed instance. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 27f30a73..bc8f4ebe 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -655,16 +655,6 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (phase === "cwr" || phase === "cwc" || phase === "cwp" || phase === "cwcron" || phase === "cwu" || canonicalLifecyclePhaseAliases[phase] || ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all", "canonical-wp-loaded-suppress-rewrite-persist"].includes(phase)) { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) - try { - const evidence = (await runtime.php.run({ code: canonicalLifecycleProbeCode(phase) })).text.trim() - return probeResponse(phase, JSON.parse(evidence) as Record) - } finally { - runtime.php.exit() - } - } - if (phase === "canonical-wordpress" || phase === "canonical-bootstrap-setup") { const canonicalSeed = await packagedCanonicalMarkdownSeed() const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, canonicalSeed, new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) @@ -1092,123 +1082,6 @@ file_put_contents($settings_path, str_replace($needle, $replacement, $settings)) require '/wordpress/wp-load.php';` } -const canonicalLifecyclePhaseAliases: Record = { - cwr: "canonical-wp-loaded-exclude-rewrite-flush", - cwc: "canonical-wp-loaded-exclude-core-template-header", - cwp: "canonical-wp-loaded-exclude-playground", - cwcron: "canonical-wp-loaded-exclude-wp-cron", - cwu: "canonical-wp-loaded-suppress-rewrite-persist", - "canon-wpl-no-rewrite": "canonical-wp-loaded-exclude-rewrite-flush", - "canon-wpl-no-core": "canonical-wp-loaded-exclude-core-template-header", - "canon-wpl-no-playground": "canonical-wp-loaded-exclude-playground", - "canon-wpl-no-cron": "canonical-wp-loaded-exclude-wp-cron", -} - -function canonicalLifecycleProbeCode(phase: string): string { - const canonicalPhase = canonicalLifecyclePhaseAliases[phase] ?? phase - const wpLoadedExclusions: Record = { - "canonical-wp-loaded-exclude-rewrite-flush": { identifiers: ["WP_Rewrite::flush_rules", "playground_maybe_flush_rewrite_rules"], retain: false }, - "canonical-wp-loaded-exclude-core-template-header": { identifiers: ["_add_template_loader_filters", "_custom_header_background_just_in_time", "_custom_logo_header_styles"], retain: false }, - "canonical-wp-loaded-exclude-playground": { identifiers: ["playground_maybe_flush_rewrite_rules", "playground_save_wp_env_info"], retain: false }, - "canonical-wp-loaded-exclude-wp-cron": { identifiers: ["_wp_cron"], retain: false }, - "canonical-wp-loaded-exclude-all": { identifiers: [], retain: true }, - "canonical-wp-loaded-suppress-rewrite-persist": { identifiers: [], retain: false }, - } - const wpLoadedNeedle = "do_action( 'wp_loaded' );" - const stops: Record = { - "canonical-current-user": { needle: "// Set up current user." }, - "canonical-init": { needle: "do_action( 'init' );", after: true }, - "canonical-site-status": { needle: "// Check site status." }, - "canonical-wp-loaded": { needle: wpLoadedNeedle, after: true }, - "canonical-wp-loaded-callbacks": { needle: wpLoadedNeedle }, - ...Object.fromEntries(Object.keys(wpLoadedExclusions).map((name) => [name, { needle: wpLoadedNeedle }])), - } - const stop = stops[canonicalPhase] - if (!stop) throw new Error(`Unknown canonical lifecycle probe phase: ${phase}`) - - const wpLoadedProbe = canonicalPhase === "canonical-wp-loaded-callbacks" || wpLoadedExclusions[canonicalPhase] - const exclusion = wpLoadedExclusions[canonicalPhase] - const wpLoadedStop = !wpLoadedProbe ? "" : `function wp_codebox_canonical_wp_loaded_callback_identifier($callback) { - if (is_string($callback)) return $callback; - if ($callback instanceof Closure) return 'Closure'; - if (is_array($callback) && count($callback) === 2 && is_string($callback[1])) { - $class = is_object($callback[0]) ? get_class($callback[0]) : $callback[0]; - return is_string($class) ? $class . '::' . $callback[1] : 'Closure'; - } - if (is_object($callback) && method_exists($callback, '__invoke')) return get_class($callback) . '::__invoke'; - return 'Closure'; -} -function wp_codebox_canonical_wp_loaded_inventory() { - $inventory = array(); - $hook = isset($GLOBALS['wp_filter']['wp_loaded']) ? $GLOBALS['wp_filter']['wp_loaded'] : null; - if (!$hook || !isset($hook->callbacks) || !is_array($hook->callbacks)) return $inventory; - $remaining = 100; - foreach ($hook->callbacks as $priority => $callbacks) { - if (0 === $remaining) break; - $identifiers = array(); - foreach ($callbacks as $registered) if (isset($registered['function'])) $identifiers[] = wp_codebox_canonical_wp_loaded_callback_identifier($registered['function']); - sort($identifiers, SORT_STRING); - $identifiers = array_slice($identifiers, 0, $remaining); - if ($identifiers) { - $inventory[(string) $priority] = $identifiers; - $remaining -= count($identifiers); - } - } - ksort($inventory, SORT_NUMERIC); - return $inventory; -} -function wp_codebox_canonical_wp_loaded_remove_snapshot($identifiers, $retain) { - $removed = array(); - $hook = isset($GLOBALS['wp_filter']['wp_loaded']) ? $GLOBALS['wp_filter']['wp_loaded'] : null; - if (!$hook || !isset($hook->callbacks) || !is_array($hook->callbacks)) return $removed; - $callbacks_by_priority = $hook->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { - foreach ($callbacks as $registered) { - if (!isset($registered['function'])) continue; - $identifier = wp_codebox_canonical_wp_loaded_callback_identifier($registered['function']); - $matches = in_array($identifier, $identifiers, true); - if (($retain && !$matches) || (!$retain && $matches)) { - if (remove_action('wp_loaded', $registered['function'], (int) $priority)) $removed[] = $identifier; - } - } - } - sort($removed, SORT_STRING); - return $removed; -} -$inventory = wp_codebox_canonical_wp_loaded_inventory(); -${canonicalPhase === "canonical-wp-loaded-callbacks" ? `echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => '${phase}', 'callbacks' => $inventory, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); -return;` : `$removed = wp_codebox_canonical_wp_loaded_remove_snapshot(${JSON.stringify(exclusion?.identifiers ?? [])}, ${exclusion?.retain ? "true" : "false"}); -${canonicalPhase === "canonical-wp-loaded-suppress-rewrite-persist" ? "add_filter('pre_update_option_rewrite_rules', static function($value, $old_value) { return $old_value; }, PHP_INT_MAX, 2);" : ""} -$memory_before = memory_get_usage(true); -do_action('wp_loaded'); -echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => '${phase}', 'completed' => true, 'callbacks' => $inventory, 'removedCallbacks' => $removed, 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); -return;`}` - return ` $wp_version, - 'bootstrapPhase' => '${phase}', - 'completed' => true, - 'memoryBytes' => memory_get_usage(true), - 'peakMemoryBytes' => memory_get_peak_usage(true), - 'wpCronInitAttached' => false !== has_action('init', 'wp_cron'), - 'updateScheduleInitAttached' => false !== has_action('init', 'wp_schedule_update_checks'), - 'privacyScheduleInitAttached' => false !== has_action('init', 'wp_schedule_delete_old_privacy_export_files'), -)); -return; -PHP; -$replacement = ${stop.after ? "$needle . \"\\n\" . \$stop" : "$stop . \"\\n\" . $needle"}; -file_put_contents($settings_path, str_replace($needle, $replacement, $settings)); -require '/wordpress/wp-load.php';` -} - async function bootWordPressRuntime( wordpressInstallMode: WordPressInstallMode = "install-from-existing-files", includeSqlite = true, @@ -1454,6 +1327,6 @@ function instantiatePrecompiledWasm(module: WebAssembly.Module) { return (imports: WebAssembly.Imports, receiveInstance: (instance: WebAssembly.Instance, wasmModule: WebAssembly.Module) => void) => receiveInstance(new WebAssembly.Instance(module, imports), module) } -function probeResponse(phase: string, evidence: Record): Response { +function probeResponse(phase: string, evidence: Record): Response { return Response.json({ schema: "wp-codebox/cloudflare-boot-probe/v1", phase, completed: true, evidence }) } diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index c5eedeba..75fd8207 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -171,21 +171,6 @@ async function assertHealthResponse() { async function assertCanonicalProbes() { const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json() const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json() - const lifecyclePhases = ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded-callbacks", "canon-wpl-no-rewrite", "canon-wpl-no-core", "canon-wpl-no-playground", "canon-wpl-no-cron", "canonical-wp-loaded-exclude-all"] - const expectedRemovedCallbacks = { - "canon-wpl-no-rewrite": ["WP_Rewrite::flush_rules"], - "canon-wpl-no-core": ["_add_template_loader_filters", "_custom_header_background_just_in_time"], - "canon-wpl-no-playground": ["playground_save_wp_env_info"], - "canon-wpl-no-cron": [], - "canonical-wp-loaded-exclude-all": ["WP_Rewrite::flush_rules", "_add_template_loader_filters", "_custom_header_background_just_in_time", "playground_save_wp_env_info"], - } - const lifecycle = await Promise.all(lifecyclePhases.map(async (phase) => { - const response = await request(`${origin}/?phase=${phase}`) - const body = await response.text() - assertNoPhpDiagnostics(body, phase) - if (!response.ok) throw new Error(`${phase} failed with ${response.status}: ${body}`) - return JSON.parse(body) - })) const counts = ["canonicalSeedFiles", "postCount", "pageCount", "userCount", "optionCount", "widgetOptionCount", "widgetStateOptionCount", "memoryBytes", "peakMemoryBytes"] if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== false || wordpress.evidence.privacyScheduleInitAttached !== false) { throw new Error(`Canonical WordPress probe did not boot the complete seed: ${JSON.stringify(wordpress)}`) @@ -194,20 +179,7 @@ async function assertCanonicalProbes() { if (setup.completed !== true || typeof setup.evidence?.wordpressVersion !== "string" || changedCounts.some((key) => !Number.isInteger(setup.evidence?.[key])) || setup.evidence.changedPathCount < 1) { throw new Error(`Canonical bootstrap setup probe did not produce ephemeral changes: ${JSON.stringify(setup)}`) } - for (const probe of lifecycle) { - const evidence = probe.evidence - const schedulingAttached = probe.phase === "canonical-current-user" - const isWpLoadedProbe = probe.phase.startsWith("canonical-wp-loaded-") || probe.phase.startsWith("canon-wpl-no-") - const lifecycleEvidenceIsValid = !isWpLoadedProbe && evidence?.wpCronInitAttached === schedulingAttached && evidence?.updateScheduleInitAttached === schedulingAttached && evidence?.privacyScheduleInitAttached === schedulingAttached - const wpLoadedEvidenceIsValid = isWpLoadedProbe && typeof evidence?.callbacks === "object" && evidence.callbacks !== null && !Array.isArray(evidence.callbacks) && (probe.phase === "canonical-wp-loaded-callbacks" || (evidence?.completed === true && Array.isArray(evidence?.removedCallbacks) && Number.isInteger(evidence?.memoryBeforeBytes))) - if (probe.completed !== true || !lifecyclePhases.includes(probe.phase) || evidence?.bootstrapPhase !== probe.phase || typeof evidence?.wordpressVersion !== "string" || !Number.isInteger(evidence?.memoryBytes) || !Number.isInteger(evidence?.peakMemoryBytes) || (!lifecycleEvidenceIsValid && !wpLoadedEvidenceIsValid)) { - throw new Error(`Canonical lifecycle probe did not stop at its bounded phase: ${JSON.stringify(probe)}`) - } - if (Object.hasOwn(expectedRemovedCallbacks, probe.phase) && JSON.stringify(evidence.removedCallbacks) !== JSON.stringify(expectedRemovedCallbacks[probe.phase])) { - throw new Error(`Canonical wp_loaded exclusion removed an unexpected callback set: ${JSON.stringify(probe)}`) - } - } - console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${lifecyclePhases.join(", ")} completed, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) + console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) } async function assertMdiDiagnostics() { diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 7f11b921..79290f02 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -337,60 +337,6 @@ test("Cloudflare MDI lifecycle diagnostics use the complete packaged seed withou assert.match(worker, /function canonicalChangedPathCounts/) }) -test("Cloudflare canonical lifecycle diagnostics compose the disabled-cron patch with unique bounded stops", async () => { - const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) - const lifecycle = worker.slice(worker.indexOf("function canonicalLifecycleProbeCode"), worker.indexOf("\nasync function bootWordPressRuntime")) - const aliases = { - cwr: "canonical-wp-loaded-exclude-rewrite-flush", - cwc: "canonical-wp-loaded-exclude-core-template-header", - cwp: "canonical-wp-loaded-exclude-playground", - cwcron: "canonical-wp-loaded-exclude-wp-cron", - cwu: "canonical-wp-loaded-suppress-rewrite-persist", - "canon-wpl-no-rewrite": "canonical-wp-loaded-exclude-rewrite-flush", - "canon-wpl-no-core": "canonical-wp-loaded-exclude-core-template-header", - "canon-wpl-no-playground": "canonical-wp-loaded-exclude-playground", - "canon-wpl-no-cron": "canonical-wp-loaded-exclude-wp-cron", - } - - for (const phase of ["canonical-current-user", "canonical-init", "canonical-site-status", "canonical-wp-loaded", "canonical-wp-loaded-callbacks", "canonical-wp-loaded-exclude-rewrite-flush", "canonical-wp-loaded-exclude-core-template-header", "canonical-wp-loaded-exclude-playground", "canonical-wp-loaded-exclude-wp-cron", "canonical-wp-loaded-exclude-all", "canonical-wp-loaded-suppress-rewrite-persist"]) { - assert.match(probes, new RegExp(`"${phase}"`)) - assert.match(lifecycle, new RegExp(`"${phase}"`)) - assert.deepEqual(routeWorkerRequest(new Request(`https://worker.example/?phase=${phase}`)), { kind: "probe", phase }) - } - assert.match(lifecycle, /pre_update_option_rewrite_rules/) - for (const [alias, phase] of Object.entries(aliases)) { - assert.ok(alias.length <= 24) - assert.match(worker, new RegExp(`"${alias}"`)) - assert.match(worker, new RegExp(`(?:"${alias}"|${alias}): "${phase}"`)) - assert.deepEqual(routeWorkerRequest(new Request(`https://worker.example/?phase=${alias}`)), { kind: "probe", phase: alias }) - } - assert.match(lifecycle, /const canonicalPhase = canonicalLifecyclePhaseAliases\[phase\] \?\? phase/) - assert.match(probes, /packagedCanonicalMarkdownSeed\(\), new Uint8Array\(markdownPrimaryBootstrapIndex\), "https:\/\/canonical-probe\.invalid", \{\}, bucket, true\)/) - assert.match(lifecycle, /substr_count\(\$settings, \$needle\) !== 1/) - assert.match(lifecycle, /WordPress canonical lifecycle probe needle was not uniquely found\./) - assert.match(lifecycle, /'completed' => true/) - assert.match(lifecycle, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) - assert.match(lifecycle, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) - assert.match(lifecycle, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/) - assert.match(lifecycle, /function wp_codebox_canonical_wp_loaded_callback_identifier/) - assert.match(lifecycle, /function wp_codebox_canonical_wp_loaded_inventory/) - assert.match(lifecycle, /\$remaining = 100/) - assert.match(lifecycle, /function wp_codebox_canonical_wp_loaded_remove_snapshot/) - assert.match(lifecycle, /remove_action\('wp_loaded', \$registered\['function'\], \(int\) \$priority\)/) - assert.match(lifecycle, /"WP_Rewrite::flush_rules", "playground_maybe_flush_rewrite_rules"/) - assert.match(lifecycle, /"_add_template_loader_filters", "_custom_header_background_just_in_time", "_custom_logo_header_styles"/) - assert.match(lifecycle, /"playground_maybe_flush_rewrite_rules", "playground_save_wp_env_info"/) - assert.match(lifecycle, /"_wp_cron"/) - assert.match(lifecycle, /canonical-wp-loaded-exclude-all": \{ identifiers: \[\], retain: true \}/) - assert.match(lifecycle, /do_action\('wp_loaded'\)/) - assert.doesNotMatch(lifecycle, /get_option|\$wpdb|\$_COOKIE|AUTH_KEY|password|token/i) - assert.equal((lifecycle.match(/do_action\( 'init' \);/g) ?? []).length, 1) - assert.equal((lifecycle.match(/do_action\( 'wp_loaded' \);/g) ?? []).length, 1) - assert.match(lifecycle, /stop\.after \? "\$needle/) - assert.match(lifecycle, /: "\$stop/) -}) - test("Cloudflare canonical runtime patches the unique init call with runtime persistence policies", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const patcher = worker.slice(worker.indexOf("function patchCanonicalRuntimePoliciesAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) From 0e854ef4e124689cd2598afae961cb87289c76f0 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 19:02:35 -0400 Subject: [PATCH 71/87] Revert "Probe complete canonical WordPress state" This reverts commit 1969510d103c7f8f591d848bb5524d3a9a97173f. --- packages/runtime-cloudflare/README.md | 8 +- packages/runtime-cloudflare/src/worker.ts | 110 +++++------------- .../runtime-core/src/runtime-php-snippets.ts | 11 +- .../src/bench-command-handlers.ts | 11 +- .../src/phpunit-command-handlers.ts | 20 ++-- scripts/cloudflare-local-gate.mjs | 35 ------ tests/cloudflare-runtime.test.ts | 99 +--------------- tests/runtime-php-snippets.test.ts | 10 -- 8 files changed, 56 insertions(+), 248 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 1b509863..e0ca5fe9 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -22,11 +22,7 @@ Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpres ## MDI Init Diagnostics -Every `mdi-*` lifecycle diagnostic materializes the validated packaged canonical MDI seed and the separately managed R2 WordPress runtime artifact in a disposable PHP-WASM runtime. They never acquire a coordinator lease, read or write a canonical R2 revision, or promote state. The old three-option fixture was intentionally removed because it does not represent complete MDI WordPress state. - -`?phase=canonical-wordpress` performs a full normal WordPress boot with that exact seed and artifact outside the coordinator, then returns WordPress version, memory, and non-sensitive table counts. Its widget-state count includes the `widget_*` options and `sidebars_widgets`. `?phase=canonical-bootstrap-setup` additionally runs the normal bootstrap setup with a probe-only internal password and fixed probe origin, flushes only the disposable runtime, and returns changed-path counts. Neither probe returns password, option, user, or content values. - -The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-wp-cron`, `mdi-init-exclude-privacy-schedule`, `mdi-init-exclude-update-schedule`, `mdi-init-exclude-wp-cron-privacy-schedule`, `mdi-init-exclude-wp-cron-update-schedule`, `mdi-init-exclude-privacy-update-schedule`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group through WordPress's public `remove_action()` API, runs `init` once, and returns the exact removed names, completion, and memory measurements before subsequent WordPress lifecycle work. +The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` reports only fixed widget option names, grouped by `get_option()` presence and direct `$wpdb` SQLite-row presence; it returns no option values. `mdi-widgets-get-settings` calls only `get_settings()` on each default factory object, with `mdi-widgets-get-settings-first` and `mdi-widgets-get-settings-second` as fixed halves; each reports completed classes and array-shape dimensions only. `mdi-widgets-register-one` preloads the fixed `widget_text` option, calls that object's `get_settings()`, then calls only `_register_one()` for its fixed instance. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. @@ -36,6 +32,6 @@ The fixed widget probes split `wp_widgets_init` without changing its normal path 2. Run `npm run provision:cloudflare-wordpress-runtime-corpus -- --local --persist-to ` to verify and upload the exact content-addressed artifact into isolated local R2 storage. Use `--remote` only for an explicit deployment operation. 3. Run `npm run test:cloudflare-runtime` for routing, canonical-state, artifact validation, source contract, and TypeScript coverage. 4. Run `npm run cloudflare:dry-run` to compile the Worker without creating Cloudflare resources. -5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions the artifact before workerd starts, then verifies the complete-seed canonical boot and ephemeral bootstrap probes, injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. +5. Run `npm run cloudflare:local-gate` for isolated local workerd evidence. It generates and provisions the artifact before workerd starts, then injects stable test-only admin-password and auth-secret values, verifies the login form, cookie-authenticated admin redirect, authenticated REST post publication, public rendering, representative frontend/admin/editor assets, PHP diagnostics, an existing authenticated cookie after cold restart, and a fresh login after restart. This document describes local candidate verification only. It does not claim remote deployment. diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index bc8f4ebe..6b645af9 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -606,7 +606,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise try { const wordpress = await materializeWordPressServerFiles(php, bucket) await materializeMarkdownDatabaseIntegration(php) - materializeRuntimeFiles(php, MARKDOWN_ROOT, await packagedCanonicalMarkdownSeed()) + materializeRuntimeFiles(php, MARKDOWN_ROOT, initialMarkdownFiles()) const evidence = (await php.run({ code: " file_exists('/wordpress/wp-content/db.php'), 'storage' => file_exists('/wordpress/wp-content/plugins/markdown-database-integration/inc/class-wp-markdown-storage.php'), 'siteurl' => file_exists('/wordpress/wp-content/markdown/_options/siteurl.json')]);", })).text.trim() @@ -617,7 +617,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } if (phase === "mdi-shortinit") { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: " $wp_version, 'markdownDropin' => defined('MARKDOWN_DB_DROPIN'), 'markdownMode' => defined('MARKDOWN_DB_MODE') ? MARKDOWN_DB_MODE : '']);", @@ -629,7 +629,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } if (phase === "mdi-wordpress" || phase === "mdi-option" || phase === "mdi-insert") { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const operation = phase === "mdi-option" ? "$updated = update_option('wp_codebox_mdi_probe', 1); $result = ['updated' => $updated];" @@ -645,8 +645,8 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-wp-cron", "mdi-init-exclude-privacy-schedule", "mdi-init-exclude-update-schedule", "mdi-init-exclude-wp-cron-privacy-schedule", "mdi-init-exclude-wp-cron-update-schedule", "mdi-init-exclude-privacy-update-schedule", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-first-pages", "mdi-widgets-direct-basic-classic-first-calendar", "mdi-widgets-direct-basic-classic-first-archives", "mdi-widgets-direct-basic-classic-first-meta", "mdi-widgets-direct-basic-classic-first-search", "mdi-widgets-direct-basic-classic-first-text", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-direct-factory-all", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, await packagedCanonicalMarkdownSeed(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) + if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { + const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() return probeResponse(phase, JSON.parse(evidence) as Record) @@ -655,40 +655,6 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (phase === "canonical-wordpress" || phase === "canonical-bootstrap-setup") { - const canonicalSeed = await packagedCanonicalMarkdownSeed() - const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, canonicalSeed, new Uint8Array(markdownPrimaryBootstrapIndex), "https://canonical-probe.invalid", {}, bucket, true) - try { - if (phase === "canonical-bootstrap-setup") { - const passwordFile = "/tmp/wp-codebox-canonical-bootstrap-probe-password" - runtime.php.writeFile(passwordFile, new TextEncoder().encode("probe-only-password")) - const output = (await runtime.php.run({ code: canonicalBootstrapSetupCode(passwordFile, "https://canonical-probe.invalid") })).text.trim() - if (output !== "flushed") throw new Error("Canonical bootstrap setup probe did not confirm its ephemeral flush.") - const changes = canonicalChangedPathCounts(canonicalSeed, collectRuntimeFiles(runtime.php, MARKDOWN_ROOT)) - return probeResponse(phase, { wordpressVersion: runtime.wordpressVersion, canonicalSeedFiles: canonicalSeed.length, ...changes }) - } - const evidence = (await runtime.php.run({ code: ` get_bloginfo('version'), - 'postCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->posts}"), - 'pageCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->posts} WHERE post_type = 'page'"), - 'userCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->users}"), - 'optionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options}"), - 'widgetOptionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options} WHERE option_name LIKE 'widget\\_%'"), - 'widgetStateOptionCount' => (int) $wpdb->get_var("SELECT COUNT(*) FROM {\$wpdb->options} WHERE option_name LIKE 'widget\\_%' OR option_name = 'sidebars_widgets'"), - 'wpCronInitAttached' => false !== has_action('init', 'wp_cron'), - 'updateScheduleInitAttached' => false !== has_action('init', 'wp_schedule_update_checks'), - 'privacyScheduleInitAttached' => false !== has_action('init', 'wp_schedule_delete_old_privacy_export_files'), - 'memoryBytes' => memory_get_usage(true), - 'peakMemoryBytes' => memory_get_peak_usage(true), -]);` })).text.trim() - return probeResponse(phase, { canonicalSeedFiles: canonicalSeed.length, ...JSON.parse(evidence) as Record }) - } finally { - runtime.php.exit() - } - } - if (phase?.startsWith("seeded-")) { const runtime = await bootWordPressRuntime( "do-not-attempt-installing", @@ -751,7 +717,7 @@ function wordpressProbeCode(phase: string): string { return " get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);" } - const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-first-pages", "mdi-widgets-direct-basic-classic-first-calendar", "mdi-widgets-direct-basic-classic-first-archives", "mdi-widgets-direct-basic-classic-first-meta", "mdi-widgets-direct-basic-classic-first-search", "mdi-widgets-direct-basic-classic-first-text", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-direct-factory-all", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"] + const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"] if (widgetPhases.includes(phase)) { return `callbacks) || !is_array($hook->callbacks)) return $removed; - $callbacks_by_priority = $hook->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { - foreach ($callbacks as $registered) { - if (!isset($registered['function'])) continue; + foreach ($hook->callbacks as $priority => $callbacks) { + foreach ($callbacks as $index => $registered) { $identifier = wp_codebox_widgets_probe_callback_identifier($registered['function']); if (in_array($identifier, $identifiers, true) !== $retain) { - if (remove_action($hook_name, $registered['function'], (int) $priority)) $removed[] = $identifier; + unset($hook->callbacks[$priority][$index]); + $removed[] = $identifier; } } + if (empty($hook->callbacks[$priority])) unset($hook->callbacks[$priority]); } sort($removed, SORT_STRING); return $removed; @@ -850,22 +816,15 @@ function wp_codebox_widgets_probe_get_settings($class_names) { return array('completedClassNames' => $completed, 'settingsShapes' => $settings_shapes); } $direct_groups = array( - 'mdi-widgets-direct-basic-classic-first' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'), - 'mdi-widgets-direct-basic-classic-first-pages' => array('WP_Widget_Pages'), - 'mdi-widgets-direct-basic-classic-first-calendar' => array('WP_Widget_Calendar'), - 'mdi-widgets-direct-basic-classic-first-archives' => array('WP_Widget_Archives'), - 'mdi-widgets-direct-basic-classic-first-meta' => array('WP_Widget_Meta'), - 'mdi-widgets-direct-basic-classic-first-search' => array('WP_Widget_Search'), - 'mdi-widgets-direct-basic-classic-first-text' => array('WP_Widget_Text'), + 'mdi-widgets-direct-basic-classic-first' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'), 'mdi-widgets-direct-basic-classic-second' => array('WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget'), 'mdi-widgets-direct-media' => array('WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video'), 'mdi-widgets-direct-custom-html-block' => array('WP_Widget_Custom_HTML', 'WP_Widget_Block'), 'mdi-widgets-direct-block' => array('WP_Widget_Block'), 'mdi-widgets-direct-custom-html' => array('WP_Widget_Custom_HTML'), - 'mdi-widgets-direct-factory-all' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block'), ); $widget_option_id_bases = array('archives', 'block', 'calendar', 'categories', 'custom_html', 'links', 'media_audio', 'media_gallery', 'media_image', 'media_video', 'meta', 'nav_menu', 'pages', 'recent-comments', 'recent-posts', 'rss', 'search', 'tag_cloud', 'text'); -$widget_classes = array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block'); +$widget_classes = array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block'); $get_settings_groups = array( 'mdi-widgets-get-settings' => $widget_classes, 'mdi-widgets-get-settings-first' => array_slice($widget_classes, 0, 10), @@ -943,12 +902,6 @@ require '/wordpress/wp-load.php';` "wp_schedule_site_health_cron", "WP_Site_Health::maybe_create_scheduled_event", ], - "mdi-init-exclude-wp-cron": ["wp_cron"], - "mdi-init-exclude-privacy-schedule": ["wp_schedule_delete_old_privacy_export_files"], - "mdi-init-exclude-update-schedule": ["wp_schedule_update_checks"], - "mdi-init-exclude-wp-cron-privacy-schedule": ["wp_cron", "wp_schedule_delete_old_privacy_export_files"], - "mdi-init-exclude-wp-cron-update-schedule": ["wp_cron", "wp_schedule_update_checks"], - "mdi-init-exclude-privacy-update-schedule": ["wp_schedule_delete_old_privacy_export_files", "wp_schedule_update_checks"], "mdi-init-exclude-block-registration": [ "register_block_core_*", "register_core_block_*", @@ -1029,15 +982,15 @@ if ($phase === 'mdi-init-callbacks') { $removed = array(); $hook = isset($GLOBALS['wp_filter']['init']) ? $GLOBALS['wp_filter']['init'] : null; if ($hook && isset($hook->callbacks) && is_array($hook->callbacks)) { - $callbacks_by_priority = $hook->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { - foreach ($callbacks as $registered) { - if (!isset($registered['function'])) continue; + foreach ($hook->callbacks as $priority => $callbacks) { + foreach ($callbacks as $index => $registered) { $identifier = wp_codebox_init_probe_callback_identifier($registered['function']); if (wp_codebox_init_probe_matches($identifier, $excluded)) { - if (remove_action('init', $registered['function'], (int) $priority)) $removed[] = $identifier; + unset($hook->callbacks[$priority][$index]); + $removed[] = $identifier; } } + if (empty($hook->callbacks[$priority])) unset($hook->callbacks[$priority]); } } sort($removed, SORT_STRING); @@ -1148,6 +1101,18 @@ async function bootWordPressRuntime( return { php, requestHandler, wordpressVersion } } +function initialMarkdownFiles(): RuntimeFile[] { + const options = [ + { option_id: 1, option_name: "siteurl", option_value: SITE_URL, autoload: "on" }, + { option_id: 2, option_name: "home", option_value: SITE_URL, autoload: "on" }, + { option_id: 3, option_name: "blogname", option_value: "WP Codebox Cloudflare Runtime", autoload: "on" }, + ] + return options.map((option) => ({ + path: `_options/${option.option_name}.json`, + bytes: new TextEncoder().encode(JSON.stringify(option, null, 2)), + })) +} + function materializeRuntimeFiles(php: PHP, root: string, files: RuntimeFile[]): void { for (const file of files) { const destination = `${root}/${file.path}` @@ -1216,21 +1181,6 @@ function collectRuntimeFiles(php: PHP, root: string, paths?: string[]): RuntimeF return files.sort((left, right) => left.path.localeCompare(right.path)) } -function canonicalChangedPathCounts(before: RuntimeFile[], after: RuntimeFile[]): { createdPathCount: number; changedPathCount: number; deletedPathCount: number } { - const beforeByPath = new Map(before.map((file) => [file.path, file.bytes])) - const afterByPath = new Map(after.map((file) => [file.path, file.bytes])) - let createdPathCount = 0 - let changedPathCount = 0 - let deletedPathCount = 0 - for (const [path, bytes] of afterByPath) { - const previous = beforeByPath.get(path) - if (!previous) createdPathCount++ - else if (previous.byteLength !== bytes.byteLength || previous.some((byte, index) => byte !== bytes[index])) changedPathCount++ - } - for (const path of beforeByPath.keys()) if (!afterByPath.has(path)) deletedPathCount++ - return { createdPathCount, changedPathCount, deletedPathCount } -} - async function sha256Hex(bytes: Uint8Array): Promise { const digest = await crypto.subtle.digest("SHA-256", Uint8Array.from(bytes).buffer) return Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("") diff --git a/packages/runtime-core/src/runtime-php-snippets.ts b/packages/runtime-core/src/runtime-php-snippets.ts index dc26ea3d..979e677a 100644 --- a/packages/runtime-core/src/runtime-php-snippets.ts +++ b/packages/runtime-core/src/runtime-php-snippets.ts @@ -28,15 +28,16 @@ function ${defer}(string $hook_name, array $before): array { if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; } - if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - } + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); + } + if (empty($wp_filter[$hook_name]->callbacks[$priority])) { + unset($wp_filter[$hook_name]->callbacks[$priority]); } } usort($deferred, static function (array $left, array $right): int { return ($left['priority'] ?? 10) <=> ($right['priority'] ?? 10); }); diff --git a/packages/runtime-playground/src/bench-command-handlers.ts b/packages/runtime-playground/src/bench-command-handlers.ts index b2085c81..db1efa5f 100644 --- a/packages/runtime-playground/src/bench-command-handlers.ts +++ b/packages/runtime-playground/src/bench-command-handlers.ts @@ -1195,15 +1195,16 @@ function wp_codebox_bench_defer_new_wordpress_hook_callbacks(string $hook_name, if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; } - if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - } + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); + } + if (empty($wp_filter[$hook_name]->callbacks[$priority])) { + unset($wp_filter[$hook_name]->callbacks[$priority]); } } usort($deferred, static function (array $left, array $right): int { diff --git a/packages/runtime-playground/src/phpunit-command-handlers.ts b/packages/runtime-playground/src/phpunit-command-handlers.ts index 7b28581a..f53aafcf 100644 --- a/packages/runtime-playground/src/phpunit-command-handlers.ts +++ b/packages/runtime-playground/src/phpunit-command-handlers.ts @@ -510,14 +510,15 @@ function pg_remove_new_wordpress_hook_callbacks(string $hook_name, array $before if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return; } - $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { if (!isset($before[$priority . ':' . $callback_id])) { - $callback = $callbacks[$callback_id]; - if (isset($callback['function'])) remove_action($hook_name, $callback['function'], (int) $priority); + unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); } } + if (empty($wp_filter[$hook_name]->callbacks[$priority])) { + unset($wp_filter[$hook_name]->callbacks[$priority]); + } } } @@ -527,15 +528,16 @@ function pg_defer_new_wordpress_hook_callbacks(string $hook_name, array $before) if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; } - if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - } + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); + } + if (empty($wp_filter[$hook_name]->callbacks[$priority])) { + unset($wp_filter[$hook_name]->callbacks[$priority]); } } usort($deferred, static function (array $left, array $right): int { return $left['priority'] <=> $right['priority']; }); diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 75fd8207..3d8239c7 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -16,8 +16,6 @@ try { await run("npm", ["run", "generate:cloudflare-wordpress-runtime-corpus"]) await run("npm", ["run", "provision:cloudflare-wordpress-runtime-corpus", "--", "--local", "--persist-to", stateDirectory]) await startWorker() - await assertCanonicalProbes() - await assertMdiDiagnostics() await assertConcurrentMutations() const adminHtml = await login() const editorHtml = await assertPostNewEditor() @@ -168,39 +166,6 @@ async function assertHealthResponse() { if (response.status !== 200 || body.schema !== "wp-codebox/cloudflare-runtime-health/v1" || body.marker !== "wp-codebox-cloudflare-runtime-health" || body.phpVersion !== "8.5.8" || typeof body.wordpressVersion !== "string" || body.execution?.status !== "ok") throw new Error(`Unexpected Cloudflare runtime health envelope: ${JSON.stringify(body)}`) } -async function assertCanonicalProbes() { - const wordpress = await (await request(`${origin}/?phase=canonical-wordpress`)).json() - const setup = await (await request(`${origin}/?phase=canonical-bootstrap-setup`)).json() - const counts = ["canonicalSeedFiles", "postCount", "pageCount", "userCount", "optionCount", "widgetOptionCount", "widgetStateOptionCount", "memoryBytes", "peakMemoryBytes"] - if (wordpress.completed !== true || typeof wordpress.evidence?.wordpressVersion !== "string" || counts.some((key) => !Number.isInteger(wordpress.evidence?.[key])) || wordpress.evidence.widgetStateOptionCount !== 19 || wordpress.evidence.wpCronInitAttached !== false || wordpress.evidence.updateScheduleInitAttached !== false || wordpress.evidence.privacyScheduleInitAttached !== false) { - throw new Error(`Canonical WordPress probe did not boot the complete seed: ${JSON.stringify(wordpress)}`) - } - const changedCounts = ["createdPathCount", "changedPathCount", "deletedPathCount"] - if (setup.completed !== true || typeof setup.evidence?.wordpressVersion !== "string" || changedCounts.some((key) => !Number.isInteger(setup.evidence?.[key])) || setup.evidence.changedPathCount < 1) { - throw new Error(`Canonical bootstrap setup probe did not produce ephemeral changes: ${JSON.stringify(setup)}`) - } - console.log(`Canonical probes: WordPress ${wordpress.evidence.wordpressVersion}, disabled-cron scheduling callbacks absent at init, ${wordpress.evidence.widgetOptionCount} widget_* options plus sidebars_widgets, ${setup.evidence.changedPathCount} changed ephemeral paths.`) -} - -async function assertMdiDiagnostics() { - const phases = ["mdi-init-exclude-widgets", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first"] - const diagnostics = await Promise.all(phases.map(async (phase) => { - const response = await request(`${origin}/?phase=${phase}`) - const body = await response.text() - assertNoPhpDiagnostics(body, phase) - if (!response.ok) throw new Error(`${phase} failed with ${response.status}: ${body}`) - const probe = JSON.parse(body) - if (probe.completed !== true || probe.evidence?.bootstrapPhase !== phase) throw new Error(`${phase} did not complete: ${body}`) - return probe.evidence - })) - const [initExclusion, factory, remainingHooks, directRegistration] = diagnostics - if (!Array.isArray(initExclusion.removedCallbacks) || !initExclusion.removedCallbacks.includes("wp_widgets_init")) throw new Error(`mdi-init-exclude-widgets did not remove wp_widgets_init: ${JSON.stringify(initExclusion)}`) - if (!Array.isArray(factory.removedInitCallbacks) || !Array.isArray(factory.removedWidgetsCallbacks) || !Number.isInteger(factory.widgetClassCount)) throw new Error(`mdi-widgets-factory did not return fixed removal evidence: ${JSON.stringify(factory)}`) - if (!Array.isArray(remainingHooks.removedWidgetsCallbacks) || !Number.isInteger(remainingHooks.widgetClassCount)) throw new Error(`mdi-widgets-remaining-hooks did not return fixed removal evidence: ${JSON.stringify(remainingHooks)}`) - if (!Array.isArray(directRegistration.classNamesAttempted) || directRegistration.classCount !== directRegistration.classNamesAttempted.length) throw new Error(`mdi-widgets-direct-basic-classic-first did not return its fixed class group: ${JSON.stringify(directRegistration)}`) - console.log(`MDI diagnostics: init exclusion removed ${initExclusion.removedCallbacks.length} callback(s); widget factory ${factory.widgetClassCount} classes; remaining hooks ${remainingHooks.widgetClassCount} classes; direct registration ${directRegistration.classCount} classes.`) -} - async function assertWordPressPage(target, label) { const response = await request(target) const body = await response.text() diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 79290f02..33ff2fa6 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -181,12 +181,6 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi for (const phase of [ "mdi-init-callbacks", "mdi-init-exclude-scheduling", - "mdi-init-exclude-wp-cron", - "mdi-init-exclude-privacy-schedule", - "mdi-init-exclude-update-schedule", - "mdi-init-exclude-wp-cron-privacy-schedule", - "mdi-init-exclude-wp-cron-update-schedule", - "mdi-init-exclude-privacy-update-schedule", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", @@ -215,12 +209,6 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /sort\(\$identifiers, SORT_STRING\)/) assert.match(worker, /ksort\(\$inventory, SORT_NUMERIC\)/) assert.match(worker, /"wp_schedule_update_checks"/) - assert.match(worker, /"mdi-init-exclude-wp-cron": \["wp_cron"\]/) - assert.match(worker, /"mdi-init-exclude-privacy-schedule": \["wp_schedule_delete_old_privacy_export_files"\]/) - assert.match(worker, /"mdi-init-exclude-update-schedule": \["wp_schedule_update_checks"\]/) - assert.match(worker, /"mdi-init-exclude-wp-cron-privacy-schedule": \["wp_cron", "wp_schedule_delete_old_privacy_export_files"\]/) - assert.match(worker, /"mdi-init-exclude-wp-cron-update-schedule": \["wp_cron", "wp_schedule_update_checks"\]/) - assert.match(worker, /"mdi-init-exclude-privacy-update-schedule": \["wp_schedule_delete_old_privacy_export_files", "wp_schedule_update_checks"\]/) assert.match(worker, /"WP_Site_Health::maybe_create_scheduled_event"/) assert.match(worker, /"register_block_core_\*"/) assert.match(worker, /"WP_Block_Supports::init"/) @@ -232,18 +220,11 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /wp_codebox_widgets_probe_inventory\('widgets_init'\)/) assert.match(worker, /function wp_codebox_widgets_probe_register_selected\(\$class_names\)/) assert.match(worker, /\$registered\[\$class_name\]\->_register\(\)/) - assert.match(worker, /'mdi-widgets-direct-basic-classic-first' => array\('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'\)/) - assert.doesNotMatch(worker, /'mdi-widgets-direct-basic-classic-first' => array\([^)]*WP_Widget_Links/) - for (const className of ["Pages", "Calendar", "Archives", "Meta", "Search", "Text"]) { - assert.match(worker, new RegExp(`'mdi-widgets-direct-basic-classic-first-${className.toLowerCase()}' => array\\('WP_Widget_${className}'\\)`)) - } + assert.match(worker, /'mdi-widgets-direct-basic-classic-first' => array\('WP_Widget_Pages'/) assert.match(worker, /'mdi-widgets-direct-media' => array\('WP_Widget_Media_Audio'/) assert.match(worker, /'mdi-widgets-direct-custom-html-block' => array\('WP_Widget_Custom_HTML', 'WP_Widget_Block'\)/) assert.match(worker, /'mdi-widgets-direct-block' => array\('WP_Widget_Block'\)/) assert.match(worker, /'mdi-widgets-direct-custom-html' => array\('WP_Widget_Custom_HTML'\)/) - assert.match(worker, /'mdi-widgets-direct-factory-all' => array\('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Media_Audio'/) - assert.doesNotMatch(worker, /'mdi-widgets-direct-factory-all' => array\([^)]*WP_Widget_Links/) - assert.doesNotMatch(worker, /'mdi-widgets-direct-factory-all' => array\([^)]*_wp_block_theme_register_classic_sidebars/) assert.match(worker, /if \(get_option\(\$option_name, false\) === false\) \$get_option_missing\[\] = \$option_name/) assert.match(worker, /\$wpdb->get_var\(\$wpdb->prepare\("SELECT 1 FROM \{\$wpdb->options\} WHERE option_name = %s LIMIT 1", \$option_name\)\)/) assert.match(worker, /'getOption' => array\('found' => \$get_option_found, 'missing' => \$get_option_missing\)/) @@ -261,82 +242,9 @@ test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusi assert.match(worker, /"create_initial_taxonomies"/) assert.match(worker, /"wp_create_initial_post_meta"/) assert.match(worker, /do_action\('init'\);/) - assert.match(worker, /'completed' => true, 'removedCallbacks' => \$removed, 'memoryBeforeBytes' => \$memory_before, 'memoryBytes' => memory_get_usage\(true\), 'peakMemoryBytes' => memory_get_peak_usage\(true\)/) assert.doesNotMatch(worker, /searchParams\.get\([^)]*callback|searchParams\.get\([^)]*exclude|searchParams\.get\([^)]*widget/) }) -test("Cloudflare widget diagnostics preserve later hook priorities after callback removal", async () => { - const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const helper = worker.match(/function wp_codebox_widgets_probe_callback_identifier\(\$callback\) \{[\s\S]*?(?=function wp_codebox_widgets_probe_register_defaults)/)?.[0] - assert.ok(helper, "The widget probe callback helper is present in the worker PHP probe.") - assert.match(helper, /\$callbacks_by_priority = \$hook->callbacks/) - assert.match(helper, /remove_action\(\$hook_name, \$registered\['function'\], \(int\) \$priority\)/) - assert.doesNotMatch(helper, /unset\(\$hook->callbacks/) - - const php = `callbacks[$priority][] = array('function' => $callback, 'accepted_args' => $accepted_args); - $this->priorities = array_keys($this->callbacks); - sort($this->priorities, SORT_NUMERIC); - } - public function remove_filter($callback, $priority) { - if (!isset($this->callbacks[$priority])) return false; - foreach ($this->callbacks[$priority] as $index => $registered) { - if ($registered['function'] === $callback) { - unset($this->callbacks[$priority][$index]); - if (empty($this->callbacks[$priority])) unset($this->callbacks[$priority]); - $this->priorities = array_keys($this->callbacks); - sort($this->priorities, SORT_NUMERIC); - return true; - } - } - return false; - } - public function do_action() { - foreach ($this->priorities as $priority) { - foreach ($this->callbacks[$priority] as $registered) call_user_func($registered['function']); - } - } -} -$GLOBALS['wp_filter'] = array('widgets_init' => new WP_Hook()); -function add_action($hook_name, $callback, $priority = 10, $accepted_args = 1) { $GLOBALS['wp_filter'][$hook_name]->add_filter($callback, $priority, $accepted_args); } -function remove_action($hook_name, $callback, $priority = 10) { return $GLOBALS['wp_filter'][$hook_name]->remove_filter($callback, $priority); } -function do_action($hook_name) { $GLOBALS['wp_filter'][$hook_name]->do_action(); } -${helper} -function wp_codebox_probe_removed_callback() { $GLOBALS['wp_codebox_probe_calls'][] = 'removed'; } -function wp_codebox_probe_later_priority_sentinel() { $GLOBALS['wp_codebox_probe_calls'][] = 'later-priority-sentinel'; } -$GLOBALS['wp_codebox_probe_calls'] = array(); -add_action('widgets_init', 'wp_codebox_probe_removed_callback', 10); -add_action('widgets_init', 'wp_codebox_probe_later_priority_sentinel', 20); -$removed_callbacks = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array('wp_codebox_probe_removed_callback'), false); -do_action('widgets_init'); -echo json_encode(array('removed' => $removed_callbacks, 'calls' => $GLOBALS['wp_codebox_probe_calls']));` - const { stdout } = await execFileAsync("php", ["-r", php.slice(" { - const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - const generator = await readFile(new URL("../scripts/build-cloudflare-canonical-mdi-seed.php", import.meta.url), "utf8") - const canonicalManifest = JSON.parse(await readFile(new URL("../packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json", import.meta.url), "utf8")) as { files: Array<{ path: string }> } - const probes = worker.slice(worker.indexOf("async function runBootProbe"), worker.indexOf("if (phase?.startsWith(\"seeded-\"))")) - - assert.equal((probes.match(/initialMarkdownFiles/g) ?? []).length, 0) - assert.ok((probes.match(/await packagedCanonicalMarkdownSeed\(\)/g) ?? []).length >= 4) - assert.match(probes, /phase === "canonical-wordpress" \|\| phase === "canonical-bootstrap-setup"/) - assert.match(probes, /materializeWordPressServerFiles/) - assert.match(probes, /canonicalBootstrapSetupCode\(passwordFile, "https:\/\/canonical-probe\.invalid"\)/) - assert.match(generator, /serialize\( array\( 'custom_css_post_id' => -1 \) \)/) - assert.ok(canonicalManifest.files.some((file) => file.path === "_options/theme_mods_twentytwentyfive.json")) - assert.match(probes, /canonicalChangedPathCounts\(canonicalSeed, collectRuntimeFiles/) - assert.doesNotMatch(probes, /persistMarkdownRevision|commitLease|coordinatorCall/) - assert.match(worker, /'widgetOptionCount' => \(int\) \$wpdb->get_var/) - assert.match(worker, /function canonicalChangedPathCounts/) -}) - test("Cloudflare canonical runtime patches the unique init call with runtime persistence policies", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const patcher = worker.slice(worker.indexOf("function patchCanonicalRuntimePoliciesAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) @@ -362,15 +270,10 @@ test("Cloudflare canonical runtime patches the unique init call with runtime per assert.match(worker, /runtimeBucket\?: R2Bucket,\n shouldPatchCanonicalRuntimePoliciesAtInit = false,/) assert.match(worker, /authConstants, bucket, true\), pointer/) assert.match(worker, /env\.WORDPRESS_STATE_BUCKET, true\)/) - assert.match(worker, /canonical-probe\.invalid", \{\}, bucket, true\)/) assert.match(worker, /canonicalBootstrapPasswordCode/) assert.match(worker, /canonicalBootstrapUrlCode/) assert.match(worker, /canonicalBootstrapFlushCode/) - assert.match(worker, /'wpCronInitAttached' => false !== has_action\('init', 'wp_cron'\)/) - assert.match(worker, /'updateScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_update_checks'\)/) - assert.match(worker, /'privacyScheduleInitAttached' => false !== has_action\('init', 'wp_schedule_delete_old_privacy_export_files'\)/) }) - test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object only for leases", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const coordinator = await readFile(new URL("../packages/runtime-cloudflare/src/state-coordinator.ts", import.meta.url), "utf8") diff --git a/tests/runtime-php-snippets.test.ts b/tests/runtime-php-snippets.test.ts index a73633ed..9481c060 100644 --- a/tests/runtime-php-snippets.test.ts +++ b/tests/runtime-php-snippets.test.ts @@ -30,16 +30,6 @@ $GLOBALS['calls'] = array(); function did_action($hook_name) { return (int) ($GLOBALS['wp_actions'][$hook_name] ?? 0); } function wp_get_abilities() { return array('before' => true, 'after' => true); } -function remove_action($hook_name, $callback, $priority = 10) { - foreach ($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority] ?? array() as $callback_id => $registered) { - if (($registered['function'] ?? null) === $callback) { - unset($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority][$callback_id]); - if (empty($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority])) unset($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority]); - return true; - } - } - return false; -} $GLOBALS['wp_filter']['init']->callbacks[10]['existing'] = array('function' => static function () { $GLOBALS['calls'][] = 'existing'; }, 'accepted_args' => 0); $state = contained_runtime_test_component_lifecycle_replay_prepare(); From 9b669927e4ea2f0a149a62a3c80d2480bd0cbf6a Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 19:03:37 -0400 Subject: [PATCH 72/87] Revert "Add bounded MDI init diagnostics" This reverts commit bea4645f66ecde2090c869d055a00d096f3f6392. --- packages/runtime-cloudflare/README.md | 7 - packages/runtime-cloudflare/src/worker.ts | 288 +--------------------- tests/cloudflare-runtime.test.ts | 70 ------ 3 files changed, 1 insertion(+), 364 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index e0ca5fe9..09ec3129 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -19,13 +19,6 @@ The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache. Canonical Cloudflare boots patch only the assembled PHP MEMFS copy of `/wordpress/wp-settings.php` after the R2 WordPress corpus is materialized and before WordPress executes. The disabled-cron scheduling policy requires exactly one canonical `do_action( 'init' );` needle and fails closed otherwise. When `DISABLE_WP_CRON` is true, it removes only core's `wp_cron`, `wp_schedule_delete_old_privacy_export_files`, and `wp_schedule_update_checks` callbacks through public `remove_action()` before executing the original `init` call once. This prevents browser requests from recreating cron work that cannot execute in the PHP-WASM/MDI shutdown path. Durable cron execution belongs to a separate explicit queue or scheduled-Worker contract. - -## MDI Init Diagnostics - -The diagnostic-only `?phase=` probes `mdi-init-callbacks`, `mdi-init-exclude-scheduling`, `mdi-init-exclude-block-registration`, `mdi-init-exclude-theme-patterns-styles`, `mdi-init-exclude-widgets`, `mdi-init-exclude-rest-connectors-sitemaps`, and `mdi-init-exclude-initial-content-types` stop at `init`. The inventory probe returns sorted callback identifiers by priority. Each exclusion probe removes only its fixed callback group, runs `init` once, and returns before subsequent WordPress lifecycle work. - -The fixed widget probes split `wp_widgets_init` without changing its normal path: `mdi-widgets-callbacks` returns the sorted nested `widgets_init` inventory before `init`; `mdi-widgets-constructors` runs only the core default widget registrations and reports widget class count; `mdi-widgets-hooks` adds the nested action; `mdi-widgets-factory` runs only `WP_Widget_Factory::_register_widgets`; and `mdi-widgets-remaining-hooks` runs the remaining nested callbacks. The bounded direct-registration phases are `mdi-widgets-direct-basic-classic-first`, `mdi-widgets-direct-basic-classic-second`, `mdi-widgets-direct-media`, `mdi-widgets-direct-custom-html-block`, `mdi-widgets-direct-block`, and `mdi-widgets-direct-custom-html`. They remove every normal nested callback, then call `_register()` only on their fixed allowlisted factory objects and return attempted classes, count, completion, and memory. `mdi-widgets-option-reads` reports only fixed widget option names, grouped by `get_option()` presence and direct `$wpdb` SQLite-row presence; it returns no option values. `mdi-widgets-get-settings` calls only `get_settings()` on each default factory object, with `mdi-widgets-get-settings-first` and `mdi-widgets-get-settings-second` as fixed halves; each reports completed classes and array-shape dimensions only. `mdi-widgets-register-one` preloads the fixed `widget_text` option, calls that object's `get_settings()`, then calls only `_register_one()` for its fixed instance. Every phase uses fixed source identities and returns before later lifecycle work; none accepts callback names, removal rules, widget classes, or option names from request input. - ## Verification 1. Run `npm run generate:cloudflare-canonical-mdi-seed` and `npm run generate:cloudflare-wordpress-runtime-corpus` to regenerate deterministic runtime artifacts and manifests. diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 6b645af9..560afd8e 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -645,7 +645,7 @@ async function runBootProbe(phase: string, bucket: R2Bucket): Promise } } - if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded", "mdi-init-callbacks", "mdi-init-exclude-scheduling", "mdi-init-exclude-block-registration", "mdi-init-exclude-theme-patterns-styles", "mdi-init-exclude-widgets", "mdi-init-exclude-rest-connectors-sitemaps", "mdi-init-exclude-initial-content-types", "mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"].includes(phase)) { + if (["mdi-includes", "mdi-embed", "mdi-textdomain", "mdi-ai-client", "mdi-plugin-constants", "mdi-muplugins", "mdi-plugins", "mdi-globals", "mdi-theme", "mdi-site-health-class", "mdi-site-health", "mdi-current-user", "mdi-init", "mdi-wp-loaded"].includes(phase)) { const runtime = await bootWordPressRuntime("do-not-attempt-installing", true, true, undefined, initialMarkdownFiles(), new Uint8Array(markdownPrimaryBootstrapIndex), SITE_URL, {}, bucket) try { const evidence = (await runtime.php.run({ code: wordpressProbeCode(phase) })).text.trim() @@ -717,292 +717,6 @@ function wordpressProbeCode(phase: string): string { return " get_option('siteurl'), 'wordpressVersion' => get_bloginfo('version')]);" } - const widgetPhases = ["mdi-widgets-callbacks", "mdi-widgets-constructors", "mdi-widgets-hooks", "mdi-widgets-factory", "mdi-widgets-remaining-hooks", "mdi-widgets-direct-basic-classic-first", "mdi-widgets-direct-basic-classic-second", "mdi-widgets-direct-media", "mdi-widgets-direct-custom-html-block", "mdi-widgets-direct-block", "mdi-widgets-direct-custom-html", "mdi-widgets-option-reads", "mdi-widgets-get-settings", "mdi-widgets-get-settings-first", "mdi-widgets-get-settings-second", "mdi-widgets-register-one"] - if (widgetPhases.includes(phase)) { - return `callbacks) || !is_array($hook->callbacks)) return $inventory; - foreach ($hook->callbacks as $priority => $callbacks) { - $identifiers = array(); - foreach ($callbacks as $registered) $identifiers[] = wp_codebox_widgets_probe_callback_identifier($registered['function']); - sort($identifiers, SORT_STRING); - $inventory[(string) $priority] = $identifiers; - } - ksort($inventory, SORT_NUMERIC); - return $inventory; -} -function wp_codebox_widgets_probe_remove_callbacks($hook_name, $identifiers, $retain) { - $removed = array(); - $hook = isset($GLOBALS['wp_filter'][$hook_name]) ? $GLOBALS['wp_filter'][$hook_name] : null; - if (!$hook || !isset($hook->callbacks) || !is_array($hook->callbacks)) return $removed; - foreach ($hook->callbacks as $priority => $callbacks) { - foreach ($callbacks as $index => $registered) { - $identifier = wp_codebox_widgets_probe_callback_identifier($registered['function']); - if (in_array($identifier, $identifiers, true) !== $retain) { - unset($hook->callbacks[$priority][$index]); - $removed[] = $identifier; - } - } - if (empty($hook->callbacks[$priority])) unset($hook->callbacks[$priority]); - } - sort($removed, SORT_STRING); - return $removed; -} -function wp_codebox_widgets_probe_register_defaults() { - if (!is_blog_installed()) return; - register_widget('WP_Widget_Pages'); - register_widget('WP_Widget_Calendar'); - register_widget('WP_Widget_Archives'); - if (get_option('link_manager_enabled')) register_widget('WP_Widget_Links'); - register_widget('WP_Widget_Media_Audio'); - register_widget('WP_Widget_Media_Image'); - register_widget('WP_Widget_Media_Gallery'); - register_widget('WP_Widget_Media_Video'); - register_widget('WP_Widget_Meta'); - register_widget('WP_Widget_Search'); - register_widget('WP_Widget_Text'); - register_widget('WP_Widget_Categories'); - register_widget('WP_Widget_Recent_Posts'); - register_widget('WP_Widget_Recent_Comments'); - register_widget('WP_Widget_RSS'); - register_widget('WP_Widget_Tag_Cloud'); - register_widget('WP_Nav_Menu_Widget'); - register_widget('WP_Widget_Custom_HTML'); - register_widget('WP_Widget_Block'); -} -function wp_codebox_widgets_probe_register_selected($class_names) { - $registered = isset($GLOBALS['wp_widget_factory']->widgets) && is_array($GLOBALS['wp_widget_factory']->widgets) ? $GLOBALS['wp_widget_factory']->widgets : array(); - foreach ($class_names as $class_name) { - if (!isset($registered[$class_name])) throw new Exception('Widget probe class was not registered: ' . $class_name); - $registered[$class_name]->_register(); - } -} -function wp_codebox_widgets_probe_array_dimensions($value) { - if (!is_array($value)) return 0; - $dimensions = 1; - foreach ($value as $item) $dimensions = max($dimensions, 1 + wp_codebox_widgets_probe_array_dimensions($item)); - return $dimensions; -} -function wp_codebox_widgets_probe_get_settings($class_names) { - $registered = isset($GLOBALS['wp_widget_factory']->widgets) && is_array($GLOBALS['wp_widget_factory']->widgets) ? $GLOBALS['wp_widget_factory']->widgets : array(); - $completed = array(); - $settings_shapes = array(); - foreach ($class_names as $class_name) { - if (!isset($registered[$class_name])) throw new Exception('Widget probe class was not registered: ' . $class_name); - $settings = $registered[$class_name]->get_settings(); - $completed[] = $class_name; - $settings_shapes[$class_name] = array('arrayLike' => is_array($settings), 'dimensions' => wp_codebox_widgets_probe_array_dimensions($settings)); - } - return array('completedClassNames' => $completed, 'settingsShapes' => $settings_shapes); -} -$direct_groups = array( - 'mdi-widgets-direct-basic-classic-first' => array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text'), - 'mdi-widgets-direct-basic-classic-second' => array('WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget'), - 'mdi-widgets-direct-media' => array('WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video'), - 'mdi-widgets-direct-custom-html-block' => array('WP_Widget_Custom_HTML', 'WP_Widget_Block'), - 'mdi-widgets-direct-block' => array('WP_Widget_Block'), - 'mdi-widgets-direct-custom-html' => array('WP_Widget_Custom_HTML'), -); -$widget_option_id_bases = array('archives', 'block', 'calendar', 'categories', 'custom_html', 'links', 'media_audio', 'media_gallery', 'media_image', 'media_video', 'meta', 'nav_menu', 'pages', 'recent-comments', 'recent-posts', 'rss', 'search', 'tag_cloud', 'text'); -$widget_classes = array('WP_Widget_Pages', 'WP_Widget_Calendar', 'WP_Widget_Archives', 'WP_Widget_Links', 'WP_Widget_Media_Audio', 'WP_Widget_Media_Image', 'WP_Widget_Media_Gallery', 'WP_Widget_Media_Video', 'WP_Widget_Meta', 'WP_Widget_Search', 'WP_Widget_Text', 'WP_Widget_Categories', 'WP_Widget_Recent_Posts', 'WP_Widget_Recent_Comments', 'WP_Widget_RSS', 'WP_Widget_Tag_Cloud', 'WP_Nav_Menu_Widget', 'WP_Widget_Custom_HTML', 'WP_Widget_Block'); -$get_settings_groups = array( - 'mdi-widgets-get-settings' => $widget_classes, - 'mdi-widgets-get-settings-first' => array_slice($widget_classes, 0, 10), - 'mdi-widgets-get-settings-second' => array_slice($widget_classes, 10), -); -if ($phase === 'mdi-widgets-callbacks') { - echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'callbacks' => wp_codebox_widgets_probe_inventory('widgets_init'), 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); - return; -} -$removed_init = wp_codebox_widgets_probe_remove_callbacks('init', array('wp_widgets_init'), false); -$memory_before = memory_get_usage(true); -if ($phase === 'mdi-widgets-option-reads') { - $get_option_found = array(); - $get_option_missing = array(); - $sqlite_present = array(); - $sqlite_missing = array(); - foreach ($widget_option_id_bases as $id_base) { - $option_name = 'widget_' . $id_base; - if (get_option($option_name, false) === false) $get_option_missing[] = $option_name; - else $get_option_found[] = $option_name; - $row_present = $wpdb->get_var($wpdb->prepare("SELECT 1 FROM {$wpdb->options} WHERE option_name = %s LIMIT 1", $option_name)); - if ($row_present === '1') $sqlite_present[] = $option_name; - else $sqlite_missing[] = $option_name; - } - echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'getOption' => array('found' => $get_option_found, 'missing' => $get_option_missing), 'sqliteRows' => array('present' => $sqlite_present, 'missing' => $sqlite_missing), 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); - return; -} -$removed_widgets = array(); -wp_codebox_widgets_probe_register_defaults(); -if (isset($get_settings_groups[$phase])) { - $settings_probe = wp_codebox_widgets_probe_get_settings($get_settings_groups[$phase]); - echo json_encode(array_merge(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true), $settings_probe)); - return; -} -if ($phase === 'mdi-widgets-register-one') { - $class_name = 'WP_Widget_Text'; - $option_name = 'widget_text'; - $instance_number = 2; - if (!isset($GLOBALS['wp_widget_factory']->widgets[$class_name])) throw new Exception('Widget probe class was not registered: ' . $class_name); - update_option($option_name, array($instance_number => array('title' => 'Widget probe'))); - $widget = $GLOBALS['wp_widget_factory']->widgets[$class_name]; - $settings = $widget->get_settings(); - $widget->_register_one($instance_number); - echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'widgetClassName' => $class_name, 'preloadedOptionName' => $option_name, 'getSettingsShape' => array('arrayLike' => is_array($settings), 'dimensions' => wp_codebox_widgets_probe_array_dimensions($settings)), 'registerOneCompleted' => true)); - return; -} -if (isset($direct_groups[$phase])) { - $removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array(), true); - wp_codebox_widgets_probe_register_selected($direct_groups[$phase]); - echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'removedInitCallbacks' => $removed_init, 'removedWidgetsCallbacks' => $removed_widgets, 'classNamesAttempted' => $direct_groups[$phase], 'classCount' => count($direct_groups[$phase]), 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); - return; -} elseif ($phase === 'mdi-widgets-hooks') { - do_action('widgets_init'); -} elseif ($phase === 'mdi-widgets-factory') { - $removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array('WP_Widget_Factory::_register_widgets'), true); - do_action('widgets_init'); -} elseif ($phase === 'mdi-widgets-remaining-hooks') { - $removed_widgets = wp_codebox_widgets_probe_remove_callbacks('widgets_init', array('WP_Widget_Factory::_register_widgets'), false); - do_action('widgets_init'); -} -$widget_class_count = isset($GLOBALS['wp_widget_factory']->widgets) && is_array($GLOBALS['wp_widget_factory']->widgets) ? count($GLOBALS['wp_widget_factory']->widgets) : 0; -echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'removedInitCallbacks' => $removed_init, 'removedWidgetsCallbacks' => $removed_widgets, 'widgetClassCount' => $widget_class_count, 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); -return; -PHP; -file_put_contents($settings_path, str_replace($needle, $probe, $settings)); -require '/wordpress/wp-load.php';` - } - - const initExclusions: Record = { - "mdi-init-exclude-scheduling": [ - "wp_schedule_update_checks", - "wp_schedule_delete_old_privacy_export_files", - "wp_cron", - "WP_Site_Health::schedule_cron", - "wp_schedule_site_health_cron", - "WP_Site_Health::maybe_create_scheduled_event", - ], - "mdi-init-exclude-block-registration": [ - "register_block_core_*", - "register_core_block_*", - "register_core_block_types_from_metadata", - "register_core_block_style_handles", - "wp_register_core_block_style_handles", - "WP_Block_Supports::init", - ], - "mdi-init-exclude-theme-patterns-styles": [ - "_register_theme_block_patterns", - "_register_theme_block_pattern_categories", - "_register_core_block_patterns_and_categories", - "wp_register_global_styles", - "wp_register_global_styles_custom_css", - "wp_register_typography_support", - ], - "mdi-init-exclude-widgets": ["wp_widgets_init"], - "mdi-init-exclude-rest-connectors-sitemaps": [ - "rest_api_init", - "wp_sitemaps_get_server", - "WP_Sitemaps::init", - "WP_Sitemaps_Registry::init", - ], - "mdi-init-exclude-initial-content-types": [ - "create_initial_taxonomies", - "create_initial_post_types", - "create_initial_post_statuses", - "wp_create_initial_post_meta", - ], - } - if (phase === "mdi-init-callbacks" || initExclusions[phase]) { - const excluded = initExclusions[phase] ?? [] - return `callbacks) || !is_array($hook->callbacks)) return $inventory; - foreach ($hook->callbacks as $priority => $callbacks) { - $identifiers = array(); - foreach ($callbacks as $registered) $identifiers[] = wp_codebox_init_probe_callback_identifier($registered['function']); - sort($identifiers, SORT_STRING); - $inventory[(string) $priority] = $identifiers; - } - ksort($inventory, SORT_NUMERIC); - return $inventory; -} -function wp_codebox_init_probe_matches($identifier, $patterns) { - foreach ($patterns as $pattern) { - if (str_ends_with($pattern, '*') && str_starts_with($identifier, substr($pattern, 0, -1))) return true; - if ($identifier === $pattern) return true; - } - return false; -} -$inventory = wp_codebox_init_probe_inventory(); -if ($phase === 'mdi-init-callbacks') { - echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'callbacks' => $inventory, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); - return; -} -$removed = array(); -$hook = isset($GLOBALS['wp_filter']['init']) ? $GLOBALS['wp_filter']['init'] : null; -if ($hook && isset($hook->callbacks) && is_array($hook->callbacks)) { - foreach ($hook->callbacks as $priority => $callbacks) { - foreach ($callbacks as $index => $registered) { - $identifier = wp_codebox_init_probe_callback_identifier($registered['function']); - if (wp_codebox_init_probe_matches($identifier, $excluded)) { - unset($hook->callbacks[$priority][$index]); - $removed[] = $identifier; - } - } - if (empty($hook->callbacks[$priority])) unset($hook->callbacks[$priority]); - } -} -sort($removed, SORT_STRING); -$memory_before = memory_get_usage(true); -do_action('init'); -echo json_encode(array('wordpressVersion' => $wp_version, 'bootstrapPhase' => $phase, 'completed' => true, 'removedCallbacks' => $removed, 'memoryBeforeBytes' => $memory_before, 'memoryBytes' => memory_get_usage(true), 'peakMemoryBytes' => memory_get_peak_usage(true))); -return; -PHP; -file_put_contents($settings_path, str_replace($needle, $probe, $settings)); -require '/wordpress/wp-load.php';` - } - const stops: Record = { "seeded-includes": { needle: "add_action( 'after_setup_theme', array( wp_script_modules(), 'add_hooks' ) );" }, "seeded-embed": { needle: "/**\n * WordPress Textdomain Registry object." }, diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 33ff2fa6..0955a62b 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -175,76 +175,6 @@ test("Cloudflare runtime pins and bundles the public constrained MDI runtime", a ]) assert.ok(names.includes(name), `Runtime bundle includes ${name}`) }) -test("Cloudflare MDI init diagnostics use fixed callback inventories and exclusion groups", async () => { - const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") - - for (const phase of [ - "mdi-init-callbacks", - "mdi-init-exclude-scheduling", - "mdi-init-exclude-block-registration", - "mdi-init-exclude-theme-patterns-styles", - "mdi-init-exclude-widgets", - "mdi-init-exclude-rest-connectors-sitemaps", - "mdi-init-exclude-initial-content-types", - "mdi-widgets-callbacks", - "mdi-widgets-constructors", - "mdi-widgets-hooks", - "mdi-widgets-factory", - "mdi-widgets-remaining-hooks", - "mdi-widgets-direct-basic-classic-first", - "mdi-widgets-direct-basic-classic-second", - "mdi-widgets-direct-media", - "mdi-widgets-direct-custom-html-block", - "mdi-widgets-direct-block", - "mdi-widgets-direct-custom-html", - "mdi-widgets-option-reads", - "mdi-widgets-get-settings", - "mdi-widgets-get-settings-first", - "mdi-widgets-get-settings-second", - "mdi-widgets-register-one", - ]) assert.match(worker, new RegExp(`"${phase}"`)) - - assert.match(worker, /\$needle = "do_action\( 'init' \);"/) - assert.match(worker, /substr_count\(\$settings, \$needle\) !== 1/) - assert.match(worker, /sort\(\$identifiers, SORT_STRING\)/) - assert.match(worker, /ksort\(\$inventory, SORT_NUMERIC\)/) - assert.match(worker, /"wp_schedule_update_checks"/) - assert.match(worker, /"WP_Site_Health::maybe_create_scheduled_event"/) - assert.match(worker, /"register_block_core_\*"/) - assert.match(worker, /"WP_Block_Supports::init"/) - assert.match(worker, /"wp_widgets_init"/) - assert.match(worker, /'WP_Widget_Factory::_register_widgets'/) - assert.match(worker, /function wp_codebox_widgets_probe_register_defaults\(\)/) - assert.match(worker, /if \(get_option\('link_manager_enabled'\)\) register_widget\('WP_Widget_Links'\);/) - assert.match(worker, /'widgetClassCount' => \$widget_class_count/) - assert.match(worker, /wp_codebox_widgets_probe_inventory\('widgets_init'\)/) - assert.match(worker, /function wp_codebox_widgets_probe_register_selected\(\$class_names\)/) - assert.match(worker, /\$registered\[\$class_name\]\->_register\(\)/) - assert.match(worker, /'mdi-widgets-direct-basic-classic-first' => array\('WP_Widget_Pages'/) - assert.match(worker, /'mdi-widgets-direct-media' => array\('WP_Widget_Media_Audio'/) - assert.match(worker, /'mdi-widgets-direct-custom-html-block' => array\('WP_Widget_Custom_HTML', 'WP_Widget_Block'\)/) - assert.match(worker, /'mdi-widgets-direct-block' => array\('WP_Widget_Block'\)/) - assert.match(worker, /'mdi-widgets-direct-custom-html' => array\('WP_Widget_Custom_HTML'\)/) - assert.match(worker, /if \(get_option\(\$option_name, false\) === false\) \$get_option_missing\[\] = \$option_name/) - assert.match(worker, /\$wpdb->get_var\(\$wpdb->prepare\("SELECT 1 FROM \{\$wpdb->options\} WHERE option_name = %s LIMIT 1", \$option_name\)\)/) - assert.match(worker, /'getOption' => array\('found' => \$get_option_found, 'missing' => \$get_option_missing\)/) - assert.match(worker, /'sqliteRows' => array\('present' => \$sqlite_present, 'missing' => \$sqlite_missing\)/) - assert.match(worker, /function wp_codebox_widgets_probe_get_settings\(\$class_names\)/) - assert.match(worker, /\$settings = \$registered\[\$class_name\]->get_settings\(\)/) - assert.match(worker, /'mdi-widgets-get-settings-first' => array_slice\(\$widget_classes, 0, 10\)/) - assert.match(worker, /'mdi-widgets-get-settings-second' => array_slice\(\$widget_classes, 10\)/) - assert.match(worker, /\$widget->_register_one\(\$instance_number\)/) - assert.match(worker, /update_option\(\$option_name, array\(\$instance_number => array\('title' => 'Widget probe'\)\)\)/) - assert.match(worker, /'classNamesAttempted' => \$direct_groups\[\$phase\]/) - assert.doesNotMatch(worker, /json_encode\(\$settings\)/) - assert.doesNotMatch(worker, /json_encode\(get_option/) - assert.match(worker, /"rest_api_init"/) - assert.match(worker, /"create_initial_taxonomies"/) - assert.match(worker, /"wp_create_initial_post_meta"/) - assert.match(worker, /do_action\('init'\);/) - assert.doesNotMatch(worker, /searchParams\.get\([^)]*callback|searchParams\.get\([^)]*exclude|searchParams\.get\([^)]*widget/) -}) - test("Cloudflare canonical runtime patches the unique init call with runtime persistence policies", async () => { const worker = await readFile(new URL("../packages/runtime-cloudflare/src/worker.ts", import.meta.url), "utf8") const patcher = worker.slice(worker.indexOf("function patchCanonicalRuntimePoliciesAtInit"), worker.indexOf("\nfunction collectRuntimeFiles")) From c423cdaed391fb789bdcf2b31e01c6f32f98d421 Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 19:09:31 -0400 Subject: [PATCH 73/87] Remove Cloudflare investigation diagnostics --- packages/runtime-cloudflare/README.md | 4 +- .../runtime-cloudflare/src/request-routing.ts | 2 - packages/runtime-cloudflare/src/worker.ts | 61 +------------------ scripts/cloudflare-local-gate.mjs | 20 ------ tests/cloudflare-runtime.test.ts | 8 --- 5 files changed, 4 insertions(+), 91 deletions(-) diff --git a/packages/runtime-cloudflare/README.md b/packages/runtime-cloudflare/README.md index 09ec3129..63d74582 100644 --- a/packages/runtime-cloudflare/README.md +++ b/packages/runtime-cloudflare/README.md @@ -12,9 +12,9 @@ Canonical browser, health, and mutation boots require the separately managed `WO After each mutating HTTP request, the entry runtime invokes MDI's explicit request-boundary flush, collects canonical files, stores immutable content-addressed R2 objects and a revision manifest, then commits the new pointer through the held lease. GET, HEAD, and asset requests release without promotion. Failed requests abort their leases; stale leases recover by token/version/expiry checks. The entry isolate can cache one runtime only for the exact acquired pointer revision and exits it after promotion or when another isolate advances the pointer. It does not persist SQLite. Existing manifests are reused when canonical file hashes have not changed. -The Worker forwards browser cookies directly to Playground and disables Playground's internal cookie store, preventing an empty per-isolate store from replacing a valid browser session after cold restart. `?phase=canonical-auth` is a boolean-only diagnostic for the canonical runtime; it reports cookie parsing and verification stages without returning credential, token, hash, or salt material. +The Worker forwards browser cookies directly to Playground and disables Playground's internal cookie store, preventing an empty per-isolate store from replacing a valid browser session after cold restart. -The bundled MDI source is pinned to immutable commit `1870fb41279e7eb5946e506c9c7406f1f1ea6dc3` from [MDI PR #127](https://github.com/Automattic/markdown-database-integration/pull/127). The bundle generator, worker provenance, and source-contract test use the same revision. +The bundled MDI source is pinned to immutable commit `2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c` from [MDI PR #139](https://github.com/Automattic/markdown-database-integration/pull/139). The bundle generator, worker provenance, and source-contract test use the same revision. The WordPress server corpus is a separate deployment artifact, never a Worker-module import. `generate:cloudflare-wordpress-runtime-corpus` uses bounded ZIP Range decoding against the pinned WordPress release, retains only `isWordPressRuntimeFile()` entries, and emits a deterministic ZIP under `artifacts/` plus the checked-in `assets/wordpress-runtime-artifact.json` provenance manifest. The immutable R2 key is derived from the corpus ZIP SHA-256. Cold boot fetches that exact object through `WORDPRESS_STATE_BUCKET`, validates the manifest and archive/file hashes and budgets, then streams it into PHP MEMFS. Static browser assets continue to use lazy exact archive ranges and Worker cache. diff --git a/packages/runtime-cloudflare/src/request-routing.ts b/packages/runtime-cloudflare/src/request-routing.ts index 51326da6..6efe103f 100644 --- a/packages/runtime-cloudflare/src/request-routing.ts +++ b/packages/runtime-cloudflare/src/request-routing.ts @@ -3,7 +3,6 @@ export type WorkerRequestRoute = | { kind: "health" } | { kind: "r2-state" } | { kind: "r2-mutate" } - | { kind: "canonical-auth" } | { kind: "operator-reset" } | { kind: "probe"; phase: string } @@ -13,7 +12,6 @@ export function routeWorkerRequest(request: Request): WorkerRequestRoute { if (phase === "health") return { kind: "health" } if (phase === "r2-state") return { kind: "r2-state" } if (phase === "r2-mutate") return { kind: "r2-mutate" } - if (phase === "canonical-auth") return { kind: "canonical-auth" } if (phase === "operator-reset") return { kind: "operator-reset" } return { kind: "probe", phase } } diff --git a/packages/runtime-cloudflare/src/worker.ts b/packages/runtime-cloudflare/src/worker.ts index 560afd8e..951ec1a3 100644 --- a/packages/runtime-cloudflare/src/worker.ts +++ b/packages/runtime-cloudflare/src/worker.ts @@ -87,14 +87,12 @@ export default { const coordinator = env.WORDPRESS_STATE.getByName("default") if (route.kind === "operator-reset") return resetCanonicalWordPress(request, env, coordinator) if (route.kind === "probe") { - if (route.phase === "canonical-session") return canonicalSessionProbe(env.WORDPRESS_STATE_BUCKET, await coordinatorCall<{ pointer: MarkdownPointer | null }>(coordinator, request.url, "state")) return runBootProbe(route.phase, env.WORDPRESS_STATE_BUCKET) } if (route.kind === "r2-state") { if (request.method !== "GET") return new Response("WordPress state read requires GET.", { status: 405 }) return coordinator.fetch(new Request(coordinatorUrl(request.url, "state"))) } - if (route.kind === "canonical-auth") return runCoordinatedWordPressRequest(request, env, coordinator, route.kind) return runCoordinatedWordPressRequest(request, env, coordinator, route.kind) }, } @@ -160,7 +158,7 @@ async function secretsMatch(left: string, right: string): Promise { return difference === 0 } -async function runCoordinatedWordPressRequest(request: Request, env: Env, coordinator: DurableObjectStub, route: "wordpress" | "health" | "r2-mutate" | "canonical-auth"): Promise { +async function runCoordinatedWordPressRequest(request: Request, env: Env, coordinator: DurableObjectStub, route: "wordpress" | "health" | "r2-mutate"): Promise { if (route === "r2-mutate" && request.method !== "POST") return new Response("WordPress state mutation requires POST.", { status: 405 }) let lease = await acquireLease(coordinator, request.url) let finalized = false @@ -181,8 +179,6 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi canonicalChanges = mutation.canonicalChanges } else if (route === "health") { response = await health(runtime) - } else if (route === "canonical-auth") { - response = await canonicalAuthProbe(runtime, request) } else { if (isMutation(request, route)) runtime.php.writeFile(MARKDOWN_CHANGES_PATH, new TextEncoder().encode(JSON.stringify({ created: [], changed: [], deleted: [] }))) response = toFetchResponse(request, await runtime.requestHandler.request(await toPHPRequest(request))) @@ -204,7 +200,7 @@ async function runCoordinatedWordPressRequest(request: Request, env: Env, coordi } } -function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate" | "canonical-auth"): boolean { +function isMutation(request: Request, route: "wordpress" | "health" | "r2-mutate"): boolean { return route === "r2-mutate" || !["GET", "HEAD", "OPTIONS"].includes(request.method) } @@ -376,59 +372,6 @@ async function health(runtime: Runtime): Promise { return cloudflareRuntimeHealthResponse({ schema: CLOUDFLARE_RUNTIME_HEALTH_SCHEMA, marker: CLOUDFLARE_RUNTIME_HEALTH_MARKER, wordpressVersion: runtime.wordpressVersion, phpVersion, runtime: { backend: "wordpress-playground", environment: "wordpress" }, evidence: { initialization: "completed", execution: "completed", initializationScope: "isolate" } }) } -async function canonicalAuthProbe(runtime: Runtime, request: Request): Promise { - const cookiePath = "/tmp/wp-codebox-canonical-auth-cookie" - runtime.php.writeFile(cookiePath, new TextEncoder().encode(request.headers.get("cookie") ?? "")) - const output = (await runtime.php.run({ code: `ID) : null; -$session_row = $user ? $GLOBALS['wpdb']->get_var($GLOBALS['wpdb']->prepare("SELECT meta_value FROM {$GLOBALS['wpdb']->usermeta} WHERE user_id = %d AND meta_key LIKE %s", $user->ID, '%session_tokens')) : null; -$session_value = is_string($session_row) ? maybe_unserialize($session_row) : null; -$auth_constants = ['AUTH_KEY', 'SECURE_AUTH_KEY', 'LOGGED_IN_KEY', 'NONCE_KEY', 'AUTH_SALT', 'SECURE_AUTH_SALT', 'LOGGED_IN_SALT', 'NONCE_SALT']; -echo json_encode([ - 'schema' => 'wp-codebox/cloudflare-canonical-auth/v1', - 'authCookiePresent' => isset($_COOKIE[LOGGED_IN_COOKIE]), - 'authCookieParsed' => is_array($parsed) && isset($parsed['username'], $parsed['expiration'], $parsed['token'], $parsed['hmac']), - 'adminCookiePresent' => isset($_COOKIE[AUTH_COOKIE]), - 'adminCookieParsed' => is_array($admin_parsed) && isset($admin_parsed['username'], $admin_parsed['expiration'], $admin_parsed['token'], $admin_parsed['hmac']), - 'userFound' => false !== $user, - 'sessionTokenRowPresent' => is_string($session_row) && $session_row !== '', - 'sessionTokenSerializedArray' => is_array($session_value), - 'sessionTokenVerified' => $session instanceof WP_Session_Tokens && is_string($token) && $token !== '' && $session->verify($token), - 'authConstantsDefined' => !array_diff($auth_constants, array_filter($auth_constants, 'defined')), - 'adminCookieValidated' => false !== wp_validate_auth_cookie('', 'auth'), - 'loggedInCookieValidated' => false !== wp_validate_auth_cookie('', 'logged_in'), -]);` })).text.trim() - return Response.json(JSON.parse(output) as Record) -} - -async function canonicalSessionProbe(bucket: R2Bucket, state: { pointer: MarkdownPointer | null }): Promise { - if (!state.pointer) return Response.json({ schema: "wp-codebox/cloudflare-canonical-session/v1", pointerRevision: null, sessionTokenRows: 0 }) - const manifest = await readMarkdownManifest(bucket, state.pointer) - const users = manifest?.files.find((file) => file.path === "_tables/users.json") - const usermeta = manifest?.files.find((file) => file.path === "_tables/usermeta.json") - if (!users || !usermeta) throw new Error("The canonical WordPress revision is missing users or usermeta.") - const usersObject = await bucket.get(users.objectKey) - const object = await bucket.get(usermeta.objectKey) - if (!usersObject || !object) throw new Error("The canonical WordPress user identity objects are missing.") - const usersRows = await usersObject.json>() - const rows = await object.json>() - const usermetaKeys = rows.map((row) => row.meta_key).filter((key): key is string => typeof key === "string").sort() - const sessionTokenRows = rows.filter((row) => row.meta_key?.endsWith("session_tokens")) - const adminRow = usersRows.find((row) => typeof row.user_login === "string") - return Response.json({ schema: "wp-codebox/cloudflare-canonical-session/v1", pointerRevision: state.pointer.revision, sessionTokenRows: sessionTokenRows.length, hasSessionTokens: sessionTokenRows.some((row) => typeof row.meta_value === "string" && row.meta_value.length > 0), usersTableStructurallyValid: !!adminRow && (typeof adminRow.ID === "number" || (typeof adminRow.ID === "string" && /^\d+$/.test(adminRow.ID))) && typeof adminRow.user_pass === "string", usersTableFieldTypes: { id: typeof adminRow?.ID, login: typeof adminRow?.user_login, password: typeof adminRow?.user_pass }, usermetaTableStructurallyValid: rows.every((row) => typeof row.meta_key === "string" && typeof row.meta_value === "string"), usermetaKeys }) -} - class CoordinatorRequestError extends Error { constructor(readonly status: number, message: string, readonly retryAfter?: number) { super(message) diff --git a/scripts/cloudflare-local-gate.mjs b/scripts/cloudflare-local-gate.mjs index 3d8239c7..22f90744 100644 --- a/scripts/cloudflare-local-gate.mjs +++ b/scripts/cloudflare-local-gate.mjs @@ -27,12 +27,9 @@ try { await assertLinkedAssets(adminHtml, "admin") await assertLinkedAssets(editorHtml, "editor") await assertStaticResponseSemantics() - await assertCanonicalAuth("before restart") await stopWorker() await startWorker() - await assertCanonicalIdentity("after restart") - await assertCanonicalAuth("after restart") const restartedAdmin = await assertAuthenticatedDashboard(new URL("/wp-admin/", origin)) const restartedPost = await assertWordPressPage(`${origin}/${post.slug}/`, "post after cold restart") assertIncludes(restartedPost, post.title, "post after cold restart") @@ -96,26 +93,9 @@ async function login() { const location = response.headers.get("location") if (!location?.includes("/wp-admin/")) throw new Error(`Login did not redirect to wp-admin: ${location}`) const admin = await assertAuthenticatedDashboard(new URL(location, origin)) - await assertCanonicalIdentity("after login") return admin } -async function assertCanonicalIdentity(stage) { - const session = await (await request(`${origin}/?phase=canonical-session`)).json() - if (!session.hasSessionTokens || session.sessionTokenRows < 1 || !session.usersTableStructurallyValid || !session.usermetaTableStructurallyValid) throw new Error(`Login did not persist a structurally valid canonical identity: ${JSON.stringify(session)}`) - console.log(`Canonical identity ${stage}: revision ${session.pointerRevision}, session-token rows ${session.sessionTokenRows}, users field types ${JSON.stringify(session.usersTableFieldTypes)}, usermeta keys ${session.usermetaKeys.join(", ")}`) -} - -async function assertCanonicalAuth(stage) { - const response = await request(`${origin}/wp-admin/?phase=canonical-auth`) - const diagnostic = await response.json() - console.log(`Canonical auth ${stage}: ${JSON.stringify(diagnostic)}`) - const checks = ["authCookiePresent", "authCookieParsed", "adminCookiePresent", "adminCookieParsed", "userFound", "sessionTokenRowPresent", "sessionTokenSerializedArray", "sessionTokenVerified", "authConstantsDefined", "adminCookieValidated", "loggedInCookieValidated"] - if (!response.ok || diagnostic.schema !== "wp-codebox/cloudflare-canonical-auth/v1" || checks.some((check) => diagnostic[check] !== true)) { - throw new Error(`Canonical auth diagnostic failed ${stage}: ${JSON.stringify(diagnostic)}`) - } -} - async function createPost(adminHtml) { const nonce = adminHtml.match(/"nonce":"([^"]+)"/)?.[1] if (!nonce) throw new Error("wp-admin did not expose a REST nonce.") diff --git a/tests/cloudflare-runtime.test.ts b/tests/cloudflare-runtime.test.ts index 0955a62b..9ece2769 100644 --- a/tests/cloudflare-runtime.test.ts +++ b/tests/cloudflare-runtime.test.ts @@ -41,7 +41,6 @@ test("Cloudflare routing reserves phases while the phase-less route serves WordP assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=health")), { kind: "health" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-state")), { kind: "r2-state" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=r2-mutate")), { kind: "r2-mutate" }) - assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=canonical-auth")), { kind: "canonical-auth" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=operator-reset")), { kind: "operator-reset" }) assert.deepEqual(routeWorkerRequest(new Request("https://worker.example/?phase=seeded-wordpress")), { kind: "probe", phase: "seeded-wordpress" }) }) @@ -230,14 +229,7 @@ test("Cloudflare keeps PHP-WASM in the entry Worker and uses the Durable Object assert.match(coordinator, /token: crypto\.randomUUID\(\)/) assert.match(coordinator, /record\.version\+\+/) assert.match(coordinator, /lease\.expiresAt <= Date\.now\(\)/) - assert.match(worker, /phase === "canonical-session"/) - assert.match(worker, /canonicalAuthProbe\(runtime, request\)/) - assert.match(worker, /WP_Session_Tokens::get_instance/) - assert.match(worker, /wp_validate_auth_cookie\('', 'logged_in'\)/) - assert.match(worker, /authConstantsDefined/) - assert.match(worker, /adminCookieValidated/) assert.match(worker, /cookieStore: false/) - assert.match(worker, /meta_key\?\.endsWith\("session_tokens"\)/) assert.match(worker, /maxPhpInstances: 1/) assert.match(worker, /WORDPRESS_AUTH_SECRET/) assert.match(worker, /canonicalWordPressAuthConstants\(env\)/) From 7e0e6427e96a3f2a198ba8c650da74b2dab7f9fe Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 19:14:05 -0400 Subject: [PATCH 74/87] Preserve generic hook removal behavior --- .../runtime-core/src/runtime-php-snippets.ts | 11 +++++----- .../src/bench-command-handlers.ts | 11 +++++----- .../src/phpunit-command-handlers.ts | 20 +++++++++---------- tests/runtime-php-snippets.test.ts | 10 ++++++++++ 4 files changed, 29 insertions(+), 23 deletions(-) diff --git a/packages/runtime-core/src/runtime-php-snippets.ts b/packages/runtime-core/src/runtime-php-snippets.ts index 979e677a..bc918751 100644 --- a/packages/runtime-core/src/runtime-php-snippets.ts +++ b/packages/runtime-core/src/runtime-php-snippets.ts @@ -15,7 +15,8 @@ function ${snapshot}(string $hook_name): array { if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $snapshot; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { $snapshot[$priority . ':' . $callback_id] = true; } @@ -33,11 +34,9 @@ function ${defer}(string $hook_name, array $before): array { if (isset($before[$priority . ':' . $callback_id])) { continue; } - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); - } - if (empty($wp_filter[$hook_name]->callbacks[$priority])) { - unset($wp_filter[$hook_name]->callbacks[$priority]); + if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + } } } usort($deferred, static function (array $left, array $right): int { return ($left['priority'] ?? 10) <=> ($right['priority'] ?? 10); }); diff --git a/packages/runtime-playground/src/bench-command-handlers.ts b/packages/runtime-playground/src/bench-command-handlers.ts index db1efa5f..ffda4d3a 100644 --- a/packages/runtime-playground/src/bench-command-handlers.ts +++ b/packages/runtime-playground/src/bench-command-handlers.ts @@ -1181,7 +1181,8 @@ function wp_codebox_bench_snapshot_wordpress_hook_callbacks(string $hook_name): if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $snapshot; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { $snapshot[$priority . ':' . $callback_id] = true; } @@ -1200,11 +1201,9 @@ function wp_codebox_bench_defer_new_wordpress_hook_callbacks(string $hook_name, if (isset($before[$priority . ':' . $callback_id])) { continue; } - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); - } - if (empty($wp_filter[$hook_name]->callbacks[$priority])) { - unset($wp_filter[$hook_name]->callbacks[$priority]); + if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + } } } usort($deferred, static function (array $left, array $right): int { diff --git a/packages/runtime-playground/src/phpunit-command-handlers.ts b/packages/runtime-playground/src/phpunit-command-handlers.ts index f53aafcf..7fb81f71 100644 --- a/packages/runtime-playground/src/phpunit-command-handlers.ts +++ b/packages/runtime-playground/src/phpunit-command-handlers.ts @@ -497,7 +497,8 @@ function pg_snapshot_wordpress_hook_callbacks(string $hook_name): array { if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $snapshot; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { $snapshot[$priority . ':' . $callback_id] = true; } @@ -513,12 +514,10 @@ function pg_remove_new_wordpress_hook_callbacks(string $hook_name, array $before foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { if (!isset($before[$priority . ':' . $callback_id])) { - unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); + $callback = $callbacks[$callback_id]; + if (isset($callback['function'])) remove_action($hook_name, $callback['function'], (int) $priority); } } - if (empty($wp_filter[$hook_name]->callbacks[$priority])) { - unset($wp_filter[$hook_name]->callbacks[$priority]); - } } } @@ -528,16 +527,15 @@ function pg_defer_new_wordpress_hook_callbacks(string $hook_name, array $before) if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; } - $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); - unset($wp_filter[$hook_name]->callbacks[$priority][$callback_id]); - } - if (empty($wp_filter[$hook_name]->callbacks[$priority])) { - unset($wp_filter[$hook_name]->callbacks[$priority]); + if (isset($callback['function']) && remove_action($hook_name, $callback['function'], (int) $priority)) { + $deferred[] = array('priority' => (int) $priority, 'callback' => $callback); + } } } usort($deferred, static function (array $left, array $right): int { return $left['priority'] <=> $right['priority']; }); diff --git a/tests/runtime-php-snippets.test.ts b/tests/runtime-php-snippets.test.ts index 9481c060..a73633ed 100644 --- a/tests/runtime-php-snippets.test.ts +++ b/tests/runtime-php-snippets.test.ts @@ -30,6 +30,16 @@ $GLOBALS['calls'] = array(); function did_action($hook_name) { return (int) ($GLOBALS['wp_actions'][$hook_name] ?? 0); } function wp_get_abilities() { return array('before' => true, 'after' => true); } +function remove_action($hook_name, $callback, $priority = 10) { + foreach ($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority] ?? array() as $callback_id => $registered) { + if (($registered['function'] ?? null) === $callback) { + unset($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority][$callback_id]); + if (empty($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority])) unset($GLOBALS['wp_filter'][$hook_name]->callbacks[$priority]); + return true; + } + } + return false; +} $GLOBALS['wp_filter']['init']->callbacks[10]['existing'] = array('function' => static function () { $GLOBALS['calls'][] = 'existing'; }, 'accepted_args' => 0); $state = contained_runtime_test_component_lifecycle_replay_prepare(); From 486bdf83188abadc387081bd19adf55821e6f29c Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 19:15:46 -0400 Subject: [PATCH 75/87] Restore hook snapshot iteration --- packages/runtime-core/src/runtime-php-snippets.ts | 6 +++--- packages/runtime-playground/src/bench-command-handlers.ts | 6 +++--- packages/runtime-playground/src/phpunit-command-handlers.ts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/packages/runtime-core/src/runtime-php-snippets.ts b/packages/runtime-core/src/runtime-php-snippets.ts index bc918751..dc26ea3d 100644 --- a/packages/runtime-core/src/runtime-php-snippets.ts +++ b/packages/runtime-core/src/runtime-php-snippets.ts @@ -15,8 +15,7 @@ function ${snapshot}(string $hook_name): array { if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $snapshot; } - $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { $snapshot[$priority . ':' . $callback_id] = true; } @@ -29,7 +28,8 @@ function ${defer}(string $hook_name, array $before): array { if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; diff --git a/packages/runtime-playground/src/bench-command-handlers.ts b/packages/runtime-playground/src/bench-command-handlers.ts index ffda4d3a..b2085c81 100644 --- a/packages/runtime-playground/src/bench-command-handlers.ts +++ b/packages/runtime-playground/src/bench-command-handlers.ts @@ -1181,8 +1181,7 @@ function wp_codebox_bench_snapshot_wordpress_hook_callbacks(string $hook_name): if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $snapshot; } - $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { $snapshot[$priority . ':' . $callback_id] = true; } @@ -1196,7 +1195,8 @@ function wp_codebox_bench_defer_new_wordpress_hook_callbacks(string $hook_name, if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $deferred; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach ($callbacks as $callback_id => $callback) { if (isset($before[$priority . ':' . $callback_id])) { continue; diff --git a/packages/runtime-playground/src/phpunit-command-handlers.ts b/packages/runtime-playground/src/phpunit-command-handlers.ts index 7fb81f71..7b28581a 100644 --- a/packages/runtime-playground/src/phpunit-command-handlers.ts +++ b/packages/runtime-playground/src/phpunit-command-handlers.ts @@ -497,8 +497,7 @@ function pg_snapshot_wordpress_hook_callbacks(string $hook_name): array { if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return $snapshot; } - $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; - foreach ($callbacks_by_priority as $priority => $callbacks) { + foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { $snapshot[$priority . ':' . $callback_id] = true; } @@ -511,7 +510,8 @@ function pg_remove_new_wordpress_hook_callbacks(string $hook_name, array $before if (!isset($wp_filter[$hook_name]) || !isset($wp_filter[$hook_name]->callbacks)) { return; } - foreach ($wp_filter[$hook_name]->callbacks as $priority => $callbacks) { + $callbacks_by_priority = $wp_filter[$hook_name]->callbacks; + foreach ($callbacks_by_priority as $priority => $callbacks) { foreach (array_keys($callbacks) as $callback_id) { if (!isset($before[$priority . ':' . $callback_id])) { $callback = $callbacks[$callback_id]; From 21f135451b9a128c368a8a0d45e0863dc867349d Mon Sep 17 00:00:00 2001 From: Chris Huber Date: Mon, 20 Jul 2026 20:18:45 -0400 Subject: [PATCH 76/87] Add canonical Cloudflare runtime homepage --- ...n-database-integration-canonical-seed.json | 28 ++--- ...wn-database-integration-canonical-seed.zip | Bin 48818 -> 51807 bytes .../build-cloudflare-canonical-mdi-seed.php | 104 ++++++++++++++++++ tests/cloudflare-runtime.test.ts | 34 ++++++ 4 files changed, 152 insertions(+), 14 deletions(-) diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json index dee9df9d..ebf04a46 100644 --- a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json +++ b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.json @@ -2,7 +2,7 @@ "schema": "wp-codebox/cloudflare-canonical-mdi-seed/v1", "markdownDatabaseIntegrationRevision": "2a8ee7f6a46e1d64b4606f1ee3c97e14032dc96c", "wordpressInstallSeedSha256": "7212b89f1c01f98a23b89e9a2dd2b214306a85c10adb0355b9c7a6005b4fe822", - "archiveSha256": "717b53395be2eb29ba7419f5a0b7eee4fe5a6527ec09246b24133aea82b3686c", + "archiveSha256": "ee84fad280beb3ee2965a6f430b849ea31ce2899d2533b2906da7dcf3f306a43", "files": [ { "path": "_options/active_plugins.json", @@ -61,13 +61,13 @@ }, { "path": "_options/blogdescription.json", - "sha256": "759154f66c8b9bed0810ce36349fc7a2ed8a0da3acf6c59a2e6e01f863d89c5b", - "size": 106 + "sha256": "c2c48dc802585847cd2301fcb89c10b624104a2860afb29b4b27569cfe390136", + "size": 157 }, { "path": "_options/blogname.json", - "sha256": "2d121fcce756b914f84be99b8a0a75a1003e6a21f46e4c9d1dcf10502aa18de2", - "size": 119 + "sha256": "bb010b37edc23e3855e5afb90109904858c0e7c1e1df070fc3af50c8cd0a06cb", + "size": 127 }, { "path": "_options/category_base.json", @@ -311,7 +311,7 @@ }, { "path": "_options/page_on_front.json", - "sha256": "aff01d58fb09d4c284db6550484612b378ef54a95b2843faba230e57f3ab6da3", + "sha256": "94b00bb0fc5ef8ef6af537bfdae32d3d568f64261627e15a4bbe113c79e90bc7", "size": 106 }, { @@ -366,8 +366,8 @@ }, { "path": "_options/show_on_front.json", - "sha256": "34d9c066eb8dd1f36c14e67e5b34ba4cfb041ebdc9d9ba1be2b78869879bb002", - "size": 110 + "sha256": "fdac10eccf664e5d32e0467b7827b6fb589b7be04eafb1ff5188964521203b56", + "size": 109 }, { "path": "_options/sidebars_widgets.json", @@ -411,7 +411,7 @@ }, { "path": "_options/theme_mods_twentytwentyfive.json", - "sha256": "1d251370cb28c8d1aa06c7d78a0cb604e954caff117eb306321c7111d60035c6", + "sha256": "b9d65f5c32b7e37a1274a40166a4ef884540cc5aff658e40ced68cf58c4ae4a0", "size": 161 }, { @@ -659,16 +659,16 @@ "sha256": "a59e1935b9636e9e7fe2fa2a9699e3d75fa055f91bf05a3a298cb97f14c463b7", "size": 431 }, + { + "path": "page/cloudflare-wordpress-runtime.md", + "sha256": "f3d3e42ada4bdde99f505a83d6b69c5b027289f4dd147b5d0e026e441c9914b9", + "size": 4820 + }, { "path": "page/privacy-policy.md", "sha256": "a2a87d2d22e084f5c8feb45329881f24f22dc83e0882b1b2cfe8eb5b05decc01", "size": 6118 }, - { - "path": "page/sample-page.md", - "sha256": "066c9dcb78ab3c93c719161e4d819bd136c12dda7b3dacf3497839a3ab927609", - "size": 1923 - }, { "path": "post/hello-world.md", "sha256": "3accc9d34069ca26be508b15ce84de25f4740b521b8acf12c87e97003c77fc11", diff --git a/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip b/packages/runtime-cloudflare/assets/markdown-database-integration-canonical-seed.zip index f4ba64762a50b460de4e2ba88e9a9d5413b5ac5c..7f55b077e8cb069a85b519903cc226a732d37ebf 100644 GIT binary patch delta 5452 zcmeHLd2AHd8TY(^v5kSX!HZcN`?1^$%i7%7#$bcNw~e_<3#8+nv3Kz7&N4G=W3C0Y z$WhaT<4aTuwlRk>H`vR4B}i#1r9rewlc zS6+?JqY7?qb>N?ITWP%a+tOxruL@#j8ED{-R`H~c+Id5*2;1M9Q1 zV*8yO*K`{?XO)m?S`QTLtNH4pV^BbJJ>{b7}iomVL8UA`%@_FedtIF0_vToj*V|BBF$2!?6vdxlD zwdUZfX0JBylst+m<4c~iZrMWZ_WXoAZ7p?*tfI`dHhJx}qU3C_>msfAc9h*to4h$L zp5+8jJsu+Rjl5{h$qw#zWmD+cJYr@qDp-}OE}rsSJwMrSCg+pcT9JfdColRWBDVWXxc!mOUR1`qItbaSzaU4LWOmf96QspoHTZfrU^o@jv1| zoB^zdqt(ULlzluI*y>)LX_HGpT^o4bs~H^9T@h5?NDhiFRRoULt>gs7D=E5l>wUc3 zOgUC%Ygq*jwq(F3yW@xUaop4`@UYRER3JNDDAK9=Fs155sR^=nr1``Cc1ohpjjHPr z;)KSK6T$%sF0kf@B4kYbyA8eTL!gPo$jUDT$&*wFVkD$MA4{i&ATkZQH(N|a2nZ_k z=q5$Q*iXaQo8hCPIEdew;4Yzhf$F3&vT$wL5MmKqlQK3 zvFH~Ou8DH+v%Aa7%51&NdR>?TtSHobtSqJz?@@Uf6D5xy$FLAC3mdy?=&A5WOYXPW65e?h$=Dn$f$HSD?Lh z0y@aadf8e*6jZpgIit6Ei-MvWoy8|c=A6!~w3oeISR2{V%Wh-6DCCz*^%}{AGFCaBm`wVd<%YEhz#3hJ{R)aRTU z9%;4g*P|O0hR)IWyCK^tHh*QwPN|VMTEEV~*g9;ulr;c@RZCWBoh-SvqZ?>6vclMT z`sogz80Fk&fr9w6lA4MOM>(e{8}osb&%&MSqnz4!6XeTC`ai#6VH&G-xbUEIDQ4%oO< zNZW5Clj0#Uek3&RHG#U{!rX?P`)kOK1lYCT0^1UjVMwcuSp+$)<;(+UX{{jbiQrE( z)|&^J2KLHa02ys72pJ5GZIvW27?RqD!G~=#m~Vrry`03FU~PL5IbnhqO%`~q-A2li z;FftP%u5;s0}t59=fsKxln85Z>Qu76q9ZvO&ft(6st@lUm`g?sGxm1mk++S< zT^%`OYl=Sm`mla*EhQfODgAY``k76My=rEg^+j0+bI3>9V>-NW&_;eR92$ma!UsJ` z&_8uZ%(bB8O) zJKDby@OJuO7ynoT_d5q(+qpZn&7S01m-7judA4>8wdBhhC{`%EHY#~d^Ro<+K<`D zMePyqO2=j}ESQfMlYSH7yT_*(-&-CXGc(8GIL?;az-}82<`XuOF$o^oEU@~7jd>4R zPLwd4;1?%~Nv3^|Jri^wC$J^UpHW0n?*8~(qzJtG?)WsQU{0w^jw(iLw&*6A{4F>F4)kohg#I$cP9 zJRLSoH&8nDjD^XCiZhFukKxrbMdYQKAk9qGk@2^gk;oW33+UMi>DniwgLt0yoA}uI zw`alGS(9MrSsS@98}jE^;JvdBvU(0aj)h6*Y-C6dJeZTAi+&*|Qo-}kajuL!p9=-K z6vJ&J33+fYHw~KGO~7?qnA70zE@hU&TixZPUp}Pv6fuWjZBHpv499!EMF!4?`|~t( znqbF#6D0N|Fx^lQSVXD{AiKaY^UZ?D1OEiS4iquFVf6V5W-hEhUrJ&Z!j1Eb&`DJn zQebZJKwZn}7c9h70@WAFnAf58LIv4b3hPU)FzBLg*>z3gEA zjH62O#u8X^c_;)fXOp@bD6TQ=7<$D*CM^ZWl~U%fu<^@jo6%!E=&s!k(x(PP$k-j(r9oGt(PvGvgd1Ryjk6goX zk&UT=y6ffWqQLdVj2HfKy^!gI{2TL_Ww7-|A@d>Jx-qse`b%L54i~pyQJ>+v;S@Iq i@87VIJ1szNPJo7+7V?J!&~kHBf-*iP=KVvOxPJliV@ufp delta 2718 zcmb_edr(y873Y3Z5e&GBAnda8fym>^Yw>|WKoEI`hc;D`be#0=?q&DN?!D{YT~?ZL zEYoRYOf}J;t*MzdIE}A}ih>}<2lzlVO{gxkgSLJ^Lq&*`gJ`fO?6`_Gz__^rQp;D=EW5ylly zwm!)v7wD4L=aKUadFK}E^vAIGZFJbo9B7E_)_|1yW=_{#R z-Z*Y#_{it=fh?Unm9{q7FRKCG=GVMj;&zYb;y%L3?Xp7(>M~aXJfwI$Tn|W{Tv55E z%G~FX95T6d-dDM`5D)qcZKoFWbK*qis??#lq=2$nQ+YONyoS?&TlUI&PCjokRz6vy zoD9!?BS^q)psciOK~fP$IxPjrX~3^JgAQ2_Tl0WM_yPo3kqDF#sY9VZ&956daj)WX z2e?jHbm&ZfCk_|!f#PjmXV@fG;NBj8N;gNK` zEIs7Q3vcFychh0pW!YS(7$E#|8n4d^(`7iOjC5Ib$$oC{`IE|k^=!n&Eix_gBJ2n@VQ@7)chQx!fGj) z%$rCsXepTB1-CXPp?7(ses7!>6e78Xvls8KhkMlC4OB(y84lMc0U zDI^=L7!_4r24H_UZ0)#ThvxMLRdSBP?Q{PZqKhiYKUd|@Lw6XOK!2_=Ra|##K@U}w zQqy>;#NB?mtqcXB3_CTdNO1=TcTY9d!XHYE~q*UWuU-9=L;Z&^or+Zm0$F=o^pC}s^YB`E-=#$|-{HHXZ?Mo5PmgjDn#YGiNBz?X-r1q~Ii zRWs8}m}bq!%dahALxz}%(jF5|&oVtSiEG0wQkb#)@M4w|j~~Qm;_hJ+dJdb}t$2(b zZV`4Msi&D$C16j&d~E9x*}VibCCPV9;y`% zz;?QtC=Z>kVt-qL%HkxWQWoQBzL#7YSI)nM0KPfBmOZl+HUT%;ER(cB%C)32`D^Y$4*ruZiP7< z_*L3$#rgA1!W6^~H?Ut<;mP3|O22!!LAXi32|GuM*^le7cQ_uO)X%_oM?|)!0V5Y? zqp&FwuZ%2W8IL_!-W^%OKA&8cU0B3M8}Do5!U6YPFw+r$QY8U4oN{Yy)QGf2E#BYY3* zF0UmQ{g+$Whiec!S}$xv>*yNR(2DL>E8Z9t=_TvlXf<0eV_2R;RhWrQW7FZg5+@wT z<}1tD9XE!rRI(%ms**;bW@7PJGV#(fRx2#Q?y(iDeLY6j&&DTXA~Uzc*FFcoyJEse ze$(`cY7caEW8KyHc>AiEZ4ThmtJ!(~`6@Sg^yfeMuNONEU!9GlYi8E83yZF$#n`hV KB1(1}CjSCSl*^I; diff --git a/scripts/build-cloudflare-canonical-mdi-seed.php b/scripts/build-cloudflare-canonical-mdi-seed.php index b841adb8..27f28cb7 100644 --- a/scripts/build-cloudflare-canonical-mdi-seed.php +++ b/scripts/build-cloudflare-canonical-mdi-seed.php @@ -56,6 +56,103 @@ function cloudflare_seed_files( string $root ): array { $output_zip = $assets . '/markdown-database-integration-canonical-seed.zip'; $output_manifest = $assets . '/markdown-database-integration-canonical-seed.json'; $root = sys_get_temp_dir() . '/wp-codebox-canonical-mdi-' . getmypid() . '-' . bin2hex( random_bytes( 4 ) ); +$front_page_content = <<<'HTML' + +