diff --git a/.gitignore b/.gitignore index a989c721f5..5c72a2c597 100644 --- a/.gitignore +++ b/.gitignore @@ -146,3 +146,6 @@ bin/studio-cli.exe scripts/eval/output/ scripts/eval/test-sites/ .promptfoo/ + +# Vendored html-to-blocks engine deps (symlinked locally) +apps/cli/ai/html-to-blocks-engine/node_modules diff --git a/apps/cli/ai/html-to-blocks-engine/content/model.mjs b/apps/cli/ai/html-to-blocks-engine/content/model.mjs new file mode 100644 index 0000000000..c4f21adb2c --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/content/model.mjs @@ -0,0 +1,751 @@ +// tools/content/model.mjs — validate and apply agent-authored WordPress content models. +import fs from 'node:fs'; +import path from 'node:path'; +import { resolvePath, resolveWorkspacePath, readJson, writeFile, writeJson, slug, titleCase } from '../lib/workspace.mjs'; + +const POST_TYPE_MAX = 20; +const TAXONOMY_MAX = 32; +const MODEL_TYPES = new Set(['content', 'submission']); +const FIELD_TYPES = new Set(['string', 'boolean', 'integer', 'number', 'array', 'object']); +const RESERVED_POST_TYPES = new Set(['post', 'page', 'attachment', 'revision', 'nav_menu_item', 'wp_block', 'wp_template', 'wp_template_part']); +const RESERVED_TAXONOMIES = new Set(['category', 'post_tag', 'nav_menu', 'link_category', 'post_format']); + +export function validateContentModel(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const modelPath = resolveWorkspacePath(workspaceRoot, args.modelPath || 'content-model/content-model.json'); + const reportPath = resolveWorkspacePath(workspaceRoot, args.reportPath || 'reports/content-model-validation.json'); + const raw = readJson(modelPath); + const { model, errors, warnings } = normalizeContentModel(raw); + const report = { + valid: errors.length === 0, + modelPath, + reportPath, + errors, + warnings, + counts: { + postTypes: model.postTypes.length, + taxonomies: model.taxonomies.length, + metaFields: model.postTypes.reduce((sum, type) => sum + type.meta.length, 0), + seedEntries: model.postTypes.reduce((sum, type) => sum + type.seed.length, 0), + }, + }; + writeJson(reportPath, report); + return report; +} + +export function scaffoldContentModelPlugin(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const modelPath = resolveWorkspacePath(workspaceRoot, args.modelPath || 'content-model/content-model.json'); + const report = validateContentModel(args); + if (!report.valid) { + throw new Error(`Content model is invalid. See ${report.reportPath}`); + } + + const { model } = normalizeContentModel(readJson(modelPath)); + const outDir = resolveWorkspacePath(workspaceRoot, args.outDir || 'content-model/plugin'); + const pluginSlug = model.plugin.slug; + const pluginRoot = path.join(outDir, pluginSlug); + const pluginFile = path.join(pluginRoot, `${pluginSlug}.php`); + fs.mkdirSync(pluginRoot, { recursive: true }); + fs.mkdirSync(path.join(pluginRoot, 'content'), { recursive: true }); + const pluginModel = writeSeedPayloads(model, pluginRoot); + writeJson(path.join(pluginRoot, 'content-model.json'), pluginModel); + writeJson(path.join(pluginRoot, 'content/manifest.json'), { + model: pluginModel.plugin, + postTypes: pluginModel.postTypes.map((type) => ({ + slug: type.slug, + kind: type.kind, + singular: type.singular, + plural: type.plural, + seed: type.seed.map(({ content, ...entry }) => entry), + })), + taxonomies: pluginModel.taxonomies, + }); + writeFile(pluginFile, contentModelPluginPhp(pluginModel)); + writeJson(path.join(workspaceRoot, 'content-model/plugin-manifest.json'), { + plugin: model.plugin, + sourceModel: path.relative(workspaceRoot, modelPath), + pluginRoot: path.relative(workspaceRoot, pluginRoot), + pluginFile: path.relative(workspaceRoot, pluginFile), + generatedAt: new Date().toISOString(), + }); + + return { + modelPath, + validationReport: report.reportPath, + pluginRoot, + pluginFile, + postTypes: model.postTypes.map((type) => type.slug), + taxonomies: model.taxonomies.map((taxonomy) => taxonomy.slug), + next: 'Install and activate this plugin in WordPress. Activation registers the model and flushes rewrites. Use Tools > Content model to import or remove generated seed content.', + }; +} + +function writeSeedPayloads(model, pluginRoot) { + const pluginModel = JSON.parse(JSON.stringify(model)); + for (const type of pluginModel.postTypes) { + for (const entry of type.seed) { + const content = String(entry.content || ''); + delete entry.content; + if (!content.trim()) continue; + const rel = `content/seeds/${type.slug}/${entry.slug}.html`; + writeFile(path.join(pluginRoot, rel), `${content.trim()}\n`); + entry.contentFile = rel; + } + } + return pluginModel; +} + +export function normalizeContentModel(raw) { + const model = JSON.parse(JSON.stringify(raw || {})); + const errors = []; + const warnings = []; + + model.version = model.version || 1; + model.plugin = model.plugin || {}; + const rawPluginSlug = String(model.plugin.slug || model.namespace || '').trim(); + model.plugin.slug = slug(rawPluginSlug); + model.plugin.name = cleanLabel(model.plugin.name || titleCase(model.plugin.slug || 'Content Model')); + model.plugin.description = cleanLabel(model.plugin.description || `Registers the ${model.plugin.name} WordPress content model.`); + model.plugin.textDomain = slug(model.plugin.textDomain || model.plugin.slug); + model.plugin.restNamespace = slug(model.plugin.restNamespace || model.plugin.slug || 'content-model').replace(/-/g, '_'); + model.postTypes = Array.isArray(model.postTypes) ? model.postTypes.map(normalizePostType) : []; + model.taxonomies = Array.isArray(model.taxonomies) ? model.taxonomies.map(normalizeTaxonomy) : []; + + if (!model.plugin.slug) errors.push('plugin.slug is required.'); + if (rawPluginSlug && rawPluginSlug !== model.plugin.slug) errors.push(`plugin.slug must already be lowercase kebab-case: ${rawPluginSlug}`); + if (!/^[a-z0-9][a-z0-9-]*$/.test(model.plugin.slug || '')) errors.push(`plugin.slug must be lowercase kebab-case: ${model.plugin.slug || '(empty)'}`); + if (!model.plugin.name) errors.push('plugin.name is required.'); + if (!model.postTypes.length && !model.taxonomies.length) warnings.push('Model has no postTypes or taxonomies.'); + + const postTypeSlugs = new Set(); + for (const type of model.postTypes) { + if (type.__rawSlug && type.__rawSlug !== type.slug) errors.push(`Post type slug must already be lowercase slug form: ${type.__rawSlug}`); + validateSlug(type.slug, 'post type', POST_TYPE_MAX, errors); + if (RESERVED_POST_TYPES.has(type.slug)) errors.push(`Post type slug is reserved by WordPress: ${type.slug}`); + if (postTypeSlugs.has(type.slug)) errors.push(`Duplicate post type slug: ${type.slug}`); + postTypeSlugs.add(type.slug); + if (!MODEL_TYPES.has(type.kind)) errors.push(`Post type ${type.slug} kind must be "content" or "submission".`); + validateFields(type.slug, type.meta, errors); + validateFields(type.slug, type.formFields, errors, 'formFields'); + validateSeed(type, errors, warnings); + if (type.kind === 'content' && !type.seed.length) warnings.push(`Content post type ${type.slug} has no seed entries.`); + if (type.kind === 'submission' && !type.formFields.length && !type.meta.length) warnings.push(`Submission post type ${type.slug} has no formFields or meta fields.`); + } + + const taxonomySlugs = new Set(); + for (const taxonomy of model.taxonomies) { + if (taxonomy.__rawSlug && taxonomy.__rawSlug !== taxonomy.slug) errors.push(`Taxonomy slug must already be lowercase slug form: ${taxonomy.__rawSlug}`); + validateSlug(taxonomy.slug, 'taxonomy', TAXONOMY_MAX, errors); + if (RESERVED_TAXONOMIES.has(taxonomy.slug)) errors.push(`Taxonomy slug is reserved by WordPress: ${taxonomy.slug}`); + if (taxonomySlugs.has(taxonomy.slug)) errors.push(`Duplicate taxonomy slug: ${taxonomy.slug}`); + taxonomySlugs.add(taxonomy.slug); + for (const typeSlug of taxonomy.postTypes) { + if (!postTypeSlugs.has(typeSlug)) errors.push(`Taxonomy ${taxonomy.slug} references unknown post type: ${typeSlug}`); + } + } + + for (const type of model.postTypes) { + for (const taxonomySlug of type.taxonomies) { + if (!taxonomySlugs.has(taxonomySlug)) errors.push(`Post type ${type.slug} references unknown taxonomy: ${taxonomySlug}`); + } + } + + for (const type of model.postTypes) delete type.__rawSlug; + for (const taxonomy of model.taxonomies) delete taxonomy.__rawSlug; + + return { model, errors, warnings }; +} + +function normalizePostType(input) { + const type = { ...input }; + type.__rawSlug = String(type.slug || type.name || '').trim(); + type.slug = wpKeySlug(type.__rawSlug); + type.kind = type.kind || 'content'; + type.singular = cleanLabel(type.singular || titleCase(type.slug)); + type.plural = cleanLabel(type.plural || pluralize(type.singular)); + type.menuName = cleanLabel(type.menuName || type.plural); + type.description = cleanLabel(type.description || ''); + type.public = type.public ?? type.kind === 'content'; + type.showUi = type.showUi ?? true; + type.showInRest = type.showInRest ?? true; + type.restBase = slug(type.restBase || type.rewriteSlug || type.hasArchive || type.slug).replace(/_/g, '-'); + type.rewriteSlug = slug(type.rewriteSlug || type.hasArchive || type.restBase || type.slug); + type.hasArchive = type.kind === 'content' ? (type.hasArchive ?? type.rewriteSlug) : false; + type.menuIcon = type.menuIcon || (type.kind === 'submission' ? 'dashicons-feedback' : 'dashicons-admin-post'); + type.supports = Array.isArray(type.supports) && type.supports.length + ? type.supports + : type.kind === 'submission' + ? ['title', 'editor', 'custom-fields'] + : ['title', 'editor', 'thumbnail', 'excerpt', 'custom-fields']; + type.meta = normalizeFields(type.meta || type.fields || []); + type.formFields = normalizeFields(type.formFields || []); + type.taxonomies = Array.isArray(type.taxonomies) ? type.taxonomies.map(wpKeySlug).filter(Boolean) : []; + type.seed = Array.isArray(type.seed) ? type.seed.map(normalizeSeedEntry) : []; + return type; +} + +function normalizeTaxonomy(input) { + const taxonomy = { ...input }; + taxonomy.__rawSlug = String(taxonomy.slug || taxonomy.name || '').trim(); + taxonomy.slug = wpKeySlug(taxonomy.__rawSlug); + taxonomy.singular = cleanLabel(taxonomy.singular || titleCase(taxonomy.slug)); + taxonomy.plural = cleanLabel(taxonomy.plural || pluralize(taxonomy.singular)); + taxonomy.description = cleanLabel(taxonomy.description || ''); + taxonomy.hierarchical = taxonomy.hierarchical ?? true; + taxonomy.public = taxonomy.public ?? true; + taxonomy.showInRest = taxonomy.showInRest ?? true; + taxonomy.restBase = slug(taxonomy.restBase || taxonomy.slug).replace(/_/g, '-'); + taxonomy.rewriteSlug = slug(taxonomy.rewriteSlug || taxonomy.restBase || taxonomy.slug); + taxonomy.postTypes = Array.isArray(taxonomy.postTypes) ? taxonomy.postTypes.map(wpKeySlug).filter(Boolean) : []; + taxonomy.terms = Array.isArray(taxonomy.terms) ? taxonomy.terms.map(normalizeTerm) : []; + return taxonomy; +} + +function normalizeFields(fields) { + return fields.map((field) => { + const key = String(field.key || field.name || '').trim(); + return { + ...field, + key, + label: cleanLabel(field.label || titleCase(key)), + type: field.type || 'string', + format: field.format || '', + single: field.single ?? true, + required: Boolean(field.required), + description: cleanLabel(field.description || ''), + }; + }); +} + +function normalizeSeedEntry(entry) { + const normalized = { ...entry }; + normalized.slug = slug(normalized.slug || normalized.title || ''); + normalized.title = cleanLabel(normalized.title || titleCase(normalized.slug)); + normalized.status = normalized.status || 'publish'; + normalized.content = String(normalized.content || ''); + normalized.excerpt = String(normalized.excerpt || ''); + normalized.meta = normalized.meta && typeof normalized.meta === 'object' ? normalized.meta : {}; + normalized.terms = normalized.terms && typeof normalized.terms === 'object' ? normalized.terms : {}; + return normalized; +} + +function normalizeTerm(term) { + const value = typeof term === 'string' ? { name: term } : { ...term }; + value.slug = slug(value.slug || value.name || ''); + value.name = cleanLabel(value.name || titleCase(value.slug)); + value.description = cleanLabel(value.description || ''); + return value; +} + +function validateSlug(value, kind, max, errors) { + if (!value) { + errors.push(`${kind} slug is required.`); + return; + } + if (value.length > max) errors.push(`${kind} slug "${value}" is ${value.length} chars; WordPress max is ${max}.`); + if (!/^[a-z0-9][a-z0-9_-]*$/.test(value)) errors.push(`${kind} slug must use lowercase letters, numbers, hyphens, or underscores: ${value}`); +} + +function validateFields(typeSlug, fields, errors, label = 'meta') { + const keys = new Set(); + for (const field of fields) { + if (!field.key) errors.push(`${typeSlug}.${label} field key is required.`); + if (!/^[A-Za-z0-9_][A-Za-z0-9_-]*$/.test(field.key || '')) errors.push(`${typeSlug}.${label} key must be alphanumeric/underscore/dash: ${field.key}`); + if (keys.has(field.key)) errors.push(`Duplicate ${label} key on ${typeSlug}: ${field.key}`); + keys.add(field.key); + if (!FIELD_TYPES.has(field.type)) errors.push(`${typeSlug}.${label}.${field.key} type must be one of ${[...FIELD_TYPES].join(', ')}.`); + } +} + +function validateSeed(type, errors, warnings) { + const metaKeys = new Set(type.meta.map((field) => field.key)); + const taxonomySet = new Set(type.taxonomies); + const slugs = new Set(); + for (const entry of type.seed) { + if (!entry.slug) errors.push(`Seed entry in ${type.slug} is missing slug.`); + if (slugs.has(entry.slug)) errors.push(`Duplicate seed slug in ${type.slug}: ${entry.slug}`); + slugs.add(entry.slug); + for (const key of Object.keys(entry.meta || {})) { + if (!metaKeys.has(key)) warnings.push(`Seed ${type.slug}/${entry.slug} sets undeclared meta key: ${key}`); + } + for (const taxonomySlug of Object.keys(entry.terms || {})) { + if (!taxonomySet.has(taxonomySlug)) warnings.push(`Seed ${type.slug}/${entry.slug} sets terms for taxonomy not attached to post type: ${taxonomySlug}`); + } + } +} + +function contentModelPluginPhp(model) { + const prefix = phpIdentifier(model.plugin.slug); + return ` array(), 'taxonomies' => array()); +} + +function ${prefix}_labels($plural, $singular, $menu_name = '') { + $menu_name = $menu_name ?: $plural; + return array( + 'name' => $plural, + 'singular_name' => $singular, + 'menu_name' => $menu_name, + 'add_new_item' => sprintf('Add New %s', $singular), + 'edit_item' => sprintf('Edit %s', $singular), + 'new_item' => sprintf('New %s', $singular), + 'view_item' => sprintf('View %s', $singular), + 'search_items' => sprintf('Search %s', $plural), + 'not_found' => sprintf('No %s found', strtolower($plural)), + ); +} + +function ${prefix}_register_content_model() { + $model = ${prefix}_model(); + + foreach (($model['taxonomies'] ?? array()) as $taxonomy) { + register_taxonomy($taxonomy['slug'], $taxonomy['postTypes'] ?? array(), array( + 'labels' => ${prefix}_labels($taxonomy['plural'], $taxonomy['singular']), + 'description' => $taxonomy['description'] ?? '', + 'hierarchical' => !empty($taxonomy['hierarchical']), + 'public' => array_key_exists('public', $taxonomy) ? (bool) $taxonomy['public'] : true, + 'show_ui' => true, + 'show_admin_column' => true, + 'show_in_rest' => array_key_exists('showInRest', $taxonomy) ? (bool) $taxonomy['showInRest'] : true, + 'rest_base' => $taxonomy['restBase'] ?? $taxonomy['slug'], + 'rewrite' => array('slug' => $taxonomy['rewriteSlug'] ?? $taxonomy['slug']), + )); + } + + foreach (($model['postTypes'] ?? array()) as $type) { + $is_submission = ($type['kind'] ?? 'content') === 'submission'; + $public = array_key_exists('public', $type) ? (bool) $type['public'] : !$is_submission; + register_post_type($type['slug'], array( + 'label' => $type['plural'], + 'labels' => ${prefix}_labels($type['plural'], $type['singular'], $type['menuName'] ?? ''), + 'description' => $type['description'] ?? '', + 'public' => $public, + 'show_ui' => array_key_exists('showUi', $type) ? (bool) $type['showUi'] : true, + 'show_in_menu' => true, + 'show_in_rest' => array_key_exists('showInRest', $type) ? (bool) $type['showInRest'] : true, + 'rest_base' => $type['restBase'] ?? $type['slug'], + 'has_archive' => $is_submission ? false : ($type['hasArchive'] ?? false), + 'publicly_queryable' => $is_submission ? false : $public, + 'exclude_from_search' => $is_submission, + 'menu_icon' => $type['menuIcon'] ?? 'dashicons-admin-post', + 'supports' => $type['supports'] ?? array('title', 'editor', 'custom-fields'), + 'taxonomies' => $type['taxonomies'] ?? array(), + 'rewrite' => $is_submission ? false : array('slug' => $type['rewriteSlug'] ?? $type['slug']), + 'map_meta_cap' => true, + )); + + foreach (($type['meta'] ?? array()) as $field) { + register_post_meta($type['slug'], $field['key'], array( + 'type' => $field['type'] ?? 'string', + 'single' => array_key_exists('single', $field) ? (bool) $field['single'] : true, + 'show_in_rest' => ${prefix}_rest_schema($field), + 'sanitize_callback' => function ($value) use ($field) { + return ${prefix}_sanitize_value($value, $field); + }, + )); + } + } +} +add_action('init', '${prefix}_register_content_model'); + +function ${prefix}_rest_schema($field) { + if (($field['type'] ?? '') === 'array' && isset($field['items'])) { + return array('schema' => array('type' => 'array', 'items' => $field['items'])); + } + if (($field['type'] ?? '') === 'object' && isset($field['properties'])) { + return array('schema' => array('type' => 'object', 'properties' => $field['properties'])); + } + return array_key_exists('showInRest', $field) ? (bool) $field['showInRest'] : true; +} + +function ${prefix}_sanitize_value($value, $field) { + $format = $field['format'] ?? ''; + switch ($field['type'] ?? 'string') { + case 'boolean': + return rest_sanitize_boolean($value); + case 'integer': + return intval($value); + case 'number': + return is_numeric($value) ? (float) $value : 0; + case 'array': + return is_array($value) ? array_map('sanitize_text_field', $value) : array(); + case 'object': + return is_array($value) ? map_deep($value, 'sanitize_text_field') : array(); + case 'string': + default: + if ($format === 'email') return sanitize_email($value); + if ($format === 'url') return esc_url_raw($value); + if ($format === 'textarea') return sanitize_textarea_field($value); + return sanitize_text_field($value); + } +} + +function ${prefix}_submission_fields($type) { + return !empty($type['formFields']) ? $type['formFields'] : ($type['meta'] ?? array()); +} + +function ${prefix}_register_submission_routes() { + $model = ${prefix}_model(); + $namespace = str_replace('_', '-', $model['plugin']['restNamespace'] ?? '${model.plugin.restNamespace}') . '/v1'; + foreach (($model['postTypes'] ?? array()) as $type) { + if (($type['kind'] ?? 'content') !== 'submission') { + continue; + } + $route = '/' . ($type['restRoute'] ?? $type['restBase'] ?? $type['slug']); + $args = array(); + foreach (${prefix}_submission_fields($type) as $field) { + $args[$field['key']] = array( + 'required' => !empty($field['required']), + 'type' => $field['type'] ?? 'string', + 'sanitize_callback' => function ($value) use ($field) { + return ${prefix}_sanitize_value($value, $field); + }, + ); + } + register_rest_route($namespace, $route, array( + 'methods' => 'POST', + 'callback' => function ($request) use ($type) { + return ${prefix}_handle_submission($type, $request); + }, + 'permission_callback' => '__return_true', + 'args' => $args, + )); + } +} +add_action('rest_api_init', '${prefix}_register_submission_routes'); + +function ${prefix}_handle_submission($type, $request) { + $params = $request->get_params(); + $meta = array(); + foreach (($type['meta'] ?? array()) as $field) { + if (array_key_exists($field['key'], $params)) { + $meta[$field['key']] = ${prefix}_sanitize_value($params[$field['key']], $field); + } + } + $title = ${prefix}_submission_title($type, $params); + $content = ${prefix}_submission_content($params); + $post_id = wp_insert_post(array( + 'post_type' => $type['slug'], + 'post_status' => 'publish', + 'post_title' => $title, + 'post_content' => $content, + 'meta_input' => $meta, + ), true); + if (is_wp_error($post_id)) { + return new WP_Error('insert_failed', 'Could not save submission.', array('status' => 500)); + } + return rest_ensure_response(array('ok' => true, 'id' => $post_id)); +} + +function ${prefix}_submission_title($type, $params) { + foreach (array('name', 'full_name', 'email', 'title') as $key) { + if (!empty($params[$key])) { + return sanitize_text_field($params[$key]) . ' — ' . current_time('Y-m-d H:i'); + } + } + return ($type['singular'] ?? 'Submission') . ' — ' . current_time('Y-m-d H:i'); +} + +function ${prefix}_submission_content($params) { + foreach (array('message', 'notes', 'details', 'story', 'content') as $key) { + if (!empty($params[$key])) { + return sanitize_textarea_field($params[$key]); + } + } + return wp_json_encode($params, JSON_PRETTY_PRINT); +} + +function ${prefix}_seed_state() { + $state = get_option(${prefix.toUpperCase()}_SEED_OPTION, array('posts' => array(), 'terms' => array())); + if (!is_array($state)) $state = array(); + if (!isset($state['posts']) || !is_array($state['posts'])) $state['posts'] = array(); + if (!isset($state['terms']) || !is_array($state['terms'])) $state['terms'] = array(); + return $state; +} + +function ${prefix}_seed_id($type, $entry) { + return $type['slug'] . ':' . ($entry['seedId'] ?? $entry['slug']); +} + +function ${prefix}_import_seed_terms(&$state) { + $results = array(); + foreach ((${prefix}_model()['taxonomies'] ?? array()) as $taxonomy) { + foreach (($taxonomy['terms'] ?? array()) as $term) { + $seed_id = $taxonomy['slug'] . ':' . $term['slug']; + $existing = term_exists($term['slug'], $taxonomy['slug']); + if ($existing) { + $results[$seed_id] = array('status' => 'already-exists', 'term_id' => is_array($existing) ? (int) $existing['term_id'] : (int) $existing); + continue; + } + $created = wp_insert_term($term['name'], $taxonomy['slug'], array( + 'slug' => $term['slug'], + 'description' => $term['description'] ?? '', + )); + if (is_wp_error($created)) { + $results[$seed_id] = array('status' => 'error: ' . $created->get_error_message(), 'term_id' => 0); + continue; + } + $term_id = (int) $created['term_id']; + $state['terms'][$seed_id] = array('term_id' => $term_id, 'taxonomy' => $taxonomy['slug'], 'imported_at' => time()); + $results[$seed_id] = array('status' => 'imported', 'term_id' => $term_id); + } + } + return $results; +} + +function ${prefix}_seed_entry_content($entry) { + $content = $entry['content'] ?? ''; + if (!empty($entry['contentFile'])) { + $file = __DIR__ . '/' . ltrim($entry['contentFile'], '/'); + if (is_readable($file)) { + $content = file_get_contents($file); + } + } + return str_replace('{{THEME_URI}}', get_stylesheet_directory_uri(), $content); +} + +function ${prefix}_import_seed_posts(&$state) { + $results = array(); + foreach ((${prefix}_model()['postTypes'] ?? array()) as $type) { + foreach (($type['seed'] ?? array()) as $entry) { + $seed_id = ${prefix}_seed_id($type, $entry); + if (isset($state['posts'][$seed_id]) && get_post($state['posts'][$seed_id]['post_id'])) { + $results[$seed_id] = array('status' => 'already-imported', 'post_id' => $state['posts'][$seed_id]['post_id']); + continue; + } + $generated = get_posts(array( + 'post_type' => $type['slug'], + 'post_status' => 'any', + 'meta_key' => ${prefix.toUpperCase()}_SEED_META, + 'meta_value' => $seed_id, + 'posts_per_page' => 1, + 'fields' => 'ids', + )); + if ($generated) { + $state['posts'][$seed_id] = array('post_id' => (int) $generated[0], 'post_type' => $type['slug'], 'slug' => $entry['slug'], 'imported_at' => time()); + $results[$seed_id] = array('status' => 'already-imported', 'post_id' => (int) $generated[0]); + continue; + } + $collision = get_page_by_path($entry['slug'], OBJECT, $type['slug']); + if ($collision) { + $collision_seed_id = get_post_meta($collision->ID, ${prefix.toUpperCase()}_SEED_META, true); + if ($collision_seed_id === $seed_id) { + $state['posts'][$seed_id] = array('post_id' => $collision->ID, 'post_type' => $type['slug'], 'slug' => $entry['slug'], 'imported_at' => time()); + $results[$seed_id] = array('status' => 'already-imported', 'post_id' => $collision->ID); + continue; + } + $results[$seed_id] = array('status' => 'slug-collision', 'post_id' => $collision->ID); + continue; + } + $meta = $entry['meta'] ?? array(); + $meta[${prefix.toUpperCase()}_SEED_META] = $seed_id; + $post_id = wp_insert_post(array( + 'post_type' => $type['slug'], + 'post_status' => $entry['status'] ?? 'publish', + 'post_title' => wp_slash($entry['title']), + 'post_name' => $entry['slug'], + 'post_content' => wp_slash(${prefix}_seed_entry_content($entry)), + 'post_excerpt' => wp_slash($entry['excerpt'] ?? ''), + 'meta_input' => $meta, + ), true); + if (is_wp_error($post_id)) { + $results[$seed_id] = array('status' => 'error: ' . $post_id->get_error_message(), 'post_id' => 0); + continue; + } + foreach (($entry['terms'] ?? array()) as $taxonomy_slug => $terms) { + wp_set_object_terms($post_id, array_values((array) $terms), $taxonomy_slug, false); + } + $state['posts'][$seed_id] = array('post_id' => $post_id, 'post_type' => $type['slug'], 'slug' => $entry['slug'], 'imported_at' => time()); + $results[$seed_id] = array('status' => 'imported', 'post_id' => $post_id); + } + } + return $results; +} + +function ${prefix}_import_seed_content() { + ${prefix}_register_content_model(); + $state = ${prefix}_seed_state(); + $results = array( + 'terms' => ${prefix}_import_seed_terms($state), + 'posts' => ${prefix}_import_seed_posts($state), + ); + update_option(${prefix.toUpperCase()}_SEED_OPTION, $state); + return $results; +} + +function ${prefix}_remove_seed_content() { + $state = ${prefix}_seed_state(); + foreach ($state['posts'] as $seed_id => $entry) { + $post = get_post($entry['post_id'] ?? 0); + if ($post && get_post_meta($post->ID, ${prefix.toUpperCase()}_SEED_META, true) === $seed_id) { + wp_delete_post($post->ID, true); + } + unset($state['posts'][$seed_id]); + } + foreach ((${prefix}_model()['postTypes'] ?? array()) as $type) { + $posts = get_posts(array( + 'post_type' => $type['slug'], + 'post_status' => 'any', + 'meta_key' => ${prefix.toUpperCase()}_SEED_META, + 'posts_per_page' => -1, + 'fields' => 'ids', + )); + foreach ($posts as $post_id) { + wp_delete_post($post_id, true); + } + } + foreach ($state['terms'] as $seed_id => $entry) { + $taxonomy = $entry['taxonomy'] ?? ''; + $term = get_term((int) ($entry['term_id'] ?? 0), $taxonomy); + if ($term && !is_wp_error($term) && (int) $term->count === 0) { + wp_delete_term($term->term_id, $taxonomy); + } + unset($state['terms'][$seed_id]); + } + update_option(${prefix.toUpperCase()}_SEED_OPTION, $state); +} + +function ${prefix}_seed_post_status($type, $entry, $state) { + $seed_id = ${prefix}_seed_id($type, $entry); + if (isset($state['posts'][$seed_id])) { + $tracked = $state['posts'][$seed_id]; + $post = get_post($tracked['post_id'] ?? 0); + if ($post && get_post_meta($post->ID, ${prefix.toUpperCase()}_SEED_META, true) === $seed_id) { + if (strtotime($post->post_modified_gmt) > (int) ($tracked['imported_at'] ?? 0) + 5) return 'modified since import'; + return 'imported'; + } + } + $generated = get_posts(array( + 'post_type' => $type['slug'], + 'post_status' => 'any', + 'meta_key' => ${prefix.toUpperCase()}_SEED_META, + 'meta_value' => $seed_id, + 'posts_per_page' => 1, + 'fields' => 'ids', + )); + if ($generated) return 'imported'; + $collision = get_page_by_path($entry['slug'], OBJECT, $type['slug']); + if ($collision) return 'slug collision'; + return 'not imported'; +} + +function ${prefix}_seed_term_status($taxonomy, $term, $state) { + $seed_id = $taxonomy['slug'] . ':' . $term['slug']; + if (isset($state['terms'][$seed_id]) && term_exists((int) $state['terms'][$seed_id]['term_id'], $taxonomy['slug'])) return 'imported'; + if (term_exists($term['slug'], $taxonomy['slug'])) return 'already exists'; + return 'not imported'; +} + +function ${prefix}_activate() { + ${prefix}_register_content_model(); + flush_rewrite_rules(); +} +register_activation_hook(__FILE__, '${prefix}_activate'); + +register_deactivation_hook(__FILE__, function () { + flush_rewrite_rules(); +}); + +add_action('admin_menu', function () { + add_management_page( + '${jsString(model.plugin.name)}', + '${jsString(model.plugin.name)}', + 'manage_options', + '${model.plugin.slug}', + '${prefix}_admin_page' + ); +}); + +function ${prefix}_admin_page() { + if (!current_user_can('manage_options')) return; + if (isset($_POST['${prefix}_action']) && wp_verify_nonce($_POST['_wpnonce'] ?? '', '${prefix}')) { + if ($_POST['${prefix}_action'] === 'import') { + ${prefix}_import_seed_content(); + echo '

Seed content imported.

'; + } + if ($_POST['${prefix}_action'] === 'remove') { + ${prefix}_remove_seed_content(); + echo '

Generated seed content removed.

'; + } + } + $model = ${prefix}_model(); + $state = ${prefix}_seed_state(); + echo '

' . esc_html($model['plugin']['name'] ?? 'Content model') . '

'; + echo '

This plugin registers the content model while active. Seed content is imported only when requested.

'; + echo '

Post type seeds

'; + foreach (($model['postTypes'] ?? array()) as $type) { + foreach (($type['seed'] ?? array()) as $entry) { + echo ''; + } + } + echo '
TypeKindSeedSlugStatus
' . esc_html($type['slug']) . '' . esc_html($type['kind'] ?? 'content') . '' . esc_html($entry['title'] ?? $entry['slug']) . '' . esc_html($entry['slug']) . '' . esc_html(${prefix}_seed_post_status($type, $entry, $state)) . '

Taxonomy seeds

'; + foreach (($model['taxonomies'] ?? array()) as $taxonomy) { + foreach (($taxonomy['terms'] ?? array()) as $term) { + echo ''; + } + } + echo '
TaxonomyTermSlugStatus
' . esc_html($taxonomy['slug']) . '' . esc_html($term['name']) . '' . esc_html($term['slug']) . '' . esc_html(${prefix}_seed_term_status($taxonomy, $term, $state)) . '
'; + wp_nonce_field('${prefix}'); + echo ' '; + echo ''; + echo '
'; +} +`; +} + +function cleanLabel(value) { + return String(value || '').replace(/\s+/g, ' ').trim(); +} + +function pluralize(value) { + const label = cleanLabel(value); + if (!label) return ''; + if (/s$/i.test(label)) return label; + return `${label}s`; +} + +function phpIdentifier(value) { + let id = slug(value).replace(/-/g, '_'); + if (!/^[A-Za-z_]/.test(id)) id = `wpcm_${id}`; + return id || 'wpcm_content_model'; +} + +function wpKeySlug(value) { + return String(value || '') + .trim() + .toLowerCase() + .replace(/[^a-z0-9_-]+/g, '_') + .replace(/^[-_]+|[-_]+$/g, ''); +} + +function headerValue(value) { + return cleanLabel(value).replace(/\*\//g, '* /'); +} + +function jsString(value) { + return String(value || '').replace(/\\/g, '\\\\').replace(/'/g, "\\'"); +} diff --git a/apps/cli/ai/html-to-blocks-engine/content/standins.mjs b/apps/cli/ai/html-to-blocks-engine/content/standins.mjs new file mode 100644 index 0000000000..077ce76514 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/content/standins.mjs @@ -0,0 +1,237 @@ +// tools/content/standins.mjs — stand-in marking, audit, and hydration. +// +// Some design regions are genuinely data-driven: an object grid is a query over +// a CPT, a journal index is a query over posts, a comment thread is core/comments. +// html-to-blocks cannot render those from real data (there is no data yet), and +// the visual gate needs to SEE them to style them. So the agent builds them as +// static stand-ins — a real core-block composition (group + image + heading + +// paragraph) seeded with representative content — and marks them. The mark rides +// in the block's own `metadata.standin` (a WordPress-supported attribute that +// serializes and round-trips), so it travels with the block and never pollutes +// layout. +// +// Two marks: +// - container: { for: "core/query", postType, taxonomy?, query?, role? } +// The container's FIRST inner block is the item template. +// - field (inside the template): { for: "core/post-title" | "core/post-featured-image" +// | "core/post-terms" | "core/post-excerpt" | "core/post-date", taxonomy?, isLink? } +// - comments: { for: "core/comments", role? } +// +// audit_standins lists every mark (so the content modeler and the skill gate can +// see what is still static). hydrate_standins swaps each marked region into the +// real dynamic core blocks AFTER the html-to-blocks visual gate has passed, +// preserving className/style so the lifted theme CSS still applies. The result +// feeds blocks-to-theme, whose playground gate renders it against the seeded site. + +const FIELD_TARGETS = new Set([ + 'core/post-title', + 'core/post-featured-image', + 'core/post-terms', + 'core/post-excerpt', + 'core/post-date', +]); + +export function getStandin(block) { + return block && block.attrs && block.attrs.metadata && block.attrs.metadata.standin || null; +} + +function walk(blocks, path, visit) { + (blocks || []).forEach((block, index) => { + const here = path.concat(index); + visit(block, here); + walk(block.innerBlocks, here, visit); + }); +} + +// Flat inventory of every stand-in mark across the given pages. +export function auditStandins(pages) { + const standins = []; + for (const { page, tree } of pages) { + walk(tree.blocks, [], (block, path) => { + const mark = getStandin(block); + if (!mark || !mark.for) return; + const kind = mark.for === 'core/query' ? 'query' + : mark.for === 'core/comments' ? 'comments' + : FIELD_TARGETS.has(mark.for) ? 'field' : 'other'; + standins.push({ + page, + path, + kind, + for: mark.for, + postType: mark.postType || null, + taxonomy: mark.taxonomy || null, + role: mark.role || null, + blockName: block.blockName || block.name || null, + }); + }); + } + return standins; +} + +// Validate marks against a content model: every query stand-in's postType must +// exist (post is always valid); taxonomy marks must reference a real taxonomy. +export function checkStandins(standins, model) { + const postTypes = new Set(['post', 'page', ...(model?.postTypes || []).map((t) => t.slug)]); + const taxonomies = new Set([ + 'category', 'post_tag', + ...(model?.taxonomies || []).map((t) => t.slug), + ]); + const errors = []; + for (const s of standins) { + if (s.kind === 'query' && s.postType && !postTypes.has(s.postType)) { + errors.push(`Query stand-in on ${s.page} [${s.path.join('.')}] references unknown postType "${s.postType}".`); + } + if (s.taxonomy && !taxonomies.has(s.taxonomy)) { + errors.push(`Stand-in on ${s.page} [${s.path.join('.')}] references unknown taxonomy "${s.taxonomy}".`); + } + } + return errors; +} + +// Carry the styling-relevant attributes from a stand-in block onto its dynamic +// replacement so the theme CSS lifted from the stand-in still targets it. +function passthrough(attrs) { + const out = {}; + for (const key of ['className', 'style', 'align', 'layout', 'textColor', 'backgroundColor', 'fontSize', 'fontFamily']) { + if (attrs && attrs[key] !== undefined) out[key] = attrs[key]; + } + return out; +} + +function fieldBlock(mark, original) { + const attrs = passthrough(original.attrs || {}); + switch (mark.for) { + case 'core/post-title': + return { blockName: 'core/post-title', attrs: { isLink: mark.isLink !== false, ...attrs }, innerBlocks: [] }; + case 'core/post-featured-image': + return { blockName: 'core/post-featured-image', attrs: { isLink: mark.isLink !== false, ...attrs }, innerBlocks: [] }; + case 'core/post-terms': + return { blockName: 'core/post-terms', attrs: { term: mark.taxonomy || 'category', ...attrs }, innerBlocks: [] }; + case 'core/post-excerpt': + return { blockName: 'core/post-excerpt', attrs: { ...attrs }, innerBlocks: [] }; + case 'core/post-date': + return { blockName: 'core/post-date', attrs: { ...attrs }, innerBlocks: [] }; + default: + return null; + } +} + +// Replace field-marked blocks inside an item template with their core/post-* +// equivalents (depth-first; non-field blocks are kept as static decoration). +function hydrateTemplate(block) { + const mark = getStandin(block); + if (mark && FIELD_TARGETS.has(mark.for)) { + const replaced = fieldBlock(mark, block); + if (replaced) return replaced; + } + return { + ...block, + attrs: stripStandin(block.attrs), + innerBlocks: (block.innerBlocks || []).map(hydrateTemplate), + }; +} + +function stripStandin(attrs) { + if (!attrs || !attrs.metadata) return attrs; + const { standin, ...metadata } = attrs.metadata; + const next = { ...attrs }; + if (Object.keys(metadata).length) next.metadata = metadata; + else delete next.metadata; + return next; +} + +// Deterministic positive integer queryId from page + path (core/query needs one). +function queryId(page, path) { + let h = 0; + const key = `${page}:${path.join('.')}`; + for (let i = 0; i < key.length; i++) h = (h * 31 + key.charCodeAt(i)) >>> 0; + return h % 100000; +} + +function buildQueryBlock(container, page, path) { + const mark = getStandin(container); + const items = container.innerBlocks || []; + if (!items.length) throw new Error(`Query stand-in on ${page} [${path.join('.')}] has no item template.`); + const template = hydrateTemplate(items[0]); + const q = mark.query || {}; + const query = { + perPage: q.perPage ?? 6, + pages: q.pages ?? 0, + offset: q.offset ?? 0, + postType: mark.postType || 'post', + order: q.order || 'desc', + orderBy: q.orderBy || 'date', + author: '', + search: '', + exclude: [], + sticky: q.sticky || '', + inherit: q.inherit ?? false, + ...(mark.taxonomy && q.terms ? { taxQuery: { [mark.taxonomy]: q.terms } } : {}), + }; + const postTemplate = { + blockName: 'core/post-template', + attrs: passthrough(container.attrs || {}), + innerBlocks: [template], + }; + return { + blockName: 'core/query', + attrs: { + queryId: queryId(page, path), + query, + ...(container.attrs && container.attrs.align ? { align: container.attrs.align } : {}), + ...(mark.queryClassName ? { className: mark.queryClassName } : {}), + }, + innerBlocks: [postTemplate], + }; +} + +function commentsBlock(container) { + return { + blockName: 'core/comments', + attrs: passthrough(container.attrs || {}), + innerBlocks: [ + { blockName: 'core/comments-title', attrs: {}, innerBlocks: [] }, + { blockName: 'core/comment-template', attrs: {}, innerBlocks: [ + { blockName: 'core/comment-author-name', attrs: {}, innerBlocks: [] }, + { blockName: 'core/comment-date', attrs: {}, innerBlocks: [] }, + { blockName: 'core/comment-content', attrs: {}, innerBlocks: [] }, + { blockName: 'core/comment-reply-link', attrs: {}, innerBlocks: [] }, + ] }, + { blockName: 'core/comments-pagination', attrs: {}, innerBlocks: [ + { blockName: 'core/comments-pagination-previous', attrs: {}, innerBlocks: [] }, + { blockName: 'core/comments-pagination-next', attrs: {}, innerBlocks: [] }, + ] }, + { blockName: 'core/post-comments-form', attrs: {}, innerBlocks: [] }, + ], + }; +} + +function hydrateBlocks(blocks, page, path, swaps) { + return (blocks || []).map((block, index) => { + const here = path.concat(index); + const mark = getStandin(block); + if (mark && mark.for === 'core/query') { + swaps.push({ page, path: here, for: 'core/query', postType: mark.postType || 'post' }); + return buildQueryBlock(block, page, here); + } + if (mark && mark.for === 'core/comments') { + swaps.push({ page, path: here, for: 'core/comments' }); + return commentsBlock(block); + } + return { + ...block, + attrs: stripStandin(block.attrs), + innerBlocks: hydrateBlocks(block.innerBlocks, page, here, swaps), + }; + }); +} + +// Returns hydrated copies of each page tree plus the list of swaps performed. +export function hydrateStandins(pages) { + const swaps = []; + const trees = pages.map(({ page, tree }) => ({ + page, + tree: { ...tree, blocks: hydrateBlocks(tree.blocks, page, [], swaps) }, + })); + return { trees, swaps }; +} diff --git a/apps/cli/ai/html-to-blocks-engine/lib/capture.mjs b/apps/cli/ai/html-to-blocks-engine/lib/capture.mjs new file mode 100644 index 0000000000..8530dd8a42 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/lib/capture.mjs @@ -0,0 +1,425 @@ +// tools/lib/capture.mjs — Playwright capture + PNG comparison shared by both skills. +import fs from 'node:fs'; +import http from 'node:http'; +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { performance } from 'node:perf_hooks'; +import { isPathInside } from './workspace.mjs'; +import * as profile from './profile.mjs'; + +// P2: count chromium launches per process and the total ms spent launching, so +// the relaunch-per-call tax (one browser per tool call, no pooling) is visible +// in the profile. A future browser pool would drive this count toward 1. All of +// this is inert when profiling is off — the counters update but nothing flushes. +let _browserLaunchCount = 0; +let _browserLaunchTotalMs = 0; + +// Best-effort host extraction for span meta. file:// URLs and malformed strings +// fall back gracefully so instrumentation never throws on the hot path. +function _hostOf(url) { + try { + return new URL(url).host || 'file'; + } catch { + return undefined; + } +} + +// P1 (behind isNet()): attach Playwright request capture to a page for the +// duration of one capture. Collects per-request { url, host, status, fromCache, +// encodedBodySize, timing } via requestfinished/requestfailed, then records an +// aggregate-per-host event (count, bytes, totalMs) plus the single slowest +// request. Returns a detach() that removes the listeners and records the +// aggregate. A no-op (returns a noop detach) unless isNet() is on. Strictly +// opt-in so default runs are byte-for-byte unaffected. +function _attachNetCapture(page, meta) { + if (!profile.isNet()) return () => {}; + + const perHost = new Map(); + let slowest = null; + + const durationOf = (timing) => { + if (!timing) return 0; + // Playwright timing: startTime is an absolute epoch (ms), but every other + // field (requestStart, responseStart, responseEnd, …) is an OFFSET + // relative to startTime, or -1 when unavailable. responseEnd is therefore + // already the request's duration. The previous code computed + // responseEnd - startTime — a relative offset minus an epoch — so the + // `end >= start` guard always failed and every request measured 0 ms. + const end = timing.responseEnd; + return typeof end === 'number' && end >= 0 ? end : 0; + }; + + const onFinished = (request) => { + try { + const response = request.response && request.response(); + const recordReq = (status, fromCache, bytes) => { + const url = request.url(); + const host = _hostOf(url) || 'unknown'; + const timing = request.timing ? request.timing() : null; + const durMs = durationOf(timing); + const bucket = perHost.get(host) || { count: 0, bytes: 0, totalMs: 0 }; + bucket.count += 1; + bucket.bytes += Number.isFinite(bytes) ? bytes : 0; + bucket.totalMs += durMs; + perHost.set(host, bucket); + if (!slowest || durMs > slowest.durMs) { + slowest = { url, host, status, fromCache, durMs, bytes: Number.isFinite(bytes) ? bytes : 0 }; + } + }; + + const settle = (resp) => { + let status; + let fromCache = false; + let bytes = 0; + if (resp) { + status = typeof resp.status === 'function' ? resp.status() : undefined; + if (typeof resp.fromCache === 'function') fromCache = resp.fromCache(); + } + const sizes = request.sizes ? request.sizes() : null; + if (sizes && typeof sizes.then === 'function') { + sizes.then((s) => recordReq(status, fromCache, s && s.responseBodySize), () => recordReq(status, fromCache, bytes)); + return; + } + if (sizes && typeof sizes.responseBodySize === 'number') bytes = sizes.responseBodySize; + recordReq(status, fromCache, bytes); + }; + + if (response && typeof response.then === 'function') { + response.then(settle, () => settle(null)); + } else { + settle(response); + } + } catch { + // Never let request bookkeeping disturb the capture. + } + }; + + const onFailed = (request) => { + try { + const url = request.url(); + const host = _hostOf(url) || 'unknown'; + const timing = request.timing ? request.timing() : null; + const durMs = durationOf(timing); + const bucket = perHost.get(host) || { count: 0, bytes: 0, totalMs: 0 }; + bucket.count += 1; + bucket.totalMs += durMs; + perHost.set(host, bucket); + if (!slowest || durMs > slowest.durMs) { + slowest = { url, host, status: undefined, fromCache: false, durMs, bytes: 0, failed: true }; + } + } catch { + // ignore + } + }; + + page.on('requestfinished', onFinished); + page.on('requestfailed', onFailed); + + return function detach() { + try { + page.off('requestfinished', onFinished); + page.off('requestfailed', onFailed); + } catch { + // ignore + } + const hosts = {}; + for (const [host, bucket] of perHost) { + hosts[host] = { + count: bucket.count, + bytes: bucket.bytes, + totalMs: Number(bucket.totalMs.toFixed(2)), + }; + } + profile.record('capture.network', 0, { + ...meta, + hosts, + requestCount: Array.from(perHost.values()).reduce((sum, b) => sum + b.count, 0), + slowest: slowest + ? { url: slowest.url, host: slowest.host, status: slowest.status, fromCache: slowest.fromCache, durMs: Number(slowest.durMs.toFixed(2)), bytes: slowest.bytes } + : null, + }); + }; +} + +// launchBrowser(chromium, options, meta): instrumented chromium.launch() wrapper. +// Times the launch ('capture.browser.launch'), bumps the P2 launch counter, and +// records an aggregate counter event with the running launch count + total ms. +// When profiling is off this is just `chromium.launch(options)` with the counters +// still ticking (cheap) but nothing recorded or flushed. +export async function launchBrowser(chromium, options = { headless: true }, meta) { + const token = profile.mark('capture.browser.launch'); + const start = performance.now(); + try { + const browser = await chromium.launch(options); + const durMs = performance.now() - start; + _browserLaunchCount += 1; + _browserLaunchTotalMs += durMs; + profile.measure(token, { ...meta, launchCount: _browserLaunchCount, headless: options && options.headless !== false }); + profile.record('capture.browser.launchCount', _browserLaunchTotalMs, { + ...meta, + count: _browserLaunchCount, + totalMs: Number(_browserLaunchTotalMs.toFixed(2)), + lastMs: Number(durMs.toFixed(2)), + }); + return browser; + } catch (err) { + profile.measure(token, { ...meta, error: true }); + throw err; + } +} + +// Expose the launch tally for harness/reporting code without re-deriving it. +export function browserLaunchStats() { + return { count: _browserLaunchCount, totalMs: _browserLaunchTotalMs }; +} + +export const DEFAULT_VIEWPORTS = [ + { name: 'desktop', width: 1440, height: 1200 }, + { name: 'mobile', width: 390, height: 1200 }, +]; + +export async function loadCaptureDeps(pluginRoot) { + try { + const { chromium } = await import('playwright'); + const { PNG } = await import('pngjs'); + const pixelmatch = (await import('pixelmatch')).default; + return { chromium, PNG, pixelmatch }; + } catch (error) { + throw new Error(`Screenshot comparison needs optional packages. Run npm install in ${pluginRoot}. Missing dependency: ${error.message}`); + } +} + +export async function serveDirectory(rootDir) { + const root = path.resolve(rootDir); + const server = http.createServer((request, response) => { + if (!['GET', 'HEAD'].includes(request.method || '')) { + response.writeHead(405, { Allow: 'GET, HEAD' }); + response.end('Method not allowed'); + return; + } + + const requestUrl = new URL(request.url || '/', 'http://127.0.0.1'); + const pathname = decodeURIComponent(requestUrl.pathname); + const filePath = path.resolve(root, `.${pathname.endsWith('/') ? `${pathname}index.html` : pathname}`); + if (!isPathInside(root, filePath)) { + response.writeHead(403); + response.end('Forbidden'); + return; + } + + if (!fs.existsSync(filePath) || !fs.statSync(filePath).isFile()) { + response.writeHead(404); + response.end('Not found'); + return; + } + + response.writeHead(200, { 'Content-Type': mimeType(filePath) }); + if (request.method === 'HEAD') { + response.end(); + return; + } + fs.createReadStream(filePath).pipe(response); + }); + + await new Promise((resolve, reject) => { + server.once('error', reject); + server.listen(0, '127.0.0.1', () => { + server.off('error', reject); + resolve(); + }); + }); + + const address = server.address(); + const port = typeof address === 'object' && address ? address.port : 0; + return { + urlFor(filePath) { + const relative = path.relative(root, filePath).split(path.sep).map(encodeURIComponent).join('/'); + return `http://127.0.0.1:${port}/${relative}`; + }, + close() { + return new Promise((resolve, reject) => { + server.close((error) => error ? reject(error) : resolve()); + }); + }, + }; +} + +function mimeType(filePath) { + const ext = path.extname(filePath).toLowerCase(); + return { + '.css': 'text/css; charset=utf-8', + '.html': 'text/html; charset=utf-8', + '.js': 'text/javascript; charset=utf-8', + '.json': 'application/json; charset=utf-8', + '.svg': 'image/svg+xml', + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.webp': 'image/webp', + }[ext] || 'application/octet-stream'; +} + +export async function captureUrl(browser, url, screenshotPath, viewport, { editor = false } = {}) { + // Shared meta for every span this capture emits. No-op overhead when off. + const kind = editor ? 'editor' : 'html'; + const meta = { + kind, + host: _hostOf(url), + viewport: viewport && viewport.name ? viewport.name : `${viewport.width}x${viewport.height}`, + }; + const page = await browser.newPage({ viewport: { width: viewport.width, height: viewport.height } }); + // P1: attach network capture only when isNet(); detaches in finally. + const detachNet = _attachNetCapture(page, meta); + try { + await page.emulateMedia({ reducedMotion: 'reduce' }); + // page.goto with waitUntil:'networkidle' couples navigation and the + // networkidle settle into one awaited call, so it carries the full + // navigate + networkidle tail. Labelled as the navigate span. + await profile.span( + 'capture.navigate', + () => page.goto(url, editor ? { waitUntil: 'networkidle', timeout: 60000 } : { waitUntil: 'networkidle' }), + meta, + ); + if (editor) { + // The editor's networkidle is gated on the block-list layout (the 47 + // s.w.org scripts must register before the layout exists), so the + // explicit wait for that selector is the separable networkidle tail. + await profile.span( + 'capture.wait.networkidle', + () => page.waitForSelector('.block-editor-block-list__layout', { timeout: 60000 }), + meta, + ); + const errorText = await page.locator('.wbdc-editor-error').textContent({ timeout: 250 }).catch(() => ''); + if (errorText && !/Loading WordPress block editor/i.test(errorText)) { + throw new Error(`Editor preview failed before screenshot: ${errorText}`); + } + await page.addStyleTag({ content: editorComparisonCss() }); + } else { + await page.addStyleTag({ content: `${motionFreezeCss()}\n${transientOverlayCaptureCss()}` }); + } + await page.waitForTimeout(150); + await profile.span( + 'capture.screenshot', + () => page.screenshot({ path: screenshotPath, fullPage: viewport.fullPage !== false, animations: 'disabled' }), + meta, + ); + } finally { + detachNet(); + await page.close(); + } +} + +export async function capture(browser, htmlPath, screenshotPath, viewport) { + await captureUrl(browser, pathToFileURL(htmlPath).href, screenshotPath, viewport); +} + +export async function captureEditor(browser, editorUrl, screenshotPath, viewport) { + await captureUrl(browser, editorUrl, screenshotPath, viewport, { editor: true }); +} + +export function editorComparisonCss() { + // Hide editor chrome and freeze motion for the screenshot. Block margins + // are deliberately NOT zeroed here: the preview's wbdc-parity layer already + // neutralizes editor block-gap margins, and the workspace CSS owns the + // document rhythm — zeroing with !important would erase that layout signal. + return ` + ${motionFreezeCss()} + ${transientOverlayCaptureCss()} + .wbdc-editor-toolbar{display:none!important} + .wbdc-editor-shell,.wbdc-editor-canvas,.is-root-container.block-editor-block-list__layout{min-height:0!important} + .editor-styles-wrapper{padding:0!important} + .block-editor-block-list__block::before, + .block-editor-block-list__block::after, + .block-editor-block-list__breadcrumb, + .block-editor-block-list__insertion-point, + .block-editor-block-contextual-toolbar, + .block-editor-block-toolbar, + .block-editor-inserter, + .block-editor-warning, + .components-placeholder, + .block-editor-block-variation-picker, + .block-editor-default-block-appender, + .block-editor-block-list__empty-block-inserter, + .components-popover{display:none!important} + .block-editor-block-list__block, + .block-editor-block-list__block.is-selected, + .block-editor-block-list__block.has-child-selected{outline:0!important;box-shadow:none!important} + `; +} + +export function motionFreezeCss() { + return '*,*::before,*::after{animation:none!important;transition:none!important;scroll-behavior:auto!important}'; +} + +export function transientOverlayCaptureCss() { + return ` + .loading-screen, + .loading-fade, + .preloader, + .loader, + .cookie-jar, + [data-role="cookie-jar-pop-up"], + [aria-label="Cookie"], + [aria-label="Cookies"] { + display: none !important; + opacity: 0 !important; + visibility: hidden !important; + pointer-events: none !important; + } + + .c-scrollbar { + display: none !important; + } + `; +} + +export function comparePngs({ target, mockupShot, candidateShot, diffShot, viewport, PNG, pixelmatch }) { + return profile.span( + 'capture.comparePngs', + () => { + const mockup = PNG.sync.read(fs.readFileSync(mockupShot)); + const candidate = PNG.sync.read(fs.readFileSync(candidateShot)); + const width = Math.min(mockup.width, candidate.width); + const height = Math.min(mockup.height, candidate.height); + const diff = new PNG({ width, height }); + const mismatch = pixelmatch( + cropPng(mockup, width, height, PNG).data, + cropPng(candidate, width, height, PNG).data, + diff.data, + width, + height, + { threshold: 0.1 } + ); + fs.writeFileSync(diffShot, PNG.sync.write(diff)); + return { + target, + viewport: viewport.name, + size: `${viewport.width}x${viewport.height}`, + mockup: mockupShot, + candidate: candidateShot, + ...(target === 'rendered' ? { rendered: candidateShot } : {}), + ...(target === 'editor' ? { editor: candidateShot } : {}), + diff: diffShot, + mismatchPercent: Number(((mismatch / (width * height)) * 100).toFixed(2)), + widthDelta: Math.abs(mockup.width - candidate.width), + heightDelta: Math.abs(mockup.height - candidate.height), + }; + }, + { + target, + kind: target === 'editor' ? 'editor' : 'html', + viewport: viewport && viewport.name ? viewport.name : `${viewport.width}x${viewport.height}`, + }, + ); +} + +export function cropPng(source, width, height, PNG) { + if (source.width === width && source.height === height) return source; + const cropped = new PNG({ width, height }); + for (let y = 0; y < height; y += 1) { + const sourceStart = y * source.width * 4; + const targetStart = y * width * 4; + source.data.copy(cropped.data, targetStart, sourceStart, sourceStart + width * 4); + } + return cropped; +} diff --git a/apps/cli/ai/html-to-blocks-engine/lib/dynamic-render.mjs b/apps/cli/ai/html-to-blocks-engine/lib/dynamic-render.mjs new file mode 100644 index 0000000000..63e0752b3b --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/lib/dynamic-render.mjs @@ -0,0 +1,233 @@ +// tools/lib/dynamic-render.mjs — deterministic frontend markup for dynamic core +// blocks so the static rendered/ preview (and the editor canvas) can show them. +// +// WordPress server-renders these blocks (their save() returns null), so the +// html-to-blocks static serializer emits only an empty block comment for them. +// That blindness is the historical reason the pipeline reached for custom +// "site-nav"/"search"/"pagination" blocks. It is a HARNESS limitation, not a +// design fact: the right output is the real core block. This module closes the +// gap by rendering the same frontend HTML WordPress would, from the block's +// attributes + inner blocks + a small preview context, so the real core block +// previews and styles correctly on both surfaces. +// +// Pure string-in/string-out (no imports) so the identical source runs in Node +// (serializer save override) and in the browser editor preview (edit override). +// The emitted class names are canonical WordPress block classes; workspace CSS +// targets them and styles both surfaces identically. + +// Blocks whose frontend markup this module reproduces. navigation-link and +// navigation-submenu are rendered by their parent navigation walk, not on +// their own; the query-pagination children are rendered by the parent. +export const DYNAMIC_SHIM_BLOCKS = [ + 'core/navigation', + 'core/search', + 'core/site-title', + 'core/site-logo', + 'core/post-comments-form', + 'core/query-pagination', + 'core/post-navigation-link', + 'core/post-date', + 'core/post-terms', +]; + +// Subset that the editor canvas does NOT render usefully on its own: these read +// site/post identity from entities that the no-data preview store lacks, so +// their real edit() shows a hidden placeholder (blank in the screenshot). The +// editor preview overrides only these with the shim; navigation, search, +// post-comments-form, query-pagination, post-navigation-link and post-date +// render correctly through their native edit() and are left alone. Every block +// here renders from attributes + preview context only (no inner blocks), so the +// editor override does not need the block store. +export const EDITOR_SHIM_BLOCKS = [ + 'core/site-title', + 'core/site-logo', + 'core/post-terms', +]; + +export const DEFAULT_PREVIEW_CONTEXT = { + siteTitle: 'Site Title', + siteTagline: 'Just another site', + siteLogoUrl: '', + siteLogoWidth: 120, + homeUrl: '#', + postDate: 'January 1, 2025', + postTerms: 'Uncategorized', +}; + +function esc(value) { + return String(value == null ? '' : value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +// Pass author classNames / preset color classes through onto the block root so +// the agent can style via the block tree, exactly like a static block. +function rootClass(base, attrs) { + const classes = [base]; + if (attrs && attrs.className) classes.push(String(attrs.className)); + if (attrs && attrs.textColor) classes.push('has-text-color', `has-${attrs.textColor}-color`); + if (attrs && attrs.backgroundColor) classes.push('has-background', `has-${attrs.backgroundColor}-background-color`); + if (attrs && attrs.fontSize) classes.push(`has-${attrs.fontSize}-font-size`); + return classes.filter(Boolean).join(' '); +} + +function navItems(innerBlocks) { + return (innerBlocks || []).map((block) => { + const name = block.blockName || block.name; + const a = block.attrs || block.attributes || {}; + const label = esc(a.label || ''); + const url = esc(a.url || '#'); + if (name === 'core/navigation-submenu') { + return `
  • ` + + `` + + `${label}` + + `` + + `` + + `
  • `; + } + // core/navigation-link (and any other simple item) + return `
  • ` + + `` + + `${label}
  • `; + }).join(''); +} + +function renderNavigation(attrs, innerBlocks) { + const overlay = attrs && attrs.overlayMenu === 'always' ? ' is-responsive' : ''; + const orientation = attrs && attrs.orientation === 'vertical' ? ' is-vertical' : ''; + const cls = rootClass(`wp-block-navigation${orientation}`, attrs); + const aria = attrs && attrs.ariaLabel ? ` aria-label="${esc(attrs.ariaLabel)}"` : ''; + return ``; +} + +function renderSearch(attrs) { + const a = attrs || {}; + const label = a.label != null ? a.label : 'Search'; + const showLabel = !a.showLabel === false ? a.showLabel !== false : true; + const hideLabel = a.showLabel === false; + const placeholder = esc(a.placeholder || ''); + const buttonText = esc(a.buttonText || 'Search'); + const buttonInside = a.buttonPosition === 'button-inside'; + const noButton = a.buttonPosition === 'no-button'; + const widthClass = a.widthUnit && a.width ? ` has-custom-width` : ''; + const cls = rootClass(`wp-block-search${widthClass}`, a); + const labelHtml = hideLabel + ? `` + : ``; + const input = ``; + const button = noButton ? '' : + ``; + const wrapClass = buttonInside + ? 'wp-block-search__inside-wrapper wp-block-search__button-inside' + : 'wp-block-search__inside-wrapper'; + return ``; +} + +function renderSiteTitle(attrs, ctx) { + const level = attrs && Number.isInteger(attrs.level) ? attrs.level : 1; + const tag = level === 0 ? 'p' : `h${level}`; + const cls = rootClass('wp-block-site-title', attrs); + const home = esc(ctx.homeUrl); + return `<${tag} class="${cls}">${esc(ctx.siteTitle)}`; +} + +function renderSiteLogo(attrs, ctx) { + const width = (attrs && attrs.width) || ctx.siteLogoWidth || 120; + const cls = rootClass('wp-block-site-logo', attrs); + const home = esc(ctx.homeUrl); + if (!ctx.siteLogoUrl) { + // No logo configured: render the canonical empty-logo box so layout and + // sizing still preview. Workspace CSS can target .wp-block-site-logo. + return `
    `; + } + return `
    ` + + `
    `; +} + +function renderPostDate(attrs, ctx) { + const cls = rootClass('wp-block-post-date', attrs); + return `
    `; +} + +function renderPostTerms(attrs, ctx) { + const cls = rootClass('wp-block-post-terms', attrs); + const prefix = attrs && attrs.prefix ? esc(attrs.prefix) : ''; + const suffix = attrs && attrs.suffix ? esc(attrs.suffix) : ''; + return `
    ${prefix}${esc(ctx.postTerms)}${suffix}
    `; +} + +function renderPostNavigationLink(attrs) { + const a = attrs || {}; + const type = a.type === 'previous' ? 'previous' : 'next'; + const cls = rootClass(`wp-block-post-navigation-link is-${type}`, a); + const label = esc(a.label || (type === 'previous' ? 'Previous' : 'Next')); + const arrow = a.arrow && a.arrow !== 'none' + ? (type === 'previous' ? '' : '') + : ''; + const arrowNext = a.arrow && a.arrow !== 'none' && type === 'next' + ? '' : ''; + return `
    ${arrow}${label}${arrowNext}
    `; +} + +function renderQueryPaginationChild(block, ctx) { + const name = block.blockName || block.name; + const a = block.attrs || block.attributes || {}; + if (name === 'core/query-pagination-previous') { + return `${esc(a.label || 'Previous Page')}`; + } + if (name === 'core/query-pagination-next') { + return `${esc(a.label || 'Next Page')}`; + } + if (name === 'core/query-pagination-numbers') { + return `
    ` + + `1` + + `2` + + `3` + + `` + + `8
    `; + } + return ''; +} + +function renderQueryPagination(attrs, innerBlocks, ctx) { + const cls = rootClass('wp-block-query-pagination is-content-justification-space-between is-layout-flex', attrs); + const children = (innerBlocks || []).map((b) => renderQueryPaginationChild(b, ctx)).join(''); + return ``; +} + +function renderCommentsForm(attrs) { + const cls = rootClass('wp-block-post-comments-form', attrs); + return `
    ` + + `

    Leave a Reply

    ` + + `
    ` + + `

    ` + + `

    ` + + `

    ` + + `
    `; +} + +// Returns the frontend HTML string for an allowlisted dynamic block, or null +// when the block is not shimmed (the caller keeps the normal save() output). +export function renderDynamicBlock(name, attributes, innerBlocks, context) { + const ctx = Object.assign({}, DEFAULT_PREVIEW_CONTEXT, context || {}); + const attrs = attributes || {}; + switch (name) { + case 'core/navigation': return renderNavigation(attrs, innerBlocks); + case 'core/search': return renderSearch(attrs); + case 'core/site-title': return renderSiteTitle(attrs, ctx); + case 'core/site-logo': return renderSiteLogo(attrs, ctx); + case 'core/post-comments-form': return renderCommentsForm(attrs); + case 'core/query-pagination': return renderQueryPagination(attrs, innerBlocks, ctx); + case 'core/post-navigation-link': return renderPostNavigationLink(attrs); + case 'core/post-date': return renderPostDate(attrs, ctx); + case 'core/post-terms': return renderPostTerms(attrs, ctx); + default: return null; + } +} diff --git a/apps/cli/ai/html-to-blocks-engine/lib/fix-markup.mjs b/apps/cli/ai/html-to-blocks-engine/lib/fix-markup.mjs new file mode 100644 index 0000000000..95a83a8ff9 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/lib/fix-markup.mjs @@ -0,0 +1,75 @@ +// tools/lib/fix-markup.mjs — canonicalize block markup through the registry. +// +// Ported from telex's block-fixer core (scripts/block-fixer/lib/blockFixer.js): +// parse() the markup, recreate every named block from its parsed attributes +// with createBlock(), and serialize() the result. The regenerated markup is +// byte-identical to what each block's save() produces, which eliminates the +// editor's block validation errors caused by drifted hand-written or +// AI-generated markup. Freeform/unnamed blocks pass through untouched. +// +// telex's nested-

    regex pre-fix is NOT ported: it targets malformed raw +// AI HTML; everything this pipeline handles is parseable block markup. + +import { loadWordPressBlocks } from './wp-serialize.mjs'; + +// parse() injects per-type attribute values that createBlock() does not +// (declared defaults rebuilt as fresh objects, and parse-time filters like +// core's default block bindings on post-date). The editor performs the same +// injection on ANY markup, so these are parse artifacts — bake them into the +// regenerated comment and idempotency breaks. Compute each type's injected +// set once by round-tripping a minimal block. +const parseInjectedCache = new Map(); +function parseInjectedAttrs(wpBlocks, name) { + if (!parseInjectedCache.has(name)) { + const injected = {}; + try { + const minimal = wpBlocks.createBlock(name, {}); + const [reparsed] = wpBlocks.parse(wpBlocks.serialize([minimal])); + for (const [key, value] of Object.entries(reparsed?.attributes || {})) { + injected[key] = JSON.stringify(value); + } + } catch { /* leave empty: strip nothing for this type */ } + parseInjectedCache.set(name, injected); + } + return parseInjectedCache.get(name); +} + +function recreateBlock(wpBlocks, block) { + const innerBlocks = (block.innerBlocks || []).map((inner) => recreateBlock(wpBlocks, inner)); + if (!block.name) return block; // freeform HTML: nothing to regenerate from + const injected = parseInjectedAttrs(wpBlocks, block.name); + const attributes = {}; + for (const [key, value] of Object.entries(block.attributes || {})) { + if (injected[key] !== undefined && JSON.stringify(value) === injected[key]) continue; + attributes[key] = value; + } + return wpBlocks.createBlock(block.name, attributes, innerBlocks); +} + +function collectIssues(blocks, issues) { + for (const block of blocks || []) { + if (block.isValid === false) { + const name = block.name || 'unknown'; + const detail = (block.validationIssues || []) + .map((issue) => (typeof issue === 'string' ? issue : Array.isArray(issue.args) && typeof issue.args[0] === 'string' ? issue.args[0] : 'block marked invalid')) + .join('; '); + issues.push(`${name}: ${detail || 'block marked invalid'}`); + } + collectIssues(block.innerBlocks, issues); + } +} + +// Callers must have registered all blocks the markup uses (core + custom) +// before calling — see ensureBlocksRegistered in wp-serialize.mjs. +export function fixBlockMarkup(markup) { + const wpBlocks = loadWordPressBlocks(); + const parsed = wpBlocks.parse(markup); + const issues = []; + collectIssues(parsed, issues); + const fixed = `${wpBlocks.serialize(parsed.map((block) => recreateBlock(wpBlocks, block))).trim()}\n`; + return { + markup: fixed, + changed: fixed.trim() !== markup.trim(), + issues, + }; +} diff --git a/apps/cli/ai/html-to-blocks-engine/lib/profile.mjs b/apps/cli/ai/html-to-blocks-engine/lib/profile.mjs new file mode 100644 index 0000000000..438891126a --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/lib/profile.mjs @@ -0,0 +1,245 @@ +// Profiling helpers for the html-to-wordpress-blocks toolchain. +// +// Design rules (see docs/profiling-plan.md): +// - In-process durations use performance.now() (monotonic). +// - Cross-process ordering uses Date.now() epoch ms (tsEpochMs). +// - NEVER write profiling output to stdout: stdout carries the Content-Length +// MCP JSON-RPC stream. Profiling goes to stderr or files only. +// - Every function is safe to call when profiling is off, with ~zero overhead +// on the hot path when WBDC_PROFILE is unset. + +import { performance } from 'node:perf_hooks'; +import fs from 'node:fs'; +import path from 'node:path'; + +// Per-process state. A flat buffer of recorded spans/events plus a depth counter +// for flamegraph nesting and a shallow run-metadata bag. +const _buffer = []; +let _depth = 0; +let _runMeta = {}; +let _exitHookRegistered = false; + +const _ON_VALUES = new Set(['on', 'deep', '1', 'true']); + +// --- env gates --------------------------------------------------------------- + +export function isOn() { + return _ON_VALUES.has(process.env.WBDC_PROFILE); +} + +export function isDeep() { + return process.env.WBDC_PROFILE === 'deep'; +} + +export function isNet() { + return process.env.WBDC_PROFILE_NET === '1'; +} + +// --- recording --------------------------------------------------------------- + +function _registerExitHook() { + if (_exitHookRegistered) return; + _exitHookRegistered = true; + // Per-call processes (mcp-call.sh driver mode) exit after one tool call; + // flushing on exit persists their spans automatically. + process.on('exit', () => { + try { + flush(); + } catch { + // Never let a profiling failure crash the host process. + } + }); +} + +// tsEpochMs anchors a span at its START (open time), so cross-process ordering +// and speedscope nesting reflect when work began — not when it finished. A +// parent span finishes (is recorded) after its children, but its start epoch is +// earlier, which is what keeps the flamegraph properly nested. +function _push(label, durMs, depth, meta, tsEpochMs) { + _registerExitHook(); + _buffer.push({ + label, + durMs, + depth, + tsEpochMs: tsEpochMs === undefined ? Date.now() : tsEpochMs, + meta: meta === undefined ? undefined : meta, + }); +} + +// span(label, fn, meta): time fn (sync or async) with performance.now() and +// record a nested span. When profiling is off, this is a pure pass-through of +// fn() with zero recording overhead. +export function span(label, fn, meta) { + if (!isOn()) { + return fn(); + } + const depth = _depth; + _depth = depth + 1; + const start = performance.now(); + const startEpoch = Date.now(); + + const finish = () => { + _depth = depth; + _push(label, performance.now() - start, depth, meta, startEpoch); + }; + + let result; + try { + result = fn(); + } catch (err) { + finish(); + throw err; + } + + if (result && typeof result.then === 'function') { + return result.then( + (value) => { + finish(); + return value; + }, + (err) => { + finish(); + throw err; + }, + ); + } + + finish(); + return result; +} + +// mark(label): capture a start time, returning an opaque token. Null when off. +export function mark(label) { + if (!isOn()) return null; + return { label, start: performance.now(), startEpoch: Date.now(), depth: _depth }; +} + +// measure(token, meta): close a mark() token and record the span. No-op on null. +export function measure(token, meta) { + if (!token) return; + _push(token.label, performance.now() - token.start, token.depth, meta, token.startEpoch); +} + +// record(name, durMs, meta): record a precomputed duration event (for +// cross-await or subprocess timings that don't fit the span() lifecycle). +export function record(name, durMs, meta) { + if (!isOn()) return; + _push(name, durMs, _depth, meta); +} + +// setRunMeta(meta): shallow-merge meta into this process's run metadata. +export function setRunMeta(meta) { + if (!meta || typeof meta !== 'object') return; + _runMeta = { ..._runMeta, ...meta }; +} + +// flush(): append every buffered span/event as JSONL to +//

    /spans-.jsonl, then clear the buffer. Idempotent. +export function flush() { + if (_buffer.length === 0) return; + + const dir = process.env.WBDC_PROFILE_DIR || path.join('reports', 'profile'); + fs.mkdirSync(dir, { recursive: true }); + + const file = path.join(dir, `spans-${process.pid}.jsonl`); + const pid = process.pid; + const run = _runMeta; + + const lines = _buffer.map((s) => + JSON.stringify({ + pid, + run, + label: s.label, + durMs: s.durMs, + depth: s.depth, + tsEpochMs: s.tsEpochMs, + meta: s.meta, + }), + ); + + fs.appendFileSync(file, lines.join('\n') + '\n'); + _buffer.length = 0; +} + +// toSpeedscope(spans): PURE function. Convert recorded spans into a speedscope +// file-format object (https://www.speedscope.app). Each span becomes an +// open ("O") event at its start and a close ("C") event at start + durMs, in a +// single evented profile. Frames are de-duplicated by label. +export function toSpeedscope(spans) { + const list = Array.isArray(spans) ? spans : []; + + const frames = []; + const frameIndex = new Map(); + const frameFor = (label) => { + const key = String(label); + if (frameIndex.has(key)) return frameIndex.get(key); + const index = frames.length; + frames.push({ name: key }); + frameIndex.set(key, index); + return index; + }; + + // Derive a monotonic "at" axis from each span's tsEpochMs, falling back to a + // running cursor so spans without timestamps still nest in recorded order. + // Each span yields two endpoints (open at start, close at end) carrying a + // stable sequence id so opens/closes can be paired and ordered deterministically. + let cursor = 0; + const endpoints = []; + let startValue = Infinity; + let endValue = 0; + + list.forEach((s, seq) => { + const dur = Number.isFinite(s && s.durMs) ? s.durMs : 0; + const at = Number.isFinite(s && s.tsEpochMs) ? s.tsEpochMs : cursor; + const depth = Number.isFinite(s && s.depth) ? s.depth : 0; + const frame = frameFor(s && s.label); + const end = at + dur; + endpoints.push({ kind: 'O', at, frame, seq, depth }); + endpoints.push({ kind: 'C', at: end, frame, seq, depth }); + if (at < startValue) startValue = at; + if (end > endValue) endValue = end; + cursor = end; + }); + + if (!Number.isFinite(startValue)) startValue = 0; + + // Speedscope evented profiles require chronologically ordered events where + // every "O" is matched by a later "C" and frames close in reverse open order + // (proper nesting). Date.now() has only millisecond resolution, so a parent + // span and its sub-millisecond children frequently share the same `at`; we + // therefore break ties by depth (which the span() recorder tracks) so the + // flamegraph nests correctly regardless of record order: + // - same instant: opens (rank 0) before closes (rank 1) + // - among opens at that instant: shallower depth opens first (parent → child) + // - among closes at that instant: deeper depth closes first (child → parent) + // - remaining ties fall back to recording sequence for determinism + endpoints.sort((a, b) => { + if (a.at !== b.at) return a.at - b.at; + const ra = a.kind === 'O' ? 0 : 1; + const rb = b.kind === 'O' ? 0 : 1; + if (ra !== rb) return ra - rb; + if (a.kind === 'O') { + if (a.depth !== b.depth) return a.depth - b.depth; // parent opens first + return a.seq - b.seq; + } + if (a.depth !== b.depth) return b.depth - a.depth; // child closes first + return b.seq - a.seq; + }); + + const events = endpoints.map((e) => ({ type: e.kind, at: e.at, frame: e.frame })); + + return { + $schema: 'https://www.speedscope.app/file-format-schema.json', + shared: { frames }, + profiles: [ + { + type: 'evented', + name: 'wbdc-profile', + unit: 'milliseconds', + startValue, + endValue, + events, + }, + ], + }; +} diff --git a/apps/cli/ai/html-to-blocks-engine/lib/workspace.mjs b/apps/cli/ai/html-to-blocks-engine/lib/workspace.mjs new file mode 100644 index 0000000000..425e5fc9f4 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/lib/workspace.mjs @@ -0,0 +1,112 @@ +// tools/lib/workspace.mjs — shared fs/path/string helpers for all skills' tools. +import fs from 'node:fs'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +export const PLUGIN_ROOT = path.resolve(fileURLToPath(new URL('../..', import.meta.url))); + +export function isPathInside(parent, child) { + const relative = path.relative(parent, child); + return relative === '' || (relative && !relative.startsWith('..') && !path.isAbsolute(relative)); +} + +export function resolvePath(value) { + if (!value) throw new Error('Path is required.'); + return path.resolve(String(value)); +} + +export function resolveWorkspacePath(workspaceRoot, value) { + if (!value) throw new Error('Workspace-relative path is required.'); + const resolved = path.resolve(workspaceRoot, String(value)); + if (!isPathInside(workspaceRoot, resolved)) { + throw new Error(`Path must stay inside workspaceRoot: ${value}`); + } + return resolved; +} + +export function readIfExists(filePath) { + return fs.existsSync(filePath) ? fs.readFileSync(filePath, 'utf8') : ''; +} + +export function readJson(filePath) { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); +} + +export function readJsonIfExists(filePath) { + return fs.existsSync(filePath) ? readJson(filePath) : {}; +} + +export function writeFile(filePath, content) { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, content, 'utf8'); +} + +export function writeJson(filePath, data) { + writeFile(filePath, `${JSON.stringify(data, null, 2)}\n`); +} + +export function firstMatch(value, pattern, group = 1) { + const match = String(value || '').match(pattern); + return match ? match[group] : ''; +} + +export function cleanText(value) { + return String(value || '') + .replace(//gi, '') + .replace(//gi, '') + .replace(/<[^>]+>/g, ' ') + .replace(/\s+/g, ' ') + .trim(); +} + +export function titleCase(value) { + return String(value || '') + .replace(/[-_]/g, ' ') + .replace(/([a-z])([A-Z])/g, '$1 $2') + .replace(/\b\w/g, (char) => char.toUpperCase()); +} + +export function slug(value) { + return String(value || '').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, ''); +} + +export function camelName(value) { + const raw = String(value || '').trim(); + if (/^[A-Za-z_$][A-Za-z0-9_$]*$/.test(raw)) { + return raw.charAt(0).toLowerCase() + raw.slice(1); + } + const parts = slug(raw).split('-').filter(Boolean); + if (!parts.length) return 'field'; + return parts[0] + parts.slice(1).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(''); +} + +export function escapeHtml(value) { + return String(value || '') + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); +} + +export function escapeAttr(value) { + return String(value) + .replace(/&/g, '&') + .replace(/"/g, '"') + .replace(/\s*/g, '') + .replace(/>\s+<'); +} + +export function serializeBlockTreeWithWordPress(tree, context, options = {}) { + const { createBlock, serialize } = loadWordPressBlocks(); + // Time the actual call regardless of the coreBlocksRegistered guard: only the + // first call does real work (registerCoreBlocks + jsdom, ~1.7s cold), so the + // span captures that one-time cold cost; later calls are recorded as ~0. + profile.span('serialize.registerCore', () => registerWordPressCoreBlocks()); + profile.span('serialize.registerCustom', () => registerWorkspaceCustomBlocks(context.workspaceRoot)); + const blocks = Array.isArray(tree) ? tree : tree.blocks; + if (!Array.isArray(blocks)) { + throw new Error('Block tree must be an array or an object with a blocks array.'); + } + const wpBlocks = blocks.map((block) => toWordPressBlock(block, createBlock)); + if (!options.shimDynamic) { + return profile.span('serialize.serialize.clean', () => serialize(wpBlocks)); + } + // Render allowlisted dynamic core blocks (navigation, search, site-title, + // pagination, …) to their frontend HTML so the static preview shows them. + // Scoped to this call and restored after: the canonical serialization + // (content.html, blocks-to-theme) stays clean, and WordPress re-renders + // these server-side regardless of any saved inner markup. + return profile.span('serialize.serialize.shimmed', () => + withDynamicShims(options.previewContext || {}, () => serialize(wpBlocks)), + ); +} + +function withDynamicShims(previewContext, fn) { + const { getBlockType, unregisterBlockType, registerBlockType } = loadWordPressBlocks(); + const { createElement, RawHTML } = loadWordPressElement(); + const saved = []; + for (const name of DYNAMIC_SHIM_BLOCKS) { + const type = getBlockType(name); + if (!type) continue; + saved.push({ name, type }); + unregisterBlockType(name); + registerBlockType(name, { + ...type, + save: (props) => { + const html = renderDynamicBlock(name, props.attributes, props.innerBlocks, previewContext); + return html == null ? null : createElement(RawHTML, null, html); + }, + }); + } + try { + return fn(); + } finally { + for (const { name, type } of saved.reverse()) { + if (getBlockType(name)) unregisterBlockType(name); + registerBlockType(name, type); + } + } +} + +function toWordPressBlock(block, createBlock) { + if (!block || typeof block !== 'object') throw new Error('Every block tree item must be an object.'); + assertDataOnlyBlock(block); + const blockName = block.blockName || block.name; + if (!blockName || typeof blockName !== 'string') throw new Error('Every block tree item needs blockName or name.'); + const attrs = block.attrs || block.attributes || {}; + validateBlockContract(blockName, attrs); + const innerBlocks = (block.innerBlocks || []).map((child) => toWordPressBlock(child, createBlock)); + return createBlock(blockName, attrs, innerBlocks); +} + +function assertDataOnlyBlock(block) { + for (const key of ['htmlLines', 'innerHTML', 'innerContent', 'html', 'markup', 'sourceHtml', 'innerHtml']) { + if (Object.prototype.hasOwnProperty.call(block, key)) { + throw new Error(`Data-only block tree violation in ${block.blockName || block.name || 'unknown block'}: remove ${key}. Use attrs, style, className, and innerBlocks only.`); + } + } +} + +function validateBlockContract(blockName, attrs) { + if (!blockName.startsWith('core/')) return; + const { getBlockType } = loadWordPressBlocks(); + const blockType = getBlockType(blockName); + if (!blockType) { + throw new Error(`${blockName} is not registered by @wordpress/block-library. Use a real registered core block or a custom block; do not invent core blocks.`); + } + const allowed = new Set(Object.keys(blockType.attributes || {})); + for (const key of Object.keys(attrs || {})) { + if (!allowed.has(key)) { + throw new Error(`${blockName} does not define "${key}" in WordPress block metadata. Move semantic attributes into a custom block or supported WordPress attributes.`); + } + } + if (blockName === 'core/group') { + const tagName = attrs.tagName || 'div'; + if (!ALLOWED_GROUP_TAGS.has(tagName)) { + throw new Error(`core/group tagName "${tagName}" is not allowed. Use core/group only for block-level containers or create a semantic custom block.`); + } + } +} + +export function registerWorkspaceCustomBlocks(workspaceRoot, blocksDir) { + const root = blocksDir || (workspaceRoot ? path.join(workspaceRoot, 'wordpress/blocks') : ''); + if (!root) return; + const blockRoots = findFiles(root, 'index.js').map((file) => path.dirname(file)); + for (const blockRoot of blockRoots) { + registerWorkspaceCustomBlock(blockRoot); + } +} + +function registerWorkspaceCustomBlock(blockRoot) { + const indexPath = path.join(blockRoot, 'index.js'); + if (!fs.existsSync(indexPath)) return; + const blockJsonPath = path.join(blockRoot, 'block.json'); + const blockJson = fs.existsSync(blockJsonPath) ? readJson(blockJsonPath) : {}; + const { registerBlockType, unregisterBlockType, getBlockType } = loadWordPressBlocks(); + const element = loadWordPressElement(); + const blockEditor = createBlockEditorShim(); + const components = createComponentShim(); + const blocks = { + ...loadWordPressBlocks(), + registerBlockType(name, settings) { + if (getBlockType(name)) unregisterBlockType(name); + return registerBlockType(name, normalizeCustomBlockSettings(name, settings, blockJson)); + }, + }; + const context = vm.createContext({ + window: { wp: { blocks, blockEditor, components, element } }, + wp: { blocks, blockEditor, components, element }, + console, + }); + vm.runInContext(fs.readFileSync(indexPath, 'utf8'), context, { filename: indexPath }); +} + +function normalizeCustomBlockSettings(name, settings, blockJson = {}) { + const save = settings.save + ? (props) => { + customBlockPropsStack.push({ name, attributes: props.attributes || {} }); + customInnerBlocksStack.push(props.innerBlocks || []); + try { + return settings.save(props); + } finally { + customInnerBlocksStack.pop(); + customBlockPropsStack.pop(); + } + } + : undefined; + return { + apiVersion: 3, + title: titleCase(name.split('/')[1] || name), + category: 'design', + ...blockJson, + ...settings, + attributes: { ...(blockJson.attributes || {}), ...(settings.attributes || {}) }, + ...(save ? { save } : {}), + }; +} + +function createBlockEditorShim() { + const { createElement: el, RawHTML } = loadWordPressElement(); + const { serialize } = loadWordPressBlocks(); + const useBlockProps = (props = {}) => blockPropsWithSupports(props, customBlockPropsStack.at(-1)); + useBlockProps.save = (props = {}) => blockPropsWithSupports(props, customBlockPropsStack.at(-1)); + const RichText = () => null; + RichText.Content = ({ tagName = 'div', value = '', ...props }) => { + const cleanProps = { ...props }; + delete cleanProps.allowedFormats; + delete cleanProps.onChange; + return el(tagName, cleanProps, el(RawHTML, null, richText(value))); + }; + return { + useBlockProps, + RichText, + InspectorControls: ({ children }) => el('div', null, children), + InnerBlocks: { + Content: () => el(RawHTML, null, serialize(customInnerBlocksStack.at(-1) || [], { isInnerBlocks: true })), + }, + }; +} + +function blockPropsWithSupports(props = {}, context) { + if (!context) return props; + const attrs = context.attributes || {}; + const supportStyle = styleSupportToReactStyle(attrs.style || {}); + const mergedStyle = { ...supportStyle, ...(props.style || {}) }; + const className = mergeClasses( + blockSupportClassName(context.name), + supportClassNames(attrs), + attrs.className, + props.className + ); + return { + ...props, + ...(className ? { className } : {}), + ...(Object.keys(mergedStyle).length ? { style: mergedStyle } : {}), + }; +} + +function blockSupportClassName(name) { + return name ? `wp-block-${name.replace('/', '-')}` : ''; +} + +function supportClassNames(attrs) { + const classes = []; + if (attrs.textColor || attrs.style?.color?.text) classes.push('has-text-color'); + if (attrs.backgroundColor || attrs.style?.color?.background || attrs.style?.color?.gradient) classes.push('has-background'); + if (attrs.fontSize) classes.push(`has-${slug(attrs.fontSize)}-font-size`); + return classes; +} + +function mergeClasses(...values) { + const seen = new Set(); + const flatten = (value) => Array.isArray(value) ? value.flatMap(flatten) : String(value || '').split(/\s+/); + return values + .flatMap(flatten) + .filter((value) => { + if (!value || seen.has(value)) return false; + seen.add(value); + return true; + }) + .join(' '); +} + +function styleSupportToReactStyle(style) { + const out = {}; + if (!style || typeof style !== 'object') return out; + + assignIf(out, 'color', style.color?.text); + assignIf(out, 'backgroundColor', style.color?.background); + assignIf(out, 'background', style.color?.gradient); + assignIf(out, 'fontSize', style.typography?.fontSize); + assignIf(out, 'fontFamily', style.typography?.fontFamily); + assignIf(out, 'lineHeight', style.typography?.lineHeight); + assignIf(out, 'fontWeight', style.typography?.fontWeight); + assignIf(out, 'fontStyle', style.typography?.fontStyle); + assignIf(out, 'letterSpacing', style.typography?.letterSpacing); + assignIf(out, 'textTransform', style.typography?.textTransform); + assignIf(out, 'minHeight', style.dimensions?.minHeight); + assignBox(out, 'padding', style.spacing?.padding); + assignBox(out, 'margin', style.spacing?.margin); + assignIf(out, 'gap', style.spacing?.blockGap); + assignBorder(out, style.border); + + for (const [key, value] of Object.entries(style)) { + if (key.startsWith('--')) out[key] = cssPresetValue(value); + } + + return out; +} + +function assignIf(out, key, value) { + if (value !== undefined && value !== null && value !== '') out[key] = cssPresetValue(value); +} + +function assignBox(out, prefix, value) { + if (!value) return; + if (typeof value === 'string') { + out[prefix] = cssPresetValue(value); + return; + } + assignIf(out, `${prefix}Top`, value.top); + assignIf(out, `${prefix}Right`, value.right); + assignIf(out, `${prefix}Bottom`, value.bottom); + assignIf(out, `${prefix}Left`, value.left); +} + +function assignBorder(out, border) { + if (!border || typeof border !== 'object') return; + assignIf(out, 'borderColor', border.color); + assignIf(out, 'borderWidth', border.width); + assignIf(out, 'borderStyle', border.style); + assignIf(out, 'borderRadius', border.radius); + for (const side of ['top', 'right', 'bottom', 'left']) { + const sideBorder = border[side]; + if (!sideBorder || typeof sideBorder !== 'object') continue; + const prefix = `border${titleCase(side)}`; + assignIf(out, `${prefix}Color`, sideBorder.color); + assignIf(out, `${prefix}Width`, sideBorder.width); + assignIf(out, `${prefix}Style`, sideBorder.style); + } +} + +function cssPresetValue(value) { + if (typeof value !== 'string') return value; + const match = value.match(/^var:preset\|([a-z0-9-]+)\|([a-z0-9-]+)$/i); + return match ? `var(--wp--preset--${match[1]}--${match[2]})` : value; +} + +function createComponentShim() { + const { createElement: el } = loadWordPressElement(); + const passthrough = ({ children }) => el('div', null, children); + return { + PanelBody: passthrough, + TextControl: passthrough, + ToggleControl: passthrough, + }; +} + +function richText(value) { + return String(value ?? ''); +} + +export function loadWordPressBlocks() { + try { + return require('@wordpress/blocks'); + } catch (error) { + throw new Error(`WordPress block serialization needs @wordpress/blocks. Run npm install in ${PLUGIN_ROOT}. Missing dependency: ${error.message}`); + } +} + +function loadWordPressElement() { + try { + return require('@wordpress/element'); + } catch (error) { + throw new Error(`WordPress block serialization needs @wordpress/element. Run npm install in ${PLUGIN_ROOT}. Missing dependency: ${error.message}`); + } +} + +export function registerWordPressCoreBlocks() { + if (coreBlocksRegistered) return; + setupDomEnvironment(); + try { + require('@wordpress/block-library').registerCoreBlocks(); + coreBlocksRegistered = true; + } catch (error) { + throw new Error(`WordPress core block registration needs @wordpress/block-library and jsdom. Run npm install in ${PLUGIN_ROOT}. Registration failed: ${error.message}`); + } +} + +function setupDomEnvironment() { + if (domEnvironmentReady) return; + let JSDOM; + let VirtualConsole; + try { + ({ JSDOM, VirtualConsole } = require('jsdom')); + } catch (error) { + throw new Error(`WordPress core block registration needs jsdom. Run npm install in ${PLUGIN_ROOT}. Missing dependency: ${error.message}`); + } + const virtualConsole = new VirtualConsole(); + virtualConsole.on('jsdomError', () => {}); + const dom = new JSDOM('', { + url: 'http://localhost/', + virtualConsole, + }); + const win = dom.window; + globalThis.window = win; + globalThis.document = win.document; + Object.defineProperty(globalThis, 'navigator', { value: win.navigator, configurable: true }); + for (const key of ['HTMLElement', 'HTMLAnchorElement', 'HTMLButtonElement', 'HTMLInputElement', 'HTMLTextAreaElement', 'Node', 'Element', 'MutationObserver', 'CustomEvent', 'File', 'Blob', 'DOMParser', 'Range', 'Selection']) { + if (win[key]) globalThis[key] = win[key]; + } + globalThis.getComputedStyle = win.getComputedStyle.bind(win); + win.matchMedia ||= () => ({ + matches: false, + media: '', + onchange: null, + addListener() {}, + removeListener() {}, + addEventListener() {}, + removeEventListener() {}, + dispatchEvent() { return false; }, + }); + globalThis.matchMedia = win.matchMedia; + globalThis.requestAnimationFrame ||= (callback) => setTimeout(callback, 16); + globalThis.cancelAnimationFrame ||= clearTimeout; + globalThis.requestIdleCallback ||= (callback) => setTimeout(() => callback({ didTimeout: false, timeRemaining: () => 50 }), 1); + globalThis.cancelIdleCallback ||= clearTimeout; + domEnvironmentReady = true; +} +let registered = false; +export function ensureBlocksRegistered(workspaceRoot, { blocksDir } = {}) { + if (!registered) { registerWordPressCoreBlocks(); registered = true; } + registerWorkspaceCustomBlocks(workspaceRoot, blocksDir); +} + +export function serializeBlocks(blocks, context) { + return serializeBlockTreeWithWordPress({ version: 2, contract: 'data-only', blocks }, context); +} diff --git a/apps/cli/ai/html-to-blocks-engine/mcp-server.mjs b/apps/cli/ai/html-to-blocks-engine/mcp-server.mjs new file mode 100755 index 0000000000..3c72c90c42 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/mcp-server.mjs @@ -0,0 +1,2148 @@ +#!/usr/bin/env node +// stdout carries the Content-Length-framed MCP protocol; library logging +// (e.g. @wordpress/blocks block-validation diffs) must go to stderr or it +// corrupts the stream. +for (const method of ['log', 'info', 'warn', 'debug']) { + console[method] = (...args) => process.stderr.write(`${args.map(String).join(' ')}\n`); +} + +import fs from 'node:fs'; +import path from 'node:path'; +import { pathToFileURL } from 'node:url'; +import { + PLUGIN_ROOT, isPathInside, resolvePath, resolveWorkspacePath, readIfExists, readJson, + readJsonIfExists, writeFile, writeJson, firstMatch, cleanText, titleCase, + slug, camelName, escapeHtml, escapeAttr, relativeUrl, findFiles, +} from './lib/workspace.mjs'; +import { + DEFAULT_VIEWPORTS, loadCaptureDeps, serveDirectory, capture, captureEditor, + editorComparisonCss, motionFreezeCss, transientOverlayCaptureCss, comparePngs, + launchBrowser, +} from './lib/capture.mjs'; +import { serializeBlockTreeWithWordPress, stripBlockComments, ensureBlocksRegistered } from './lib/wp-serialize.mjs'; +import { DEFAULT_PREVIEW_CONTEXT, EDITOR_SHIM_BLOCKS } from './lib/dynamic-render.mjs'; +import { fixBlockMarkup } from './lib/fix-markup.mjs'; +import { analyzeThemeEvidence } from './theme/evidence.mjs'; +import { inferTemplateParts } from './theme/parts.mjs'; +import { fetchThemeFonts } from './theme/fonts.mjs'; +import { scaffoldBlockTheme } from './theme/scaffold.mjs'; +import { validateBlockTheme } from './theme/validate.mjs'; +import { playgroundRender } from './theme/playground.mjs'; +import { validateContentModel, scaffoldContentModelPlugin } from './content/model.mjs'; +import { auditStandins, checkStandins, hydrateStandins } from './content/standins.mjs'; +import * as profile from './lib/profile.mjs'; + +const TOOLS = [ + { + name: 'create_workspace', + description: 'Create an html-to-blocks workspace with mockup, plan, wordpress, rendered, editor, and report folders.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot', 'prompt'], + properties: { + workspaceRoot: { type: 'string' }, + prompt: { type: 'string' }, + force: { type: 'boolean', default: false }, + }, + }, + }, + { + name: 'import_provided_markup', + description: 'Import an existing HTML/CSS site export into a workspace mockup path instead of generating a new mockup.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot', 'sourceHtmlPath'], + properties: { + workspaceRoot: { type: 'string' }, + sourceHtmlPath: { type: 'string' }, + sourceRoot: { type: 'string' }, + mockupPath: { type: 'string', default: 'mockup/index.html' }, + cssOutPath: { type: 'string', default: 'mockup/style.css' }, + cssPaths: { + type: 'array', + items: { type: 'string' }, + }, + copyAssets: { type: 'boolean', default: true }, + }, + }, + }, + { + name: 'analyze_mockup', + description: 'Analyze mockup/index.html and mockup/style.css into content inventory and CSS selector summaries.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + htmlPath: { type: 'string', default: 'mockup/index.html' }, + cssPath: { type: 'string', default: 'mockup/style.css' }, + }, + }, + }, + { + name: 'scaffold_custom_block', + description: 'Generate a vanilla JavaScript WordPress custom block baseline with block.json, index.js, and style.css.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot', 'name', 'attributes'], + properties: { + workspaceRoot: { type: 'string' }, + name: { type: 'string' }, + title: { type: 'string' }, + category: { type: 'string' }, + description: { type: 'string' }, + form: { type: 'boolean', default: false }, + attributes: { + type: 'array', + items: { + type: 'object', + additionalProperties: true, + required: ['name', 'type'], + properties: { + name: { type: 'string' }, + type: { type: 'string', enum: ['string', 'number', 'boolean', 'array', 'object'] }, + role: { type: 'string' }, + default: {}, + }, + }, + }, + }, + }, + }, + { + name: 'serialize_wordpress_blocks', + description: 'Serialize wordpress/block-tree.json with @wordpress/blocks into canonical wordpress/content.html, frontend rendered/rendered-blocks.html, editor/block-editor.html, and CSS reports.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + treePath: { type: 'string', default: 'wordpress/block-tree.json' }, + contentPath: { type: 'string', default: 'wordpress/content.html' }, + outPath: { type: 'string', default: 'rendered/rendered-blocks.html' }, + editorPath: { type: 'string', default: 'editor/block-editor.html' }, + includeMockupCss: { type: 'boolean', default: false }, + }, + }, + }, + { + name: 'create_block_editor_preview', + description: 'Create a reusable no-build WordPress block editor preview that loads a generated data-only block tree, custom blocks, and CSS sources.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + treePath: { type: 'string', default: 'wordpress/block-tree.json' }, + editorPath: { type: 'string', default: 'editor/block-editor.html' }, + cssPaths: { + type: 'array', + items: { type: 'string' }, + }, + includeMockupCss: { type: 'boolean', default: false }, + validateTree: { type: 'boolean', default: true }, + }, + }, + }, + { + name: 'screenshot_html', + description: 'Capture screenshots for mockup, rendered, editor, or arbitrary workspace HTML files without running a pixel diff.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + outDir: { type: 'string', default: 'visual' }, + targets: { + type: 'array', + items: { + type: 'object', + additionalProperties: false, + required: ['name', 'path'], + properties: { + name: { type: 'string' }, + path: { type: 'string' }, + kind: { type: 'string', enum: ['html', 'editor'], default: 'html' }, + }, + }, + }, + viewports: { + type: 'array', + items: { + type: 'object', + required: ['name', 'width', 'height'], + properties: { + name: { type: 'string' }, + width: { type: 'number' }, + height: { type: 'number' }, + fullPage: { type: 'boolean', default: true }, + }, + }, + }, + }, + }, + }, + { + name: 'compare_html', + description: 'Capture mockup/rendered/editor screenshots, generate pixel diffs, and write a per-page comparison report plus repair tasks. Reports and screenshots are namespaced by the mockup filename, so multi-page comparisons never overwrite each other.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + mockupPath: { type: 'string', default: 'mockup/index.html' }, + renderedPath: { type: 'string', default: 'rendered/rendered-blocks.html' }, + editorPath: { type: 'string', default: 'editor/block-editor.html' }, + reportPath: { type: 'string', description: 'Override the comparison JSON path. Default: reports/comparison.json for index, reports/.comparison.json otherwise.' }, + tasksPath: { type: 'string', description: 'Override the repair-tasks markdown path. Default: reports/repair-tasks.md for index, reports/.repair-tasks.md otherwise.' }, + compareEditor: { type: 'boolean', default: true }, + maxMismatchPercent: { type: 'number', default: 1 }, + maxHeightDelta: { type: 'number', default: 8 }, + viewports: { + type: 'array', + items: { + type: 'object', + required: ['name', 'width', 'height'], + properties: { + name: { type: 'string' }, + width: { type: 'number' }, + height: { type: 'number' }, + fullPage: { type: 'boolean', default: true }, + }, + }, + }, + }, + }, + }, + { + name: 'measure_layout', + description: 'Compare element geometry (offsetTop/height) between the mockup and a rendered or editor page, aligned by selector match order. Localizes vertical drift to specific sections far faster than reading pixel diffs — use it to find WHERE a height delta comes from, then drill with a narrower selector.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + mockupPath: { type: 'string', default: 'mockup/index.html' }, + candidatePath: { type: 'string', default: 'rendered/rendered-blocks.html' }, + candidateKind: { type: 'string', enum: ['html', 'editor'], default: 'html' }, + selector: { type: 'string', description: 'CSS selector evaluated in both pages; matches are aligned by index. Default: main sections plus footer.' }, + viewports: { + type: 'array', + items: { + type: 'object', + required: ['name', 'width', 'height'], + properties: { + name: { type: 'string' }, + width: { type: 'number' }, + height: { type: 'number' }, + }, + }, + }, + }, + }, + }, + { + name: 'validate_content_model', + description: 'Validate an agent-authored WordPress content model JSON for CPT, taxonomy, meta, REST, and seed-content consistency. Writes reports/content-model-validation.json.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + modelPath: { type: 'string', default: 'content-model/content-model.json' }, + reportPath: { type: 'string', default: 'reports/content-model-validation.json' }, + }, + }, + }, + { + name: 'scaffold_content_model_plugin', + description: 'Generate an installable WordPress plugin from content-model/content-model.json. The plugin registers CPTs, taxonomies, post meta, and submission REST routes while active, plus a Tools screen to import/remove generated seed content with state and collision reporting.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + modelPath: { type: 'string', default: 'content-model/content-model.json' }, + reportPath: { type: 'string', default: 'reports/content-model-validation.json' }, + outDir: { type: 'string', default: 'content-model/plugin' }, + }, + }, + }, + { + name: 'audit_standins', + description: 'List every stand-in mark (attrs.metadata.standin) across the page block trees: query loops, comments, and per-field marks. Validates them against content-model/content-model.json when present. Writes reports/standins.json so the run can see which regions are still static placeholders awaiting hydration.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + modelPath: { type: 'string', default: 'content-model/content-model.json' }, + reportPath: { type: 'string', default: 'reports/standins.json' }, + }, + }, + }, + { + name: 'hydrate_standins', + description: 'Swap marked stand-ins into real dynamic core blocks: query stand-ins become core/query + core/post-template (with field marks turned into core/post-title/featured-image/terms/excerpt/date), comments stand-ins become core/comments. Preserves className/style so lifted theme CSS still applies. Run AFTER the html-to-blocks visual gate passes and the content-model plugin exists; the result feeds blocks-to-theme. Backs up the pre-hydration trees and writes reports/standins-hydration.json.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + modelPath: { type: 'string', default: 'content-model/content-model.json' }, + }, + }, + }, + { + name: 'analyze_theme_evidence', + description: 'Scan all page block trees and workspace CSS into a style-evidence report (recurring colors/fonts/spacing with occurrence counts, custom properties, support usage, lift buckets per CSS rule). Facts only — the agent decides what lifts into theme.json.', + inputSchema: { type: 'object', additionalProperties: false, required: ['workspaceRoot'], properties: { workspaceRoot: { type: 'string' } } }, + }, + { + name: 'infer_template_parts', + description: 'Group top-level subtrees across pages by exact and structural hashes into template-part candidates with occurrence, position, tag evidence and per-page variance tables. No header/footer assumptions — evidence only.', + inputSchema: { type: 'object', additionalProperties: false, required: ['workspaceRoot'], properties: { workspaceRoot: { type: 'string' } } }, + }, + { + name: 'fetch_theme_fonts', + description: 'Resolve the mockup CSS Google Fonts @import to local woff2 files under the theme assets and return ready theme.json fontFace entries. Fails explicitly offline.', + inputSchema: { type: 'object', additionalProperties: false, required: ['workspaceRoot', 'slug'], properties: { workspaceRoot: { type: 'string' }, slug: { type: 'string' }, importUrl: { type: 'string' } } }, + }, + { + name: 'scaffold_block_theme', + description: 'Write the block theme (style.css, theme.json, templates incl. default archive/single/404, parts, functions.php, assets), the blocks plugin, and the content plugin payload from agent-authored decisions. Owns serialization and the mechanical rewrites (preset refs, --wp--custom-- renames, permalinks, media placeholders).', + inputSchema: { type: 'object', additionalProperties: false, + required: ['workspaceRoot', 'slug', 'name', 'tokenMap', 'themeSettings', 'themeStyles', 'parts', 'templates', 'pages'], + properties: { + workspaceRoot: { type: 'string' }, slug: { type: 'string' }, name: { type: 'string' }, description: { type: 'string' }, + tokenMap: { type: 'object' }, themeSettings: { type: 'object' }, themeStyles: { type: 'object' }, + fontFamilies: { type: 'array' }, customCss: { type: 'string' }, + parts: { type: 'array' }, + templates: { type: 'object' }, + pages: { type: 'array', description: 'Page manifest entries: { page, slug, title, front?, stripIndexes?, sourceFile? }. sourceFile is the original mockup filename (e.g. "Bucharest Feline Show.html"); it keys the permalink link map so cross-page rewrites resolve — required whenever the mockup filename differs from ".html".' }, + mediaMap: { type: 'object' }, + } }, + }, + { + name: 'validate_block_theme', + description: 'Static gate: theme.json schema (vendored), template/part parse with all blocks registered, header/file/ref/fontFace/remote-url/payload checks. Writes reports/theme-validation.json.', + inputSchema: { type: 'object', additionalProperties: false, required: ['workspaceRoot', 'slug'], properties: { workspaceRoot: { type: 'string' }, slug: { type: 'string' } } }, + }, + { + name: 'playground_render', + description: 'Boot the theme + plugins in WordPress Playground, import the pages through the content plugin, screenshot every page logged-out at both viewports, and diff against the mockups. Writes reports/theme-comparison.json with the standard thresholds.', + inputSchema: { type: 'object', additionalProperties: false, required: ['workspaceRoot', 'slug'], properties: { workspaceRoot: { type: 'string' }, slug: { type: 'string' }, port: { type: 'number' }, maxMismatchPercent: { type: 'number' }, maxHeightDelta: { type: 'number' } } }, + }, + { + name: 'fix_block_markup', + description: 'Canonicalize block markup: parse, recreate every block from its attributes, and re-serialize so the markup byte-matches save() output, eliminating editor block-validation errors. Pass raw markup, or workspace-relative file paths to fix in place. Registers the workspace custom blocks before parsing.', + inputSchema: { + type: 'object', + additionalProperties: false, + required: ['workspaceRoot'], + properties: { + workspaceRoot: { type: 'string' }, + markup: { type: 'string' }, + paths: { type: 'array', items: { type: 'string' } }, + }, + }, + }, +]; + +const handlers = { + create_workspace: createWorkspace, + import_provided_markup: importProvidedMarkup, + analyze_mockup: analyzeMockup, + scaffold_custom_block: scaffoldCustomBlock, + serialize_wordpress_blocks: serializeWordPressBlocks, + create_block_editor_preview: createBlockEditorPreview, + screenshot_html: screenshotHtml, + compare_html: compareHtml, + measure_layout: measureLayout, + validate_content_model: (args) => validateContentModel(args), + scaffold_content_model_plugin: (args) => scaffoldContentModelPlugin(args), + audit_standins: (args) => auditStandinsHandler(args), + hydrate_standins: (args) => hydrateStandinsHandler(args), + analyze_theme_evidence: (args) => analyzeThemeEvidence(args), + infer_template_parts: (args) => inferTemplateParts(args), + fetch_theme_fonts: (args) => { + const workspaceRoot = resolvePath(args.workspaceRoot); + return fetchThemeFonts({ + ...args, + sourceCss: readIfExists(path.join(workspaceRoot, 'mockup/style.css')) || readIfExists(path.join(workspaceRoot, 'wordpress/style.css')), + targetDir: path.join(workspaceRoot, 'theme', args.slug, 'assets/fonts'), + }); + }, + scaffold_block_theme: (args) => scaffoldBlockTheme(args), + validate_block_theme: (args) => validateBlockTheme(args), + playground_render: (args) => playgroundRender(args), + fix_block_markup: (args) => { + const workspaceRoot = resolvePath(args.workspaceRoot); + ensureBlocksRegistered(workspaceRoot); + if (args.markup !== undefined) { + return fixBlockMarkup(args.markup); + } + if (!Array.isArray(args.paths) || args.paths.length === 0) { + throw new Error('fix_block_markup needs either markup or a non-empty paths array.'); + } + const results = args.paths.map((rel) => { + const filePath = resolveWorkspacePath(workspaceRoot, rel); + const result = fixBlockMarkup(readIfExists(filePath)); + if (result.changed) writeFile(filePath, result.markup); + return { path: rel, changed: result.changed, issues: result.issues }; + }); + return { results, next: 'Re-run validate_block_theme to confirm the markup is clean.' }; + }, +}; + +export { handlers, TOOLS }; + +let buffer = Buffer.alloc(0); + +process.stdin.on('data', (chunk) => { + buffer = Buffer.concat([buffer, chunk]); + processIncoming(); +}); + +function processIncoming() { + while (buffer.length) { + const headerEnd = buffer.indexOf('\r\n\r\n'); + if (headerEnd >= 0) { + const header = buffer.slice(0, headerEnd).toString('utf8'); + const match = header.match(/Content-Length:\s*(\d+)/i); + if (!match) throw new Error('Missing Content-Length header.'); + const length = Number(match[1]); + const messageStart = headerEnd + 4; + if (buffer.length < messageStart + length) return; + const raw = buffer.slice(messageStart, messageStart + length).toString('utf8'); + buffer = buffer.slice(messageStart + length); + void handleMessage(JSON.parse(raw)); + continue; + } + + const newline = buffer.indexOf('\n'); + if (newline < 0) return; + const line = buffer.slice(0, newline).toString('utf8').trim(); + buffer = buffer.slice(newline + 1); + if (line) void handleMessage(JSON.parse(line)); + } +} + +async function handleMessage(message) { + if (!message || typeof message !== 'object') return; + if (!Object.prototype.hasOwnProperty.call(message, 'id')) return; + + try { + if (message.method === 'initialize') { + return send({ + jsonrpc: '2.0', + id: message.id, + result: { + protocolVersion: '2024-11-05', + capabilities: { tools: {} }, + serverInfo: { name: 'html-to-blocks', version: '0.1.0' }, + }, + }); + } + + if (message.method === 'tools/list') { + return send({ jsonrpc: '2.0', id: message.id, result: { tools: TOOLS } }); + } + + if (message.method === 'tools/call') { + const { name, arguments: args = {} } = message.params || {}; + if (!handlers[name]) throw new Error(`Unknown tool: ${name}`); + // Per-tool wall-clock profiling. mark/measure spans the await so async + // handlers are timed correctly; meta carries arg/result JSON sizes. All + // profiler output goes to files/stderr only — never the stdout protocol + // stream below. flush() persists incrementally for a long-lived server. + profile.setRunMeta({ tool: name }); + const _argBytes = profile.isOn() ? Buffer.byteLength(JSON.stringify(args ?? null), 'utf8') : 0; + const _toolMark = profile.mark('tool.' + name); + let result; + try { + result = await handlers[name](args); + } finally { + if (_toolMark) { + const _resultBytes = Buffer.byteLength(JSON.stringify(result ?? null), 'utf8'); + profile.measure(_toolMark, { argBytes: _argBytes, resultBytes: _resultBytes }); + profile.flush(); + } + } + return send({ + jsonrpc: '2.0', + id: message.id, + result: { + content: [{ type: 'text', text: JSON.stringify(result, null, 2) }], + }, + }); + } + + if (message.method === 'ping') { + return send({ jsonrpc: '2.0', id: message.id, result: {} }); + } + + throw new Error(`Unsupported method: ${message.method}`); + } catch (error) { + send({ + jsonrpc: '2.0', + id: message.id, + error: { + code: -32000, + message: error instanceof Error ? error.message : String(error), + }, + }); + } +} + +function send(payload) { + const body = JSON.stringify(payload); + process.stdout.write(`Content-Length: ${Buffer.byteLength(body, 'utf8')}\r\n\r\n${body}`); +} + +async function createWorkspace(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + if (fs.existsSync(workspaceRoot) && !args.force) { + throw new Error(`Workspace exists: ${workspaceRoot}. Pass force=true to reuse it.`); + } + + for (const dir of ['mockup', 'analysis', 'plan', 'content-model', 'wordpress/blocks', 'rendered', 'editor', 'reports', 'visual']) { + fs.mkdirSync(path.join(workspaceRoot, dir), { recursive: true }); + } + + writeFile(path.join(workspaceRoot, 'brief.md'), `${args.prompt.trim()}\n`); + writeFile(path.join(workspaceRoot, 'mockup/index.html'), starterHtml(args.prompt)); + writeFile(path.join(workspaceRoot, 'mockup/style.css'), starterCss()); + writeFile(path.join(workspaceRoot, 'wordpress/style.css'), '/* Generated WordPress preview CSS belongs here. Do not import mockup/style.css. */\n'); + writeJson(path.join(workspaceRoot, 'wordpress/block-tree.json'), { version: 2, contract: 'data-only', blocks: [] }); + writeFile(path.join(workspaceRoot, 'wordpress/content.html'), '\n'); + writeJson(path.join(workspaceRoot, 'plan/block-plan.json'), { sections: [], customBlocks: [] }); + writeJson(path.join(workspaceRoot, 'content-model/content-model.json'), { + version: 1, + plugin: { + slug: `${slug(path.basename(workspaceRoot)) || 'site'}-content`, + name: `${titleCase(path.basename(workspaceRoot)) || 'Site'} Content Model`, + }, + postTypes: [], + taxonomies: [], + }); + copyReference('design-prompt.md', path.join(workspaceRoot, 'plan/design-prompt.md')); + + return { + workspaceRoot, + files: { + brief: path.join(workspaceRoot, 'brief.md'), + mockupHtml: path.join(workspaceRoot, 'mockup/index.html'), + mockupCss: path.join(workspaceRoot, 'mockup/style.css'), + blockPlan: path.join(workspaceRoot, 'plan/block-plan.json'), + blockTree: path.join(workspaceRoot, 'wordpress/block-tree.json'), + blockContent: path.join(workspaceRoot, 'wordpress/content.html'), + contentModel: path.join(workspaceRoot, 'content-model/content-model.json'), + editorPreview: path.join(workspaceRoot, 'editor/block-editor.html'), + wordpressCss: path.join(workspaceRoot, 'wordpress/style.css'), + }, + next: 'Replace the starter mockup with the designed HTML/CSS/JS, then call analyze_mockup. Assemble blocks in wordpress/block-tree.json.', + }; +} + +async function importProvidedMarkup(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const sourceHtmlPath = resolvePath(args.sourceHtmlPath); + if (!fs.existsSync(sourceHtmlPath)) { + throw new Error(`sourceHtmlPath does not exist: ${sourceHtmlPath}`); + } + + const sourceRoot = args.sourceRoot ? resolvePath(args.sourceRoot) : path.dirname(sourceHtmlPath); + if (!fs.existsSync(sourceRoot) || !fs.statSync(sourceRoot).isDirectory()) { + throw new Error(`sourceRoot is not a directory: ${sourceRoot}`); + } + if (!isPathInside(sourceRoot, sourceHtmlPath)) { + throw new Error(`sourceHtmlPath must be inside sourceRoot: ${sourceHtmlPath}`); + } + + const mockupPath = resolveWorkspacePath(workspaceRoot, args.mockupPath || 'mockup/index.html'); + const cssOutPath = resolveWorkspacePath(workspaceRoot, args.cssOutPath || 'mockup/style.css'); + fs.mkdirSync(path.dirname(mockupPath), { recursive: true }); + fs.mkdirSync(path.dirname(cssOutPath), { recursive: true }); + + if (args.copyAssets !== false) { + copyProvidedSourceRoot(sourceRoot, path.dirname(mockupPath)); + } else { + fs.copyFileSync(sourceHtmlPath, mockupPath); + } + + const copiedHtmlPath = path.join(path.dirname(mockupPath), path.relative(sourceRoot, sourceHtmlPath)); + if (path.resolve(copiedHtmlPath) !== path.resolve(mockupPath)) { + fs.copyFileSync(sourceHtmlPath, mockupPath); + } + + const importedHtml = fs.readFileSync(mockupPath, 'utf8'); + const stylesheetPaths = providedStylesheetPaths({ sourceRoot, sourceHtmlPath, html: importedHtml, cssPaths: args.cssPaths }); + const cssBundle = stylesheetPaths + .map((file) => `/* ${path.relative(sourceRoot, file)} */\n${fs.readFileSync(file, 'utf8').trim()}\n`) + .join('\n'); + writeFile(cssOutPath, cssBundle || '/* No local stylesheets discovered from provided markup. */\n'); + + const pages = discoverProvidedPages(sourceRoot, sourceHtmlPath, path.dirname(mockupPath), workspaceRoot); + + return { + workspaceRoot, + sourceHtmlPath, + sourceRoot, + mockupPath, + cssOutPath, + copiedAssets: args.copyAssets !== false, + stylesheets: stylesheetPaths.map((file) => path.relative(sourceRoot, file)), + pages, + next: pages.length > 1 + ? 'Multi-page export detected. Call analyze_mockup per page (htmlPath), plan shared blocks once, then use the suggested per-page treePath/renderedPath/editorPath/reportPath when calling serialize_wordpress_blocks, create_block_editor_preview, compare_html, and measure_layout. Every page must pass comparison before the run is complete.' + : 'Call analyze_mockup on the imported mockup, then plan and assemble the block tree without generating a replacement HTML mockup.', + }; +} + +function discoverProvidedPages(sourceRoot, primaryHtmlPath, mockupRoot, workspaceRoot) { + const htmlFiles = fs.readdirSync(sourceRoot, { withFileTypes: true }) + .filter((entry) => entry.isFile() && entry.name.toLowerCase().endsWith('.html')) + .map((entry) => entry.name) + .sort((a, b) => { + if (path.join(sourceRoot, a) === primaryHtmlPath) return -1; + if (path.join(sourceRoot, b) === primaryHtmlPath) return 1; + return a.localeCompare(b); + }); + + return htmlFiles.map((name) => { + const pageSlug = slug(path.basename(name, path.extname(name))) || 'page'; + return { + page: pageSlug, + sourceFile: name, + primary: path.join(sourceRoot, name) === primaryHtmlPath, + mockupPath: path.relative(workspaceRoot, path.join(mockupRoot, name)), + suggested: { + treePath: `wordpress/pages/${pageSlug}.block-tree.json`, + contentPath: `wordpress/pages/${pageSlug}.content.html`, + renderedPath: `rendered/${pageSlug}.html`, + editorPath: `editor/${pageSlug}.html`, + reportPath: `reports/${pageSlug}.comparison.json`, + tasksPath: `reports/${pageSlug}.repair-tasks.md`, + }, + }; + }); +} + +function copyProvidedSourceRoot(sourceRoot, mockupRoot) { + fs.mkdirSync(mockupRoot, { recursive: true }); + for (const entry of fs.readdirSync(sourceRoot, { withFileTypes: true })) { + const sourcePath = path.join(sourceRoot, entry.name); + const targetPath = path.join(mockupRoot, entry.name); + if (entry.isDirectory()) { + fs.cpSync(sourcePath, targetPath, { recursive: true }); + } else if (entry.isFile()) { + fs.copyFileSync(sourcePath, targetPath); + } + } +} + +function providedStylesheetPaths({ sourceRoot, sourceHtmlPath, html, cssPaths = [] }) { + const explicit = Array.isArray(cssPaths) ? cssPaths : []; + const hrefs = [...html.matchAll(/]*rel=["'][^"']*stylesheet[^"']*["'][^>]*>/gi)] + .map((match) => firstMatch(match[0], /\bhref=["']([^"']+)["']/i)) + .filter(Boolean); + const candidates = [...explicit, ...hrefs]; + const baseDir = path.dirname(sourceHtmlPath); + const seen = new Set(); + const files = []; + + for (const candidate of candidates) { + if (isRemoteUrl(candidate) || candidate.startsWith('#')) continue; + const withoutQuery = candidate.split(/[?#]/)[0]; + const resolved = path.resolve(baseDir, withoutQuery); + if (!isPathInside(sourceRoot, resolved)) continue; + if (!fs.existsSync(resolved) || !fs.statSync(resolved).isFile()) continue; + if (seen.has(resolved)) continue; + seen.add(resolved); + files.push(resolved); + } + + return files; +} + +function isRemoteUrl(value) { + return /^(?:[a-z][a-z0-9+.-]*:)?\/\//i.test(String(value || '')) || /^(?:data|mailto|tel):/i.test(String(value || '')); +} + +async function analyzeMockup(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const htmlPath = path.join(workspaceRoot, args.htmlPath || 'mockup/index.html'); + const cssPath = path.join(workspaceRoot, args.cssPath || 'mockup/style.css'); + const html = fs.readFileSync(htmlPath, 'utf8'); + const css = fs.existsSync(cssPath) ? fs.readFileSync(cssPath, 'utf8') : ''; + const inventory = extractInventory(html); + const analysis = { + title: firstMatch(html, /]*>([\s\S]*?)<\/title>/i) || '', + sections: inventory.sections.map((section) => ({ + id: section.id, + selector: section.selector, + tagName: section.tagName, + className: section.className, + heading: section.headings[0]?.content || '', + textLength: section.text.length, + features: { + forms: section.forms.length, + links: section.links.length, + cards: section.cards.length, + headings: section.headings.length, + }, + })), + css: { + customProperties: extractCustomProperties(css), + selectors: extractSelectors(css), + }, + }; + + writeJson(path.join(workspaceRoot, 'analysis/content-inventory.json'), inventory); + writeJson(path.join(workspaceRoot, 'analysis/analysis.json'), analysis); + + return { + analysisPath: path.join(workspaceRoot, 'analysis/analysis.json'), + inventoryPath: path.join(workspaceRoot, 'analysis/content-inventory.json'), + sections: analysis.sections.length, + forms: inventory.forms.length, + links: inventory.links.length, + selectors: analysis.css.selectors.length, + }; +} + +async function scaffoldCustomBlock(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const name = String(args.name || '').trim(); + if (!/^[a-z0-9-]+\/[a-z0-9-]+$/.test(name)) { + throw new Error('Block name must look like namespace/block-name.'); + } + + const slug = name.split('/')[1]; + const blockRoot = path.join(workspaceRoot, 'wordpress/blocks', slug); + fs.mkdirSync(blockRoot, { recursive: true }); + const attributes = normalizeAttributes(args.attributes || [], Boolean(args.form)); + const title = args.title || titleCase(slug); + const form = Boolean(args.form) || looksFormLike(name, attributes); + + writeJson(path.join(blockRoot, 'block.json'), { + apiVersion: 3, + name, + title, + category: args.category || (form ? 'forms' : 'design'), + description: args.description || `${title} custom block generated by the html-to-blocks workflow.`, + editorScript: 'file:./index.js', + style: 'file:./style.css', + attributes: blockJsonAttributes(attributes), + supports: defaultSupports(), + }); + writeFile(path.join(blockRoot, 'index.js'), generateIndexJs({ name, title, slug, attributes, form })); + writeFile(path.join(blockRoot, 'style.css'), generateBlockCss({ name, slug, form })); + + return { + blockRoot, + files: ['block.json', 'index.js', 'style.css'].map((file) => path.join(blockRoot, file)), + next: 'Edit the generated block source to match the mockup component exactly, then reference it from wordpress/block-tree.json.', + }; +} + +async function serializeWordPressBlocks(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const treePath = path.join(workspaceRoot, args.treePath || 'wordpress/block-tree.json'); + const contentPath = path.join(workspaceRoot, args.contentPath || 'wordpress/content.html'); + const outPath = path.join(workspaceRoot, args.outPath || 'rendered/rendered-blocks.html'); + const editorPath = path.join(workspaceRoot, args.editorPath || 'editor/block-editor.html'); + const treeExists = fs.existsSync(treePath); + const tree = treeExists ? readJson(treePath) : null; + const previewContext = readPreviewContext(workspaceRoot); + // content.html is the canonical (un-shimmed) WordPress block markup. The + // rendered/ preview is serialized with dynamic-block shims so navigation, + // search, site-title, pagination etc. actually show their frontend HTML — + // letting the visual gate pass on REAL core blocks instead of custom stand-ins. + const blockMarkup = treeExists + ? serializeBlockTreeWithWordPress(tree, { workspaceRoot }) + : fs.readFileSync(contentPath, 'utf8'); + const renderedMarkup = treeExists + ? serializeBlockTreeWithWordPress(tree, { workspaceRoot }, { shimDynamic: true, previewContext }) + : blockMarkup; + const cssSources = workspaceCssSources(workspaceRoot, args); + const styleAudit = auditStyleUsage(tree, cssSources); + + if (treeExists) writeFile(contentPath, `${blockMarkup.trim()}\n`); + writeFile(outPath, renderedPreviewHtml('Rendered WordPress Blocks', path.dirname(outPath), cssSources, stripBlockComments(renderedMarkup))); + if (treeExists) writeFile(editorPath, editorPreviewHtml({ workspaceRoot, editorPath, treePath, cssSources })); + writeJson(path.join(workspaceRoot, 'reports/style-audit.json'), styleAudit); + return { + treePath: treeExists ? treePath : null, + contentPath, + renderedPath: outPath, + editorPath: treeExists ? editorPath : null, + cssSources: cssSources.map((source) => source.relativePath), + styleAuditPath: path.join(workspaceRoot, 'reports/style-audit.json'), + styleAudit, + next: 'Call compare_html, inspect rendered and editor screenshots/diffs, then write repair tasks against wordpress/block-tree.json, custom block edit/save code, or CSS.', + }; +} + +// Page block-tree files, mirroring loadPageTrees: wordpress/pages/*.block-tree.json +// when present, else the single wordpress/block-tree.json. +function pageTreeFiles(workspaceRoot) { + const pagesDir = path.join(workspaceRoot, 'wordpress/pages'); + if (fs.existsSync(pagesDir)) { + return fs.readdirSync(pagesDir) + .filter((f) => f.endsWith('.block-tree.json')) + .sort() + .map((f) => ({ page: f.replace(/\.block-tree\.json$/, ''), file: path.join(pagesDir, f) })); + } + const single = path.join(workspaceRoot, 'wordpress/block-tree.json'); + return fs.existsSync(single) ? [{ page: 'index', file: single }] : []; +} + +function auditStandinsHandler(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const pages = pageTreeFiles(workspaceRoot).map(({ page, file }) => ({ page, tree: readJson(file) })); + const standins = auditStandins(pages); + const model = readJsonIfExists(path.join(workspaceRoot, args.modelPath || 'content-model/content-model.json')); + const errors = model ? checkStandins(standins, model) : []; + const reportPath = path.join(workspaceRoot, args.reportPath || 'reports/standins.json'); + const byKind = standins.reduce((acc, s) => { acc[s.kind] = (acc[s.kind] || 0) + 1; return acc; }, {}); + const report = { count: standins.length, byKind, modelChecked: Boolean(model), errors, standins }; + writeJson(reportPath, report); + return { + reportPath, + count: standins.length, + byKind, + errors, + next: errors.length + ? 'Fix the stand-in marks or the content model, then re-run audit_standins.' + : 'After the html-to-blocks visual gate passes and the content-model plugin exists, run hydrate_standins.', + }; +} + +function hydrateStandinsHandler(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const files = pageTreeFiles(workspaceRoot); + const pages = files.map(({ page, file }) => ({ page, file, tree: readJson(file) })); + const standins = auditStandins(pages.map(({ page, tree }) => ({ page, tree }))); + const model = readJsonIfExists(path.join(workspaceRoot, args.modelPath || 'content-model/content-model.json')); + if (model) { + const errors = checkStandins(standins, model); + if (errors.length) throw new Error(`Cannot hydrate: ${errors.join(' ')}`); + } + const { trees, swaps } = hydrateStandins(pages.map(({ page, tree }) => ({ page, tree }))); + const backupDir = path.join(workspaceRoot, 'wordpress/standin-backup'); + const fileByPage = new Map(pages.map(({ page, file }) => [page, file])); + for (const { page, tree } of trees) { + const original = readJson(fileByPage.get(page)); + writeJson(path.join(backupDir, `${page}.json`), original); + writeJson(fileByPage.get(page), tree); + } + const reportPath = path.join(workspaceRoot, 'reports/standins-hydration.json'); + writeJson(reportPath, { swaps, backupDir, pages: trees.map((t) => t.page) }); + return { + reportPath, + backupDir, + swaps, + next: swaps.length + ? 'Stand-ins swapped to dynamic core blocks. Run blocks-to-theme; the playground gate renders them against the seeded content.' + : 'No stand-in marks found; page trees unchanged.', + }; +} + +async function createBlockEditorPreview(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const treePath = resolveWorkspacePath(workspaceRoot, args.treePath || 'wordpress/block-tree.json'); + const editorPath = resolveWorkspacePath(workspaceRoot, args.editorPath || 'editor/block-editor.html'); + if (!fs.existsSync(treePath)) { + throw new Error(`Block tree does not exist: ${treePath}`); + } + + const tree = readJson(treePath); + if (args.validateTree !== false) { + serializeBlockTreeWithWordPress(tree, { workspaceRoot }); + } + + const cssSources = workspaceCssSources(workspaceRoot, args); + writeFile(editorPath, editorPreviewHtml({ workspaceRoot, editorPath, treePath, cssSources })); + + return { + treePath, + editorPath, + cssSources: cssSources.map((source) => source.relativePath), + customBlocks: findFiles(path.join(workspaceRoot, 'wordpress/blocks'), 'index.js') + .map((file) => path.relative(workspaceRoot, path.dirname(file))), + next: 'Open the editor preview in a local static server or call screenshot_html with kind="editor" to inspect the editable block tree.', + }; +} + +function workspaceCssSources(workspaceRoot, args = {}) { + const cssPaths = Array.isArray(args.cssPaths) && args.cssPaths.length + ? args.cssPaths.map((cssPath) => resolveWorkspacePath(workspaceRoot, cssPath)) + : [ + ...(args.includeMockupCss ? [path.join(workspaceRoot, 'mockup/style.css')] : []), + path.join(workspaceRoot, 'wordpress/style.css'), + ...findFiles(path.join(workspaceRoot, 'wordpress/blocks'), 'style.css'), + ]; + + return cssPaths + .map((file) => cssSource(workspaceRoot, file)) + .filter((source) => source.css); +} + +function cssSource(workspaceRoot, filePath) { + const css = readIfExists(filePath); + return { + path: filePath, + relativePath: path.relative(workspaceRoot, filePath), + css, + }; +} + +// Preview values for entity-backed dynamic blocks (site title/logo, post date, +// post terms). Lets site-title etc. render with real-looking text on both +// surfaces. Optional: wordpress/preview-context.json overrides the defaults. +function readPreviewContext(workspaceRoot) { + const fromFile = readJsonIfExists(path.join(workspaceRoot, 'wordpress/preview-context.json')) || {}; + return { ...DEFAULT_PREVIEW_CONTEXT, ...fromFile }; +} + +// The dynamic-render module is pure (no imports); strip its `export` keywords +// and inline it so the editor canvas can render the entity-backed blocks +// identically to the static preview. navigation/search/pagination/etc. keep +// their native edit(); only EDITOR_SHIM_BLOCKS are overridden. +function dynamicRenderBrowserScript(previewContext) { + const source = readIfExists(path.join(PLUGIN_ROOT, 'tools/lib/dynamic-render.mjs')) + .replace(/^export\s+/gm, ''); + return ``; +} + +function editorPreviewHtml({ workspaceRoot, editorPath, treePath, cssSources }) { + const editorDir = path.dirname(editorPath); + const tree = readJson(treePath); + const cssLinks = cssSources + .map((source) => ``) + .join('\n '); + const customBlockAssets = findFiles(path.join(workspaceRoot, 'wordpress/blocks'), 'index.js') + .map((file) => ({ + script: relativeUrl(editorDir, file), + source: readIfExists(file), + metadata: readJsonIfExists(path.join(path.dirname(file), 'block.json')), + })); + const scriptTags = wordpressBrowserScripts() + .map((src) => ``) + .join('\n '); + const dynamicShimScript = dynamicRenderBrowserScript(readPreviewContext(workspaceRoot)); + + return ` + + + + + Editable WordPress Block Tree + + + ${cssLinks} + + + +
    +
    Loading WordPress block editor...
    +
    + ${scriptTags} + ${dynamicShimScript} + + + + +`; +} + +function wordpressBrowserScripts() { + const base = 'https://s.w.org/wp-includes/js/dist'; + return [ + `${base}/vendor/react.min.js`, + `${base}/vendor/react-dom.min.js`, + `${base}/vendor/react-jsx-runtime.min.js`, + `${base}/vendor/moment.min.js`, + `${base}/element.min.js`, + `${base}/hooks.min.js`, + `${base}/deprecated.min.js`, + `${base}/i18n.min.js`, + `${base}/warning.min.js`, + `${base}/escape-html.min.js`, + `${base}/is-shallow-equal.min.js`, + `${base}/priority-queue.min.js`, + `${base}/private-apis.min.js`, + `${base}/compose.min.js`, + `${base}/dom.min.js`, + `${base}/dom-ready.min.js`, + `${base}/html-entities.min.js`, + `${base}/url.min.js`, + `${base}/a11y.min.js`, + `${base}/blob.min.js`, + `${base}/autop.min.js`, + `${base}/shortcode.min.js`, + `${base}/token-list.min.js`, + `${base}/redux-routine.min.js`, + `${base}/data.min.js`, + `${base}/rich-text.min.js`, + `${base}/date.min.js`, + `${base}/primitives.min.js`, + `${base}/keycodes.min.js`, + `${base}/keyboard-shortcuts.min.js`, + `${base}/notices.min.js`, + `${base}/components.min.js`, + `${base}/preferences.min.js`, + `${base}/viewport.min.js`, + `${base}/api-fetch.min.js`, + `${base}/upload-media.min.js`, + `${base}/block-serialization-default-parser.min.js`, + `${base}/blocks.min.js`, + `${base}/undo-manager.min.js`, + `${base}/commands.min.js`, + `${base}/style-engine.min.js`, + `${base}/server-side-render.min.js`, + `${base}/wordcount.min.js`, + `${base}/block-editor.min.js`, + `${base}/core-data.min.js`, + `${base}/patterns.min.js`, + `${base}/block-library.min.js`, + ]; +} + +function auditStyleUsage(tree, cssSources) { + const blocks = collectTreeBlocks(tree); + const supportKeys = ['style', 'layout', 'align', 'backgroundColor', 'textColor', 'gradient', 'fontSize', 'borderColor']; + const blocksWithSupportAttrs = blocks.filter((block) => { + const attrs = block.attrs || block.attributes || {}; + return supportKeys.some((key) => attrs[key] !== undefined); + }); + const stylePaths = new Map(); + for (const block of blocks) { + const attrs = block.attrs || block.attributes || {}; + collectStylePaths(attrs.style, '', stylePaths); + } + const cssFiles = cssSources.map((source) => ({ + path: source.relativePath, + bytes: Buffer.byteLength(source.css, 'utf8'), + lines: source.css.split(/\r?\n/).filter((line) => line.trim()).length, + rules: countCssRules(source.css), + })); + const pageCss = cssFiles.filter((file) => file.path === 'wordpress/style.css'); + const blockCss = cssFiles.filter((file) => file.path.startsWith('wordpress/blocks/')); + return { + generatedAt: new Date().toISOString(), + blockCount: blocks.length, + coreBlockCount: blocks.filter((block) => (block.blockName || block.name || '').startsWith('core/')).length, + customBlockCount: blocks.filter((block) => !(block.blockName || block.name || '').startsWith('core/')).length, + blocksWithSupportAttrs: blocksWithSupportAttrs.length, + supportStyledPercent: blocks.length ? Number(((blocksWithSupportAttrs.length / blocks.length) * 100).toFixed(2)) : 0, + supportAttributeCounts: Object.fromEntries( + supportKeys.map((key) => [key, blocks.filter((block) => (block.attrs || block.attributes || {})[key] !== undefined).length]) + ), + stylePaths: [...stylePaths.entries()].sort().map(([pathKey, count]) => ({ path: pathKey, count })), + css: { + totalBytes: cssFiles.reduce((sum, file) => sum + file.bytes, 0), + totalLines: cssFiles.reduce((sum, file) => sum + file.lines, 0), + totalRules: cssFiles.reduce((sum, file) => sum + file.rules, 0), + pageCssBytes: pageCss.reduce((sum, file) => sum + file.bytes, 0), + pageCssLines: pageCss.reduce((sum, file) => sum + file.lines, 0), + pageCssRules: pageCss.reduce((sum, file) => sum + file.rules, 0), + blockCssBytes: blockCss.reduce((sum, file) => sum + file.bytes, 0), + blockCssLines: blockCss.reduce((sum, file) => sum + file.lines, 0), + blockCssRules: blockCss.reduce((sum, file) => sum + file.rules, 0), + files: cssFiles, + }, + guidance: [ + 'Prefer attrs.style, layout, align, color, spacing, typography, border, and dimensions support settings for block-level design.', + 'Keep wordpress/style.css for tokens, document-level defaults, responsive grid behavior, and selectors that WordPress supports cannot express.', + 'Keep wordpress/blocks/*/style.css scoped to custom block internals such as pseudo-elements, nested controls, horizontal rails, and ornamental geometry.', + ], + }; +} + +function collectTreeBlocks(tree) { + const roots = Array.isArray(tree) ? tree : tree?.blocks || []; + const blocks = []; + const visit = (block) => { + if (!block || typeof block !== 'object') return; + blocks.push(block); + for (const child of block.innerBlocks || []) visit(child); + }; + for (const block of roots) visit(block); + return blocks; +} + +function collectStylePaths(value, prefix, out) { + if (!value || typeof value !== 'object') return; + for (const [key, child] of Object.entries(value)) { + const next = prefix ? `${prefix}.${key}` : key; + if (child && typeof child === 'object' && !Array.isArray(child)) collectStylePaths(child, next, out); + else out.set(next, (out.get(next) || 0) + 1); + } +} + +function countCssRules(css) { + return (String(css || '').match(/\{[^{}]*\}/g) || []).length; +} + +async function compareHtml(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const { chromium, PNG, pixelmatch } = await loadCaptureDeps(PLUGIN_ROOT); + + const mockupPath = path.join(workspaceRoot, args.mockupPath || 'mockup/index.html'); + const renderedPath = path.join(workspaceRoot, args.renderedPath || 'rendered/rendered-blocks.html'); + const editorPath = path.join(workspaceRoot, args.editorPath || 'editor/block-editor.html'); + const shouldCompareEditor = args.compareEditor !== false && fs.existsSync(editorPath); + const outDir = path.join(workspaceRoot, 'visual'); + fs.mkdirSync(outDir, { recursive: true }); + const viewports = Array.isArray(args.viewports) && args.viewports.length ? args.viewports : DEFAULT_VIEWPORTS; + // Per-page namespacing: comparisons of secondary pages must not overwrite + // the index page's reports/screenshots. "index" keeps the legacy names. + const pageSlug = pageSlugFor(mockupPath); + const prefix = pageSlug === 'index' ? '' : `${pageSlug}-`; + const reportPath = resolveWorkspacePath( + workspaceRoot, + args.reportPath || (pageSlug === 'index' ? 'reports/comparison.json' : `reports/${pageSlug}.comparison.json`), + ); + const tasksPath = resolveWorkspacePath( + workspaceRoot, + args.tasksPath || (pageSlug === 'index' ? 'reports/repair-tasks.md' : `reports/${pageSlug}.repair-tasks.md`), + ); + const browser = await launchBrowser(chromium, { headless: true }, { tool: 'compare_html' }); + const results = []; + const server = shouldCompareEditor ? await serveDirectory(workspaceRoot) : null; + + try { + for (const viewport of viewports) { + const mockupShot = path.join(outDir, `${prefix}mockup-${viewport.name}.png`); + const renderedShot = path.join(outDir, `${prefix}rendered-${viewport.name}.png`); + const diffShot = path.join(outDir, `${prefix}diff-${viewport.name}.png`); + await capture(browser, mockupPath, mockupShot, viewport); + await capture(browser, renderedPath, renderedShot, viewport); + results.push(comparePngs({ + target: 'rendered', + mockupShot, + candidateShot: renderedShot, + diffShot, + viewport, + PNG, + pixelmatch, + })); + + if (shouldCompareEditor) { + const editorShot = path.join(outDir, `${prefix}editor-${viewport.name}.png`); + const editorDiffShot = path.join(outDir, `${prefix}diff-editor-${viewport.name}.png`); + await captureEditor(browser, server.urlFor(editorPath), editorShot, viewport); + results.push(comparePngs({ + target: 'editor', + mockupShot, + candidateShot: editorShot, + diffShot: editorDiffShot, + viewport, + PNG, + pixelmatch, + })); + } + } + } finally { + await browser.close(); + if (server) await server.close(); + } + + const thresholds = { + maxMismatchPercent: Number(args.maxMismatchPercent ?? 1), + maxHeightDelta: Number(args.maxHeightDelta ?? 8), + }; + const aggregate = aggregateComparisonResults(results); + const aggregates = { + all: aggregate, + rendered: aggregateComparisonResults(results.filter((result) => result.target === 'rendered')), + editor: aggregateComparisonResults(results.filter((result) => result.target === 'editor')), + }; + const tasks = comparisonTasks(results, thresholds); + const report = { + page: pageSlug, + mockupPath, + renderedPath, + editorPath: shouldCompareEditor ? editorPath : null, + thresholds, + aggregate, + aggregates, + results, + tasks, + }; + fs.mkdirSync(path.dirname(reportPath), { recursive: true }); + writeJson(reportPath, report); + writeFile(tasksPath, renderRepairTasks(tasks, report)); + + return { + page: pageSlug, + reportPath, + tasksPath, + aggregate, + passed: aggregate.maxMismatchPercent <= thresholds.maxMismatchPercent && aggregate.maxHeightDelta <= thresholds.maxHeightDelta, + tasks, + }; +} + +function pageSlugFor(mockupPath) { + return slug(path.basename(mockupPath, path.extname(mockupPath))) || 'index'; +} + +const DEFAULT_MEASURE_SELECTOR = 'main > section, main > div, main section, footer, .site-footer'; + +async function measureLayout(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + let chromium; + try { + chromium = (await import('playwright')).chromium; + } catch (error) { + throw new Error(`measure_layout needs optional packages. Run npm install in ${PLUGIN_ROOT}. Missing dependency: ${error.message}`); + } + + const mockupPath = resolveWorkspacePath(workspaceRoot, args.mockupPath || 'mockup/index.html'); + const candidatePath = resolveWorkspacePath(workspaceRoot, args.candidatePath || 'rendered/rendered-blocks.html'); + const candidateKind = args.candidateKind === 'editor' ? 'editor' : 'html'; + const selector = args.selector || DEFAULT_MEASURE_SELECTOR; + const viewports = Array.isArray(args.viewports) && args.viewports.length ? args.viewports : DEFAULT_VIEWPORTS; + for (const file of [mockupPath, candidatePath]) { + if (!fs.existsSync(file)) throw new Error(`measure_layout target does not exist: ${file}`); + } + + const browser = await launchBrowser(chromium, { headless: true }, { tool: 'measure_layout' }); + const server = candidateKind === 'editor' ? await serveDirectory(workspaceRoot) : null; + const measurements = []; + + try { + for (const viewport of viewports) { + const mockup = await measurePageGeometry(browser, { htmlPath: mockupPath, kind: 'html', viewport, selector }); + const candidate = await measurePageGeometry(browser, { + htmlPath: candidatePath, + kind: candidateKind, + url: server ? server.urlFor(candidatePath) : null, + viewport, + selector, + }); + + const count = Math.max(mockup.elements.length, candidate.elements.length); + const rows = []; + for (let index = 0; index < count; index += 1) { + const a = mockup.elements[index] || null; + const b = candidate.elements[index] || null; + rows.push({ + index, + key: (a || b).key, + mockup: a ? { top: a.top, height: a.height } : null, + candidate: b ? { top: b.top, height: b.height } : null, + deltaTop: a && b ? b.top - a.top : null, + deltaHeight: a && b ? b.height - a.height : null, + drifted: a && b ? Math.abs(b.top - a.top) > 2 || Math.abs(b.height - a.height) > 2 : true, + missingIn: a ? (b ? null : 'candidate') : 'mockup', + }); + } + + measurements.push({ + viewport: viewport.name, + width: viewport.width, + bodyHeight: { mockup: mockup.bodyHeight, candidate: candidate.bodyHeight, delta: candidate.bodyHeight - mockup.bodyHeight }, + driftedRows: rows.filter((row) => row.drifted).length, + rows, + }); + } + } finally { + await browser.close(); + if (server) await server.close(); + } + + return { + mockupPath, + candidatePath, + candidateKind, + selector, + measurements, + next: 'Rows with drifted=true localize the divergence. Re-run with a narrower selector (e.g. ".section-x > *") to drill into the drifted section, then fix the block tree or CSS rather than guessing from pixel diffs.', + }; +} + +async function measurePageGeometry(browser, { htmlPath, kind, url, viewport, selector }) { + const page = await browser.newPage({ + viewport: { width: Number(viewport.width), height: Number(viewport.height) }, + deviceScaleFactor: 1, + }); + try { + await page.emulateMedia({ reducedMotion: 'reduce' }); + if (kind === 'editor') { + await page.goto(url, { waitUntil: 'networkidle', timeout: 60000 }); + await page.waitForSelector('.block-editor-block-list__layout', { timeout: 60000 }); + await page.addStyleTag({ content: editorComparisonCss() }); + } else { + await page.goto(pathToFileURL(htmlPath).href, { waitUntil: 'networkidle' }); + await page.addStyleTag({ content: `${motionFreezeCss()}\n${transientOverlayCaptureCss()}` }); + } + await page.waitForTimeout(150); + return await page.evaluate((sel) => { + const seen = new Set(); + const elements = []; + for (const node of document.querySelectorAll(sel)) { + if (seen.has(node)) continue; + seen.add(node); + const rect = node.getBoundingClientRect(); + elements.push({ + key: `${node.tagName.toLowerCase()}${node.className && typeof node.className === 'string' ? ` ${node.className.split(/\s+/).filter(Boolean).slice(0, 4).join('.')}` : ''}`.trim(), + top: Math.round(rect.top + window.scrollY), + height: Math.round(rect.height), + }); + if (elements.length >= 400) break; + } + return { elements, bodyHeight: Math.round(document.body.scrollHeight) }; + }, selector); + } finally { + await page.close(); + } +} + +async function screenshotHtml(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + let chromium; + try { + chromium = (await import('playwright')).chromium; + } catch (error) { + throw new Error(`screenshot_html needs optional packages. Run npm install in ${PLUGIN_ROOT}. Missing dependency: ${error.message}`); + } + + const outDir = resolveWorkspacePath(workspaceRoot, args.outDir || 'visual'); + fs.mkdirSync(outDir, { recursive: true }); + const viewports = Array.isArray(args.viewports) && args.viewports.length ? args.viewports : DEFAULT_VIEWPORTS; + const targets = normalizeScreenshotTargets(workspaceRoot, args.targets); + const needsServer = targets.some((target) => target.kind === 'editor'); + const browser = await launchBrowser(chromium, { headless: true }, { tool: 'screenshot_html' }); + const server = needsServer ? await serveDirectory(workspaceRoot) : null; + const screenshots = []; + + try { + for (const target of targets) { + for (const viewport of viewports) { + const screenshotPath = path.join(outDir, `${safeFileSegment(target.name)}-${safeFileSegment(viewport.name)}.png`); + if (target.kind === 'editor') { + await captureEditor(browser, server.urlFor(target.path), screenshotPath, viewport); + } else { + await capture(browser, target.path, screenshotPath, viewport); + } + screenshots.push({ + target: target.name, + kind: target.kind, + sourcePath: target.path, + viewport: viewport.name, + size: `${viewport.width}x${viewport.height}`, + screenshotPath, + }); + } + } + } finally { + await browser.close(); + if (server) await server.close(); + } + + return { + outDir, + screenshots, + next: 'Inspect the screenshots directly, or use compare_html when you need measured diffs against the mockup.', + }; +} + +function normalizeScreenshotTargets(workspaceRoot, targets) { + const normalized = Array.isArray(targets) && targets.length + ? targets.map((target) => ({ + name: String(target.name || '').trim(), + path: resolveWorkspacePath(workspaceRoot, target.path || ''), + kind: target.kind === 'editor' ? 'editor' : 'html', + })) + : [ + { name: 'mockup', path: path.join(workspaceRoot, 'mockup/index.html'), kind: 'html' }, + { name: 'rendered', path: path.join(workspaceRoot, 'rendered/rendered-blocks.html'), kind: 'html' }, + { name: 'editor', path: path.join(workspaceRoot, 'editor/block-editor.html'), kind: 'editor' }, + ].filter((target) => fs.existsSync(target.path)); + + if (!normalized.length) { + throw new Error('No screenshot targets found. Pass targets or create mockup/rendered/editor files first.'); + } + + for (const target of normalized) { + if (!target.name) throw new Error('Every screenshot target needs a non-empty name.'); + if (!fs.existsSync(target.path)) throw new Error(`Screenshot target does not exist: ${target.path}`); + if (!isPathInside(workspaceRoot, target.path)) { + throw new Error(`Screenshot target must be inside workspaceRoot: ${target.path}`); + } + } + + return normalized; +} + +function safeFileSegment(value) { + const safe = String(value || '').toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, ''); + return safe || 'target'; +} + +function aggregateComparisonResults(results) { + if (!results.length) return { maxMismatchPercent: 0, maxHeightDelta: 0 }; + return { + maxMismatchPercent: Math.max(...results.map((result) => result.mismatchPercent)), + maxHeightDelta: Math.max(...results.map((result) => result.heightDelta)), + }; +} + +function comparisonTasks(results, thresholds) { + const tasks = []; + for (const result of results) { + const surface = result.target === 'editor' ? 'editor preview' : 'rendered frontend'; + const label = result.target === 'editor' ? 'Editor preview' : 'Rendered page'; + if (result.heightDelta > thresholds.maxHeightDelta) { + tasks.push({ + priority: 'high', + surface, + viewport: result.viewport, + issue: `${label} height differs by ${result.heightDelta}px.`, + target: result.target === 'editor' ? 'editable editor canvas / block edit output' : 'macro layout / section vertical scale', + fix: result.target === 'editor' + ? 'Inspect mockup/editor/diff screenshots and restore edit render structure, wrapper scale, missing block content, responsive behavior, or editor-only CSS drift.' + : 'Inspect screenshots and restore missing content, section height, component scale, responsive columns, or vertical rhythm before fine polish.', + verification: `Height delta <= ${thresholds.maxHeightDelta}px for ${result.viewport}.`, + images: { mockup: result.mockup, candidate: result.candidate, rendered: result.rendered, editor: result.editor, diff: result.diff }, + }); + } + if (result.mismatchPercent > thresholds.maxMismatchPercent) { + tasks.push({ + priority: result.mismatchPercent > thresholds.maxMismatchPercent * 3 ? 'high' : 'medium', + surface, + viewport: result.viewport, + issue: `${label} pixel mismatch is ${result.mismatchPercent}%.`, + target: result.target === 'editor' ? 'visible editor canvas differences in screenshot diff' : 'visible frontend differences in screenshot diff', + fix: result.target === 'editor' + ? 'Inspect mockup/editor/diff images. Write specific tasks for edit component output, missing editable text, wrapper classes, wrong grids, button layout, component scale, color, and typography.' + : 'Inspect mockup/rendered/diff images. Write specific tasks for missing elements, wrong grid geometry, button layout, component scale, color, and typography.', + verification: `Mismatch <= ${thresholds.maxMismatchPercent}% for ${result.viewport}.`, + images: { mockup: result.mockup, candidate: result.candidate, rendered: result.rendered, editor: result.editor, diff: result.diff }, + }); + } + } + return tasks; +} + +function renderRepairTasks(tasks, report) { + const lines = [ + '# Repair Tasks', + '', + `Mockup: ${report.mockupPath}`, + `Rendered: ${report.renderedPath}`, + ...(report.editorPath ? [`Editor: ${report.editorPath}`] : []), + `Max mismatch: ${report.aggregate.maxMismatchPercent}%`, + `Max height delta: ${report.aggregate.maxHeightDelta}px`, + ...(report.aggregates ? [ + `Rendered aggregate: ${report.aggregates.rendered.maxMismatchPercent}% mismatch, ${report.aggregates.rendered.maxHeightDelta}px height delta`, + `Editor aggregate: ${report.aggregates.editor.maxMismatchPercent}% mismatch, ${report.aggregates.editor.maxHeightDelta}px height delta`, + ] : []), + '', + ]; + if (!tasks.length) { + lines.push('No deterministic visual drift tasks. Inspect screenshots for residual polish.'); + } else { + for (const task of tasks) { + lines.push( + `- [ ] Priority: ${task.priority}`, + ` Surface: ${task.surface}`, + ` Viewport: ${task.viewport}`, + ` Issue: ${task.issue}`, + ` Target: ${task.target}`, + ` Fix: ${task.fix}`, + ` Verify: ${task.verification}`, + ` Images: ${task.images.mockup}, ${task.images.candidate}, ${task.images.diff}`, + '' + ); + } + } + return `${lines.join('\n').replace(/\n+$/g, '')}\n`; +} + +function extractInventory(html) { + const sections = []; + const sectionPattern = /<(header|section|footer|main|aside|article|nav)\b([^>]*)>([\s\S]*?)<\/\1>/gi; + let match; + let index = 0; + while ((match = sectionPattern.exec(html))) { + index += 1; + const tagName = match[1].toLowerCase(); + const attrs = parseAttrs(match[2]); + const inner = match[3]; + const id = attrs['data-section'] || attrs.id || attrs.class || `${tagName}-${index}`; + sections.push({ + id: slug(id) || `${tagName}-${index}`, + selector: attrs.id ? `#${attrs.id}` : attrs.class ? `.${attrs.class.split(/\s+/)[0]}` : tagName, + tagName, + className: attrs.class || '', + text: cleanText(inner), + headings: extractHeadings(inner), + paragraphs: extractParagraphs(inner), + links: extractLinks(inner), + forms: extractForms(inner), + cards: extractCards(inner), + html: match[0], + }); + } + return { + sections, + headings: extractHeadings(html), + paragraphs: extractParagraphs(html), + links: extractLinks(html), + forms: extractForms(html), + cards: extractCards(html), + }; +} + +function extractHeadings(html) { + return [...html.matchAll(/]*)>([\s\S]*?)<\/h\1>/gi)] + .map((match) => ({ level: Number(match[1]), className: parseAttrs(match[2]).class || '', content: cleanText(match[3]) })) + .filter((item) => item.content); +} + +function extractParagraphs(html) { + return [...html.matchAll(/]*)>([\s\S]*?)<\/p>/gi)] + .map((match) => ({ className: parseAttrs(match[1]).class || '', content: cleanText(match[2]) })) + .filter((item) => item.content); +} + +function extractLinks(html) { + return [...html.matchAll(/]*)>([\s\S]*?)<\/a>/gi)] + .map((match) => { + const attrs = parseAttrs(match[1]); + return { className: attrs.class || '', url: attrs.href || '', text: cleanText(match[2]) }; + }) + .filter((item) => item.text || item.url); +} + +function extractForms(html) { + return [...html.matchAll(/]*)>([\s\S]*?)<\/form>/gi)].map((match) => { + const attrs = parseAttrs(match[1]); + const inner = match[2]; + const fields = [...inner.matchAll(/]*>([\s\S]*?)<\/label>/gi)].map((labelMatch, index) => { + const labelHtml = labelMatch[1]; + const control = firstMatch(labelHtml, /<(input|select|textarea)\b([^>]*)>/i, 0); + const controlAttrs = control ? parseAttrs(control.replace(/^<\w+\s*|\s*\/?>$/g, '')) : {}; + return { + label: cleanText(labelHtml.replace(/<(input|select|textarea)\b[\s\S]*$/i, '')) || `Field ${index + 1}`, + type: control?.startsWith(']*>([\s\S]*?)<\/button>/i) || ''), + }; + }); +} + +function extractCards(html) { + return [...html.matchAll(/]*)>([\s\S]*?)<\/article>/gi)].map((match) => ({ + className: parseAttrs(match[1]).class || '', + title: extractHeadings(match[2])[0]?.content || '', + text: extractParagraphs(match[2])[0]?.content || '', + links: extractLinks(match[2]), + })); +} + +function parseAttrs(value) { + const attrs = {}; + for (const match of String(value || '').matchAll(/([:@A-Za-z0-9_-]+)(?:=(?:"([^"]*)"|'([^']*)'|([^\s"'>]+)))?/g)) { + attrs[match[1]] = match[2] ?? match[3] ?? match[4] ?? ''; + } + return attrs; +} + +function extractCustomProperties(css) { + const props = {}; + for (const match of css.matchAll(/(--[A-Za-z0-9_-]+)\s*:\s*([^;]+);/g)) props[match[1]] = match[2].trim(); + return props; +} + +function extractSelectors(css) { + return [...new Set([...css.matchAll(/([^{}@]+)\{/g)].map((match) => match[1].trim()).filter(Boolean))]; +} + +function normalizeAttributes(attributes, form) { + const normalized = attributes.map((attribute) => ({ + name: camelName(attribute.name), + type: ['string', 'number', 'boolean', 'array', 'object'].includes(attribute.type) ? attribute.type : 'string', + role: attribute.role || roleFromName(attribute.name), + default: attribute.default, + })); + if (form && !normalized.some((attribute) => attribute.name === 'fields')) { + normalized.push({ name: 'fields', type: 'array', role: 'form-fields', default: [] }); + } + return normalized; +} + +function blockJsonAttributes(attributes) { + const payload = {}; + for (const attribute of attributes) { + payload[attribute.name] = { type: attribute.type }; + if (attribute.default !== undefined) payload[attribute.name].default = attribute.default; + else if (attribute.type === 'array') payload[attribute.name].default = []; + else if (attribute.type === 'object') payload[attribute.name].default = {}; + else if (attribute.type === 'boolean') payload[attribute.name].default = false; + } + return payload; +} + +function defaultSupports() { + return { + anchor: true, + align: ['wide', 'full'], + className: true, + color: { text: true, background: true, gradients: true }, + spacing: { margin: true, padding: true, blockGap: true }, + typography: { fontSize: true, lineHeight: true }, + border: { color: true, radius: true, style: true, width: true }, + dimensions: { minHeight: true }, + html: false, + }; +} + +function generateIndexJs({ name, slug, attributes, form }) { + const richText = attributes + .filter((attribute) => isInlineEditable(attribute) && !(form && isButtonText(attribute))) + .map((attribute) => richTextEdit(attribute, slug)) + .join(',\n '); + const inspector = attributes.filter((attribute) => !isInlineEditable(attribute) && attribute.type !== 'array' && attribute.type !== 'object').map(inspectorControl).join(',\n '); + const formCanvas = form ? formEditCanvas(slug) : ''; + const saveContent = form ? formSaveCanvas(slug) : attributes.filter((attribute) => isInlineEditable(attribute) || attribute.type === 'array').map((attribute) => saveElement(attribute, slug)).join(',\n '); + return `(function (blocks, blockEditor, components, element) { + const el = element.createElement; + const Fragment = element.Fragment; + const registerBlockType = blocks.registerBlockType; + const useBlockProps = blockEditor.useBlockProps; + const RichText = blockEditor.RichText; + const InspectorControls = blockEditor.InspectorControls; + const PanelBody = components.PanelBody; + const TextControl = components.TextControl; + const ToggleControl = components.ToggleControl; + + registerBlockType(${JSON.stringify(name)}, { + apiVersion: 3, + + edit: function Edit(props) { + const attributes = props.attributes; + const setAttributes = props.setAttributes; + const blockProps = useBlockProps({ className: ${JSON.stringify(slug)} }); + const fields = attributes.fields && attributes.fields.length ? attributes.fields : [{ label: 'Email address', type: 'email', name: 'email', placeholder: '', required: false }]; + const updateField = function (index, key, value) { + const next = fields.slice(); + next[index] = Object.assign({}, next[index], { [key]: value }); + setAttributes({ fields: next }); + }; + + return el(Fragment, null, + ${inspector ? `el(InspectorControls, null, el(PanelBody, { title: 'Settings' }, ${inspector})),` : ''} + el('section', blockProps, + ${[richText, formCanvas].filter(Boolean).join(',\n ') || "el('div', null)"} + ) + ); + }, + + save: function Save(props) { + const attributes = props.attributes; + const blockProps = useBlockProps.save({ className: ${JSON.stringify(slug)} }); + return el('section', blockProps, + ${saveContent || "el('div', null)"} + ); + } + }); +})(window.wp.blocks, window.wp.blockEditor, window.wp.components, window.wp.element); +`; +} + +function richTextEdit(attribute, slugValue) { + const tag = tagFor(attribute); + return `el(RichText, { + tagName: ${JSON.stringify(tag)}, + className: ${JSON.stringify(`${slugValue}__${slug(attribute.name)}`)}, + value: attributes.${attribute.name} || '', + allowedFormats: ['core/bold', 'core/italic', 'core/link'], + placeholder: ${JSON.stringify(titleCase(attribute.name))}, + onChange: function (value) { setAttributes({ ${attribute.name}: value }); } + })`; +} + +function inspectorControl(attribute) { + if (attribute.type === 'boolean') { + return `el(ToggleControl, { label: ${JSON.stringify(titleCase(attribute.name))}, checked: !!attributes.${attribute.name}, onChange: function (value) { setAttributes({ ${attribute.name}: value }); } })`; + } + const type = attribute.type === 'number' ? ', type: "number"' : ''; + const value = attribute.type === 'number' ? `Number(value)` : 'value'; + return `el(TextControl, { label: ${JSON.stringify(titleCase(attribute.name))}${type}, value: attributes.${attribute.name} || '', onChange: function (value) { setAttributes({ ${attribute.name}: ${value} }); } })`; +} + +function formEditCanvas(slugValue) { + return `el('form', { className: ${JSON.stringify(`${slugValue}__form`)} }, + fields.map(function (field, index) { + return el('label', { key: field.name || index }, + el(RichText, { + tagName: 'span', + className: ${JSON.stringify(`${slugValue}__field-label`)}, + value: field.label || '', + placeholder: 'Field label', + allowedFormats: ['core/bold', 'core/italic'], + onChange: function (value) { updateField(index, 'label', value); } + }), + field.type === 'textarea' + ? el('textarea', { name: field.name || '', placeholder: field.placeholder || '', rows: field.rows || 5, required: !!field.required, disabled: true }) + : el('input', { type: field.type || 'text', name: field.name || '', placeholder: field.placeholder || '', required: !!field.required, disabled: true }) + ); + }), + el('button', { type: 'button', disabled: true }, + el(RichText, { + tagName: 'span', + value: attributes.buttonText || 'Submit', + placeholder: 'Button text', + allowedFormats: ['core/bold', 'core/italic'], + onChange: function (value) { setAttributes({ buttonText: value }); } + }) + ) + )`; +} + +function formSaveCanvas(slugValue) { + return `el('form', { className: ${JSON.stringify(`${slugValue}__form`)}, action: attributes.action || '#', method: attributes.method || 'post' }, + (attributes.fields || []).map(function (field, index) { + const name = field.name || String(field.label || 'field-' + index).toLowerCase().replace(/[^a-z0-9]+/g, '-'); + return el('label', { key: name }, + field.label || name, + field.type === 'textarea' + ? el('textarea', { name: name, placeholder: field.placeholder || '', rows: field.rows || 5, required: !!field.required }) + : el('input', { type: field.type || 'text', name: name, placeholder: field.placeholder || '', required: !!field.required }) + ); + }), + el('button', { type: 'submit' }, attributes.buttonText || 'Submit') + )`; +} + +function saveElement(attribute, slugValue) { + if (attribute.type === 'array') { + return `el('div', { className: ${JSON.stringify(`${slugValue}__${slug(attribute.name)}`)} }, (attributes.${attribute.name} || []).map(function (item, index) { return el('article', { key: index }, item.title ? el('h3', null, item.title) : null, item.text ? el('p', null, item.text) : null); }))`; + } + if (!isInlineEditable(attribute)) return ''; + return `attributes.${attribute.name} ? el(RichText.Content, { tagName: ${JSON.stringify(tagFor(attribute))}, className: ${JSON.stringify(`${slugValue}__${slug(attribute.name)}`)}, value: attributes.${attribute.name} }) : null`; +} + +function generateBlockCss({ name, slug: slugValue, form }) { + const className = `wp-block-${name.replace('/', '-')}`; + return `.${className} { + box-sizing: border-box; +} + +.${className} .${slugValue}__form { + display: grid; + gap: 1rem; +} + +.${className} .${slugValue}__form label { + display: grid; + gap: 0.5rem; +} + +.${className} .${slugValue}__form input, +.${className} .${slugValue}__form textarea, +.${className} .${slugValue}__form select, +.${className} .${slugValue}__form button { + font: inherit; +} +${form ? '' : `\n.${className} [class$="__items"] { display: grid; gap: 1rem; }\n`} +`; +} + +function isInlineEditable(attribute) { + const role = `${attribute.role || ''} ${attribute.name}`.toLowerCase(); + if (/url|href|action|method|required|placeholder|inputname|style|variant|speed|duration|fields/.test(role)) return false; + return attribute.type === 'string'; +} + +function isButtonText(attribute) { + return /button|cta|submit/.test(`${attribute.role || ''} ${attribute.name}`.toLowerCase()); +} + +function tagFor(attribute) { + const role = `${attribute.role || ''} ${attribute.name}`.toLowerCase(); + if (/heading|title|headline/.test(role)) return 'h2'; + if (/eyebrow|kicker|label/.test(role)) return 'p'; + if (/button|cta/.test(role)) return 'span'; + return 'p'; +} + +function roleFromName(name) { + const value = String(name).toLowerCase(); + if (/heading|title|headline/.test(value)) return 'heading'; + if (/body|text|intro|description|copy|lede/.test(value)) return 'body'; + if (/button|cta|submit/.test(value)) return 'button-text'; + if (/url|href|link/.test(value)) return 'url'; + if (/fields?/.test(value)) return 'form-fields'; + return 'content'; +} + +function looksFormLike(name, attributes) { + return /form|search|subscribe|booking|contact|inquiry|email/.test(`${name} ${attributes.map((attribute) => `${attribute.name} ${attribute.role}`).join(' ')}`.toLowerCase()); +} + +function starterHtml(prompt) { + return ` + + + + + HTML to Blocks Mockup + + + +
    + +
    + + +`; +} + +function starterCss() { + return `:root { + --paper: #f8f5ef; + --ink: #181512; +} + +* { box-sizing: border-box; } +body { margin: 0; background: var(--paper); color: var(--ink); font-family: system-ui, sans-serif; } +`; +} + +function fullHtml(title, css, body) { + return ` + + + + + ${escapeHtml(title)} + + + +${body} + + +`; +} + +function renderedPreviewHtml(title, outDir, cssSources, body) { + // CSS is linked (not inlined) so relative url() assets resolve from each + // stylesheet's own directory — identical to how the editor preview loads + // the same files. Inlining used to shift url() resolution to the rendered/ + // directory, splitting asset paths between the two surfaces. + const cssLinks = cssSources + .map((source) => ``) + .join('\n '); + return ` + + + + + ${escapeHtml(title)} + ${cssLinks} + + +${body} + + +`; +} + +function copyReference(name, target) { + const source = path.join(PLUGIN_ROOT, 'skills/html-to-blocks/references', name); + if (fs.existsSync(source)) writeFile(target, fs.readFileSync(source, 'utf8')); +} diff --git a/apps/cli/ai/html-to-blocks-engine/package-lock.json b/apps/cli/ai/html-to-blocks-engine/package-lock.json new file mode 100644 index 0000000000..65f406ccec --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/package-lock.json @@ -0,0 +1,11803 @@ +{ + "name": "html-to-wordpress-blocks", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "html-to-wordpress-blocks", + "version": "0.1.0", + "dependencies": { + "@wordpress/block-library": "^9.48.0", + "@wordpress/blocks": "^15.21.0", + "@wordpress/element": "^8.0.0", + "ajv": "^8.20.0", + "jsdom": "^27.0.1", + "pixelmatch": "^7.1.0", + "playwright": "^1.54.0", + "pngjs": "^7.0.0" + }, + "bin": { + "html-to-blocks-mcp": "tools/mcp-server.mjs" + }, + "devDependencies": { + "@wp-playground/cli": "^3.1.38" + } + }, + "node_modules/@ariakit/components": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@ariakit/components/-/components-0.1.2.tgz", + "integrity": "sha512-tvh2P0x1cJnoPXnmDEJwdRk3z7x6cTB8ArctcZdAUXlRg9tuwW/rJoBFJMzD5qMI9CDDlQ3Zctx58HvENw4BYw==", + "license": "MIT", + "dependencies": { + "@ariakit/store": "0.1.2", + "@ariakit/utils": "0.1.2" + } + }, + "node_modules/@ariakit/react": { + "version": "0.4.29", + "resolved": "https://registry.npmjs.org/@ariakit/react/-/react-0.4.29.tgz", + "integrity": "sha512-SLXlsddWHSwfUol4Yi0zULlalNWjzWjpS3zg7B7aaPd64saONQ5ktWf9KMxqBklcpjMLeF2dB9BAHAvpPVdCIQ==", + "license": "MIT", + "dependencies": { + "@ariakit/react-components": "0.1.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ariakit" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/react-components": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@ariakit/react-components/-/react-components-0.1.2.tgz", + "integrity": "sha512-SM+SPMAVlOZmGAfWNBza+0k9y4mkA5/dJhDoOyhE96cbNARy665uLdwowSJl1JGuFfcZzuzAwGon7f/rYeyfkQ==", + "license": "MIT", + "dependencies": { + "@ariakit/components": "0.1.2", + "@ariakit/react-store": "0.1.2", + "@ariakit/react-utils": "0.1.2", + "@ariakit/store": "0.1.2", + "@ariakit/utils": "0.1.2", + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/react-store": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@ariakit/react-store/-/react-store-0.1.2.tgz", + "integrity": "sha512-1r1Gn0tqhnOS0LFvHNGzn5/8C5aOANO5vb0Gxh94oR/be4zwCSE2zfQjOjRfpL+BBDhOcProME2+G6UslEJxbg==", + "license": "MIT", + "dependencies": { + "@ariakit/react-utils": "0.1.2", + "@ariakit/store": "0.1.2", + "@ariakit/utils": "0.1.2", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/react-utils": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@ariakit/react-utils/-/react-utils-0.1.2.tgz", + "integrity": "sha512-Rnl6D1542Mqu80xK++oUv1JXS0PtNmKXd9nkdud5nyvySiBDTrmPqRW44/D+5GbuZrboreQuY3tPYwKL7a7onQ==", + "license": "MIT", + "dependencies": { + "@ariakit/store": "0.1.2", + "@ariakit/utils": "0.1.2" + }, + "peerDependencies": { + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@ariakit/store": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@ariakit/store/-/store-0.1.2.tgz", + "integrity": "sha512-SS7bV4+a+1q9M9i0WV6DD4P/ypRKlCvII8soo2UMe1yuaxZA/Fc0htHe+EZwjJ6TMLjHfHh2TDSnXyrjC7QImA==", + "license": "MIT", + "dependencies": { + "@ariakit/utils": "0.1.2" + } + }, + "node_modules/@ariakit/utils": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@ariakit/utils/-/utils-0.1.2.tgz", + "integrity": "sha512-lBJhtBWpKjIck/9i7G8cahvaUgLsyGklI/Pjv+VtY9KTzyuzX5GpRbbLKMS/e1qLnFPS4C3CybYB70b1bVcAkw==", + "license": "MIT" + }, + "node_modules/@arraypress/waveform-player": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@arraypress/waveform-player/-/waveform-player-1.2.1.tgz", + "integrity": "sha512-PsgOZStUN+1GnY0tujbwk2PYE3HMT/Vl3wEvunqH16hIJWzisf8GEngy5EbswTAjQ1aV3Tk4XBkAskc8eslY1A==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/arraypress" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.2.tgz", + "integrity": "sha512-NfBUvBaYgKIuq6E/RBLY1m0IohzNHAYyaJGuTK79Z23uNwmz2jl1mPsC5ZxCCxylinKhT1Amn5oNTlx1wN8cQg==", + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.0.0", + "@csstools/css-color-parser": "^4.0.1", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.5" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.8.1.tgz", + "integrity": "sha512-MvRz1nCqW0fsy8Qz4dnLIvhOlMzqDVBabZx6lH+YywFDdjXhMY37SmpV1XFX3JzG5GWHn63j6HX6QPr3lZXHvQ==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.6" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT", + "peer": true + }, + "node_modules/@babel/core/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "peer": true, + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-async-generators": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", + "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-bigint": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", + "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-properties": { + "version": "7.12.13", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", + "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.12.13" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.29.7.tgz", + "integrity": "sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-meta": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", + "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-json-strings": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", + "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-logical-assignment-operators": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", + "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", + "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-numeric-separator": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", + "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.10.4" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-object-rest-spread": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", + "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-catch-binding": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", + "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-optional-chaining": { + "version": "7.8.3", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", + "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.8.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-top-level-await": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", + "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@base-ui/react": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@base-ui/react/-/react-1.5.0.tgz", + "integrity": "sha512-z1gSAlced1yY+iM+mHDEtIkD8UI3Ebs52MuBPxvV6f5hRutk+xvCH/wuB7hDqDzK9JG5FoMz5nhrqtSs1wjt1A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@base-ui/utils": "0.2.9", + "@floating-ui/react-dom": "^2.1.8", + "@floating-ui/utils": "^0.2.11", + "use-sync-external-store": "^1.6.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@date-fns/tz": "^1.2.0", + "@types/react": "^17 || ^18 || ^19", + "date-fns": "^4.0.0", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@date-fns/tz": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "date-fns": { + "optional": true + } + } + }, + "node_modules/@base-ui/react/node_modules/@floating-ui/react-dom": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.8.tgz", + "integrity": "sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.6" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@base-ui/utils": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@base-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-x/PDDCYzoqPpjrdyb3VcyylTI2IjUXEtYDGi5foh7KsnmNJIIaVwA2GLgDH1dps1GgXiJbA60hM+AyuTfQzIvw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@floating-ui/utils": "^0.2.11", + "reselect": "^5.1.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "@types/react": "^17 || ^18 || ^19", + "react": "^17 || ^18 || ^19", + "react-dom": "^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz", + "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz", + "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.2.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.5.tgz", + "integrity": "sha512-oNjBvzLq2GPZtJphCjLqXow/cHySHSgtxvKZb7OqSZ/xHgw6NWNhfad+6AB9cLeVm6eA9d/qMll3JdEHjy6M+A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@date-fns/tz": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@date-fns/tz/-/tz-1.5.0.tgz", + "integrity": "sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==", + "license": "MIT" + }, + "node_modules/@date-fns/utc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@date-fns/utc/-/utc-2.1.1.tgz", + "integrity": "sha512-SlJDfG6RPeEX8wEVv6ZB3kak4MmbtyiI2qX/5zuKdordbrhB/iaJ58GVMZgJ6P1sJaM1gMgENFYYeg1JWrCFrA==", + "license": "MIT" + }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/css": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/css/-/css-11.13.5.tgz", + "integrity": "sha512-wQdD0Xhkn3Qy2VNcIzbLP9MR8TafI0MJb7BEAXKp+w4+XqErksWR4OXomuDzPsN4InLdGhVe6EYcn2ZIUCpB8w==", + "license": "MIT", + "dependencies": { + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/native": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/@emotion/native/-/native-11.11.0.tgz", + "integrity": "sha512-t1b5bLv+o5OUNLqXlnw+LJYU10OpmYkLC/1W873Y1ohG+vObx5TT3o3Eh1okXb2KCuZTTBPgsEnU/Sl7NNkJ9Q==", + "license": "MIT", + "dependencies": { + "@emotion/primitives-core": "^11.11.0" + }, + "peerDependencies": { + "react-native": ">=0.14.0 <1" + } + }, + "node_modules/@emotion/primitives-core": { + "version": "11.13.2", + "resolved": "https://registry.npmjs.org/@emotion/primitives-core/-/primitives-core-11.13.2.tgz", + "integrity": "sha512-+MX60ROt1fDi5EYafhE/zs78XD4OuFUn6j0Z274wo5wVMT8sSBRx2CKPMbOUnmCcT0K5GPog+41mtkcppzkMmg==", + "license": "MIT", + "dependencies": { + "css-to-react-native": "^3.0.0" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", + "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.6.1" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT" + }, + "node_modules/@isaacs/ttlcache": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@isaacs/ttlcache/-/ttlcache-1.4.1.tgz", + "integrity": "sha512-RQgQ4uQ+pLbqXfOmieB91ejmLwvSgv9nLx6sT6sD83s7umBypgg+OIBOBbEUiJXrfpnp9j0mRhYYdzp9uqq3lA==", + "license": "ISC", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/load-nyc-config": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", + "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", + "license": "ISC", + "peer": true, + "dependencies": { + "camelcase": "^5.3.1", + "find-up": "^4.1.0", + "get-package-type": "^0.1.0", + "js-yaml": "^3.13.1", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "license": "MIT", + "peer": true, + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "license": "MIT", + "peer": true, + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/load-nyc-config/node_modules/resolve-from": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", + "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.6.tgz", + "integrity": "sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@jest/create-cache-key-function": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/create-cache-key-function/-/create-cache-key-function-29.7.0.tgz", + "integrity": "sha512-4QqS3LY5PBmTRHj9sAg1HLoPzqAI0uOX6wI/TRqHIcOxlFidy6YEmCQJk6FSZjNLGCeubDMfmkWL+qaLKhSGQA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/environment": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", + "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/fake-timers": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", + "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@sinonjs/fake-timers": "^10.0.2", + "@types/node": "*", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/schemas": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", + "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@sinclair/typebox": "^0.27.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", + "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.11.6", + "@jest/types": "^29.6.3", + "@jridgewell/trace-mapping": "^0.3.18", + "babel-plugin-istanbul": "^6.1.1", + "chalk": "^4.0.0", + "convert-source-map": "^2.0.0", + "fast-json-stable-stringify": "^2.1.0", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "micromatch": "^4.0.4", + "pirates": "^4.0.4", + "slash": "^3.0.0", + "write-file-atomic": "^4.0.2" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jest/transform/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT", + "peer": true + }, + "node_modules/@jest/types": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", + "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "@types/istanbul-lib-coverage": "^2.0.0", + "@types/istanbul-reports": "^3.0.0", + "@types/node": "*", + "@types/yargs": "^17.0.8", + "chalk": "^4.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodable/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@nodable/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-9uGyhaQavEUMC8AIddIjau4NsnsXhou+j5sBAGojCM1oxmQpVKTWR/9JxABD6UAv12vpIms55fPZKFQEhG6uBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/nodable" + } + ], + "license": "MIT" + }, + "node_modules/@octokit/app": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/@octokit/app/-/app-14.1.0.tgz", + "integrity": "sha512-g3uEsGOQCBl1+W1rgfwoRFUIR6PtvB2T1E4RpygeUU5LrLvlOqcxrt5lfykIeRpUPpupreGJUYl70fqMDXdTpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-app": "^6.0.0", + "@octokit/auth-unauthenticated": "^5.0.0", + "@octokit/core": "^5.0.0", + "@octokit/oauth-app": "^6.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/types": "^12.0.0", + "@octokit/webhooks": "^12.0.4" + }, + "engines": { + "node": ">= 18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-app": "^7.1.0", + "@octokit/auth-oauth-user": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.1.0", + "deprecation": "^2.3.1", + "lru-cache": "npm:@wolfy1339/lru-cache@^11.0.2-patch.1", + "universal-github-app-jwt": "^1.1.2", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-app/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-app/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-device": "^6.1.0", + "@octokit/auth-oauth-user": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "@types/btoa-lite": "^1.0.0", + "btoa-lite": "^1.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-app/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-app/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/oauth-methods": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-device/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-device/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-device": "^6.1.0", + "@octokit/oauth-methods": "^4.1.0", + "@octokit/request": "^8.3.1", + "@octokit/types": "^13.0.0", + "btoa-lite": "^1.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/auth-oauth-user/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/auth-oauth-user/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.1.0", + "@octokit/request": "^8.4.1", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/core/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/endpoint/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request": "^8.4.1", + "@octokit/types": "^13.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/graphql/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/auth-oauth-app": "^7.0.0", + "@octokit/auth-oauth-user": "^4.0.0", + "@octokit/auth-unauthenticated": "^5.0.0", + "@octokit/core": "^5.0.0", + "@octokit/oauth-authorization-url": "^6.0.2", + "@octokit/oauth-methods": "^4.0.0", + "@types/aws-lambda": "^8.10.83", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/oauth-authorization-url": "^6.0.2", + "@octokit/request": "^8.3.1", + "@octokit/request-error": "^5.1.0", + "@octokit/types": "^13.0.0", + "btoa-lite": "^1.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/oauth-methods/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/oauth-methods/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.4.1.tgz", + "integrity": "sha512-xV1b+ceKV9KytQe3zCVqjg+8GTGfDYwaT1ATU5isiUyVtlVAO3HNdzpS4sr4GBx4hxQ46s7ITtZrAsxG22+rVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.6.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^13.0.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "5" + } + }, + "node_modules/@octokit/plugin-retry/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/plugin-retry/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^12.2.0", + "bottleneck": "^2.15.3" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": "^5.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/endpoint": "^9.0.6", + "@octokit/request-error": "^5.1.1", + "@octokit/types": "^13.1.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/types": "^13.1.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request-error/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "node_modules/@octokit/request/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@octokit/request/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^24.2.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/openapi-types": "^20.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/request-error": "^5.0.0", + "@octokit/webhooks-methods": "^4.1.0", + "@octokit/webhooks-types": "7.6.1", + "aggregate-error": "^3.1.0" + }, + "engines": { + "node": ">= 18" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@php-wasm/cli-util": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/cli-util/-/cli-util-3.1.38.tgz", + "integrity": "sha512-dbGKLVejsW4IsrqcZfRlrCawt/Xaw6/heGDElzvNIFOo9dKq3O+TEGTWe4uTr9E0qCRWA6cReAh0IKnuFApRlA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/util": "3.1.38", + "fast-xml-parser": "^5.8.0", + "jsonc-parser": "3.3.1" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/logger": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/logger/-/logger-3.1.38.tgz", + "integrity": "sha512-DSLl/gvYtJl+gDubY95PPdks/4MvDd8wJlXE3PPkCIbc7dm+g3V5wXHTSl4cZzfSNdytBmRwmZkdCpe5Fu3hKw==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/node": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node/-/node-3.1.38.tgz", + "integrity": "sha512-dRl6LHPN42/TSFX25tq4WgKRq55YnIUjfDOi9TlywVzMw4dMY2/aAIDJX/dwN3Sgn9pc9ekgJqXivzDlnLDVWQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/cli-util": "3.1.38", + "@php-wasm/logger": "3.1.38", + "@php-wasm/node-5-2": "3.1.38", + "@php-wasm/node-7-4": "3.1.38", + "@php-wasm/node-8-0": "3.1.38", + "@php-wasm/node-8-1": "3.1.38", + "@php-wasm/node-8-2": "3.1.38", + "@php-wasm/node-8-3": "3.1.38", + "@php-wasm/node-8-4": "3.1.38", + "@php-wasm/node-8-5": "3.1.38", + "@php-wasm/universal": "3.1.38", + "@php-wasm/util": "3.1.38", + "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" + } + }, + "node_modules/@php-wasm/node-5-2": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node-5-2/-/node-5-2-3.1.38.tgz", + "integrity": "sha512-s4Ly1znifuhI+esoUdsjWQrf0TcC2Jd7HQkmN7sDSn0xCd36uxRAdZ0w04t4NqWHjQUFvWT1XpBDbEQh779fOw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/node-7-4": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node-7-4/-/node-7-4-3.1.38.tgz", + "integrity": "sha512-/7LibshPqhZWXgnhKlbqg0xTyuTvZV62mGhr4imstomJepdoL5DcpyX/+z6UKkCB0YQDMAak8JK/2wwBwFoEdw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/node-8-0": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-0/-/node-8-0-3.1.38.tgz", + "integrity": "sha512-Lzyqk1DWf/fjarX9IidJ6oHKI00htKgcnuPFwwMYUvxdzWTgWa2WhJuwprWXtQH4sAOjq0Kd7Q1wXFhN2YTFpA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/node-8-1": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-1/-/node-8-1-3.1.38.tgz", + "integrity": "sha512-9b75i2uJwD0ApFJ7+XwV1+fHNMoxZJNFyNTNhleezQJX2XsIpRskPRiW/HuUEYfQmfrsfVUZlcUKdtjmWoG+Ww==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/node-8-2": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-2/-/node-8-2-3.1.38.tgz", + "integrity": "sha512-93VX1v+C48V/UZQ+avGncKNON3dxZJGFMj2vlMkQHF4982tbaQM+UcpkifMeSdt5BlArsT5AokR5ll+wUlK+0Q==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/node-8-3": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-3/-/node-8-3-3.1.38.tgz", + "integrity": "sha512-klWqS+ZTjNibBo6INWC8A4PaHkGgZg0ubaZKGf5yMsVSjdYP1i9dJGSGy+hEhXKPHTHhtM0qKwTkbjjrvCQjBQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/node-8-4": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-4/-/node-8-4-3.1.38.tgz", + "integrity": "sha512-mg3fm0HMKY7VTEl8mFITqGkSlI4Xp8/XQ/9P9Wc/Qn1ktkjA2r110vAZl8bXT7mry2qNUXXixAqPj9zQKrFf7g==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/node-8-5": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/node-8-5/-/node-8-5-3.1.38.tgz", + "integrity": "sha512-kDA3Lm7rvx/HuqO32BlmV2XjtMCKgDiPrB42Kp8ZhOrNhb0gzhoMBEvDT4dppUq0GiLp39fP818o3fbKIiJZWw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "wasm-feature-detect": "1.8.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/progress": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/progress/-/progress-3.1.38.tgz", + "integrity": "sha512-cmmbe7ZgIN0kG2Tbp7Nh0gYKVpPygFJMu8B+fOjdS/lfm5zwJqkkIAn6fdcjYL2DO9s3+IGWjhdhqGtlzY3L+g==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "3.1.38" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/scopes": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/scopes/-/scopes-3.1.38.tgz", + "integrity": "sha512-rbrsC5X3WVkD7K9E86wnRr4qhUU8AzycJxafJdSsh/LPjeRvxQpu8xhIMxmtFgfuMBzpWRQbsFQmKfSCgHMcAA==", + "dev": true, + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/stream-compression": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/stream-compression/-/stream-compression-3.1.38.tgz", + "integrity": "sha512-8YUS+1buRqhkKBfqpPcZYI59wOYaOHiMzryZMpkdJVA4ELFkWR9NfLxJopGJbGGB/qGg7V5GFKFV6EP5RxbdPw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/util": "3.1.38" + } + }, + "node_modules/@php-wasm/universal": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/universal/-/universal-3.1.38.tgz", + "integrity": "sha512-H2op/4rZb5XYit54Z9jHULQwQIDp11Cv2ubp8Iv0pNkPBb5acW9g5/R+slB6tP6GW2Z0PyVxoVjzc3fab2v0Yw==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "3.1.38", + "@php-wasm/progress": "3.1.38", + "@php-wasm/stream-compression": "3.1.38", + "@php-wasm/util": "3.1.38", + "ini": "4.1.2" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/util": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/util/-/util-3.1.38.tgz", + "integrity": "sha512-afqgA8gBzISH6FGifXWQPcZ7WDLXxvEaSTFnc7uWPtDgsmhl0fGE0vIRMmrfIgR/7EvrdR/9d4wJXs8LcpX5uA==", + "dev": true, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/web-service-worker": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/web-service-worker/-/web-service-worker-3.1.38.tgz", + "integrity": "sha512-7ghdF2YfrRnbKUpl8/KYkwgp4qkNBz6hubfXjMiHTmfEoPG/ZPEMxAavdJxJSAOfW1GXpdzUTVviu2B+g/UTLg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/scopes": "3.1.38", + "@php-wasm/universal": "3.1.38" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/xdebug-bridge": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@php-wasm/xdebug-bridge/-/xdebug-bridge-3.1.38.tgz", + "integrity": "sha512-yJBZG8TmzYEMs5BPnadJz0cJ35biwPcvxuwcz+kcc5Rp6DioRZ64MFWKT2hsQgMGvLQskBfKCyC/8PNDRuqU6g==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "3.1.38", + "@php-wasm/universal": "3.1.38", + "ws": "8.21.0", + "xml2js": "0.6.2", + "yargs": "17.7.2" + }, + "bin": { + "xdebug-bridge": "xdebug-bridge.js" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@php-wasm/xdebug-bridge/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "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/@php-wasm/xdebug-bridge/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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@preact/signals": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@preact/signals/-/signals-1.3.4.tgz", + "integrity": "sha512-TPMkStdT0QpSc8FpB63aOwXoSiZyIrPsP9Uj347KopdS6olZdAYeeird/5FZv/M1Yc1ge5qstub2o8VDbvkT4g==", + "license": "MIT", + "dependencies": { + "@preact/signals-core": "^1.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + }, + "peerDependencies": { + "preact": "10.x" + } + }, + "node_modules/@preact/signals-core": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/@preact/signals-core/-/signals-core-1.14.2.tgz", + "integrity": "sha512-RZHdBj9ZF4n40Rp4jS052EHHjBWf96P9oNdXPfhQTovCuWY9iQn3Gq+gOTJSgBO9A/JBuPfMOWsSX/lIU9Pc/A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.4.tgz", + "integrity": "sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.3.tgz", + "integrity": "sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.4.tgz", + "integrity": "sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.16.tgz", + "integrity": "sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-context": "1.1.4", + "@radix-ui/react-dismissable-layer": "1.1.12", + "@radix-ui/react-focus-guards": "1.1.4", + "@radix-ui/react-focus-scope": "1.1.9", + "@radix-ui/react-id": "1.1.2", + "@radix-ui/react-portal": "1.1.11", + "@radix-ui/react-presence": "1.1.6", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-slot": "1.2.5", + "@radix-ui/react-use-controllable-state": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.12.tgz", + "integrity": "sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.4", + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2", + "@radix-ui/react-use-escape-keydown": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.4.tgz", + "integrity": "sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.9.tgz", + "integrity": "sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.3", + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.2.tgz", + "integrity": "sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.11.tgz", + "integrity": "sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.5", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.6.tgz", + "integrity": "sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.5.tgz", + "integrity": "sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.5.tgz", + "integrity": "sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.2.tgz", + "integrity": "sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.3.tgz", + "integrity": "sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.3", + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.3.tgz", + "integrity": "sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.2.tgz", + "integrity": "sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.2.tgz", + "integrity": "sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@react-native/assets-registry": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/assets-registry/-/assets-registry-0.84.1.tgz", + "integrity": "sha512-lAJ6PDZv95FdT9s9uhc9ivhikW1Zwh4j9XdXM7J2l4oUA3t37qfoBmTSDLuPyE3Bi+Xtwa11hJm0BUTT2sc/gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/codegen": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/codegen/-/codegen-0.84.1.tgz", + "integrity": "sha512-n1RIU0QAavgCg1uC5+s53arL7/mpM+16IBhJ3nCFSd/iK5tUmCwxQDcIDC703fuXfpub/ZygeSjVN8bcOWn0gA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/parser": "^7.25.3", + "hermes-parser": "0.32.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "tinyglobby": "^0.2.15", + "yargs": "^17.6.2" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@babel/core": "*" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true + }, + "node_modules/@react-native/codegen/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", + "peer": true + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/@react-native/codegen/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", + "peer": true, + "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/@react-native/codegen/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", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/@react-native/community-cli-plugin": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/community-cli-plugin/-/community-cli-plugin-0.84.1.tgz", + "integrity": "sha512-f6a+mJEJ6Joxlt/050TqYUr7uRRbeKnz8lnpL7JajhpsgZLEbkJRjH8HY5QiLcRdUwWFtizml4V+vcO3P4RxoQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@react-native/dev-middleware": "0.84.1", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "metro": "^0.83.3", + "metro-config": "^0.83.3", + "metro-core": "^0.83.3", + "semver": "^7.1.3" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@react-native-community/cli": "*", + "@react-native/metro-config": "*" + }, + "peerDependenciesMeta": { + "@react-native-community/cli": { + "optional": true + }, + "@react-native/metro-config": { + "optional": true + } + } + }, + "node_modules/@react-native/debugger-frontend": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/debugger-frontend/-/debugger-frontend-0.84.1.tgz", + "integrity": "sha512-rUU/Pyh3R5zT0WkVgB+yA6VwOp7HM5Hz4NYE97ajFS07OUIcv8JzBL3MXVdSSjLfldfqOuPEuKUaZcAOwPgabw==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/debugger-shell": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/debugger-shell/-/debugger-shell-0.84.1.tgz", + "integrity": "sha512-LIGhh4q4ette3yW5OzmukNMYwmINYrRGDZqKyTYc/VZyNpblZPw72coXVHXdfpPT6+YlxHqXzn3UjFZpNODGCQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "cross-spawn": "^7.0.6", + "debug": "^4.4.0", + "fb-dotslash": "0.5.8" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/dev-middleware/-/dev-middleware-0.84.1.tgz", + "integrity": "sha512-Z83ra+Gk6ElAhH3XRrv3vwbwCPTb04sPPlNpotxcFZb5LtRQZwT91ZQEXw3GOJCVIFp9EQ/gj8AQbVvtHKOUlQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@isaacs/ttlcache": "^1.4.1", + "@react-native/debugger-frontend": "0.84.1", + "@react-native/debugger-shell": "0.84.1", + "chrome-launcher": "^0.15.2", + "chromium-edge-launcher": "^0.2.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "invariant": "^2.2.4", + "nullthrows": "^1.1.1", + "open": "^7.0.3", + "serve-static": "^1.16.2", + "ws": "^7.5.10" + }, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/dev-middleware/node_modules/ws": { + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", + "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@react-native/gradle-plugin": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/gradle-plugin/-/gradle-plugin-0.84.1.tgz", + "integrity": "sha512-7uVlPBE3uluRNRX4MW7PUJIO1LDBTpAqStKHU7LHH+GRrdZbHsWtOEAX8PiY4GFfBEvG8hEjiuTOqAxMjV+hDg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/js-polyfills": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/js-polyfills/-/js-polyfills-0.84.1.tgz", + "integrity": "sha512-UsTe2AbUugsfyI7XIHMQq4E7xeC8a6GrYwuK+NohMMMJMxmyM3JkzIk+GB9e2il6ScEQNMJNaj+q+i5za8itxQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 20.19.4" + } + }, + "node_modules/@react-native/normalize-colors": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/normalize-colors/-/normalize-colors-0.84.1.tgz", + "integrity": "sha512-/UPaQ4jl95soXnLDEJ6Cs6lnRXhwbxtT4KbZz+AFDees7prMV2NOLcHfCnzmTabf5Y3oxENMVBL666n4GMLcTA==", + "license": "MIT", + "peer": true + }, + "node_modules/@react-spring/animated": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/animated/-/animated-9.7.5.tgz", + "integrity": "sha512-Tqrwz7pIlsSDITzxoLS3n/v/YCUHQdOIKtOJf4yL6kYVSDTSmVK1LI1Q3M/uu2Sx4X3pIWF3xLUhlsA6SPNTNg==", + "license": "MIT", + "dependencies": { + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/core": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/core/-/core-9.7.5.tgz", + "integrity": "sha512-rmEqcxRcu7dWh7MnCcMXLvrf6/SDlSokLaLTxiPlAYi11nN3B5oiCUAblO72o+9z/87j2uzxa2Inm8UbLjXA+w==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-spring/donate" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/rafz": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/rafz/-/rafz-9.7.5.tgz", + "integrity": "sha512-5ZenDQMC48wjUzPAm1EtwQ5Ot3bLIAwwqP2w2owG5KoNdNHpEJV263nGhCeKKmuA3vG2zLLOdu3or6kuDjA6Aw==", + "license": "MIT" + }, + "node_modules/@react-spring/shared": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/shared/-/shared-9.7.5.tgz", + "integrity": "sha512-wdtoJrhUeeyD/PP/zo+np2s1Z820Ohr/BbuVYv+3dVLW7WctoiN7std8rISoYoHpUXtbkpesSKuPIw/6U1w1Pw==", + "license": "MIT", + "dependencies": { + "@react-spring/rafz": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@react-spring/types": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/types/-/types-9.7.5.tgz", + "integrity": "sha512-HVj7LrZ4ReHWBimBvu2SKND3cDVUPWKLqRTmWe/fNY6o1owGOX0cAHbdPDTMelgBlVbrTKrre6lFkhqGZErK/g==", + "license": "MIT" + }, + "node_modules/@react-spring/web": { + "version": "9.7.5", + "resolved": "https://registry.npmjs.org/@react-spring/web/-/web-9.7.5.tgz", + "integrity": "sha512-lmvqGwpe+CSttsWNZVr+Dg62adtKhauGwLyGE/RRyZ8AAMLgb9x3NDMA5RMElXo+IMyTkPp7nxTB8ZQlmhb6JQ==", + "license": "MIT", + "dependencies": { + "@react-spring/animated": "~9.7.5", + "@react-spring/core": "~9.7.5", + "@react-spring/shared": "~9.7.5", + "@react-spring/types": "~9.7.5" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@sinclair/typebox": { + "version": "0.27.10", + "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.10.tgz", + "integrity": "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==", + "license": "MIT", + "peer": true + }, + "node_modules/@sinonjs/commons": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "type-detect": "4.0.8" + } + }, + "node_modules/@sinonjs/fake-timers": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", + "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@sinonjs/commons": "^3.0.0" + } + }, + "node_modules/@tabby_ai/hijri-converter": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@tabby_ai/hijri-converter/-/hijri-converter-1.0.5.tgz", + "integrity": "sha512-r5bClKrcIusDoo049dSL8CawnHR6mRdDwhlQuIgZRNty68q0x8k3Lf1BtPAMxRf/GgnHBnIO4ujd3+GQdLWzxQ==", + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/@tannin/compile": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/compile/-/compile-1.1.0.tgz", + "integrity": "sha512-n8m9eNDfoNZoxdvWiTfW/hSPhehzLJ3zW7f8E7oT6mCROoMNWCB4TYtv041+2FMAxweiE0j7i1jubQU4MEC/Gg==", + "license": "MIT", + "dependencies": { + "@tannin/evaluate": "^1.2.0", + "@tannin/postfix": "^1.1.0" + } + }, + "node_modules/@tannin/evaluate": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@tannin/evaluate/-/evaluate-1.2.0.tgz", + "integrity": "sha512-3ioXvNowbO/wSrxsDG5DKIMxC81P0QrQTYai8zFNY+umuoHWRPbQ/TuuDEOju9E+jQDXmj6yI5GyejNuh8I+eg==", + "license": "MIT" + }, + "node_modules/@tannin/plural-forms": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/plural-forms/-/plural-forms-1.1.0.tgz", + "integrity": "sha512-xl9R2mDZO/qiHam1AgMnAES6IKIg7OBhcXqy6eDsRCdXuxAFPcjrej9HMjyCLE0DJ/8cHf0i5OQTstuBRhpbHw==", + "license": "MIT", + "dependencies": { + "@tannin/compile": "^1.1.0" + } + }, + "node_modules/@tannin/postfix": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@tannin/postfix/-/postfix-1.1.0.tgz", + "integrity": "sha512-oocsqY7g0cR+Gur5jRQLSrX2OtpMLMse1I10JQBm8CdGMrDkh1Mg2gjsiquMHRtBs4Qwu5wgEp5GgIYHk4SNPw==", + "license": "MIT" + }, + "node_modules/@tannin/sprintf": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@tannin/sprintf/-/sprintf-1.3.3.tgz", + "integrity": "sha512-RwARl+hFwhzy0tg9atWcchLFvoQiOh4rrP7uG2N5E4W80BPCUX0ElcUR9St43fxB9EfjsW2df9Qp+UsTbvQDjA==", + "license": "MIT" + }, + "node_modules/@types/aws-lambda": { + "version": "8.10.162", + "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.162.tgz", + "integrity": "sha512-Fn658grtLOci1oxi1391vvDWJRKNGWRSqfxRkmN/Iy3c0tQH1USMKEXcPYHLvope+ZgTFocx9FRQJx1muBL6qw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/graceful-fs": { + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/gradient-parser": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@types/gradient-parser/-/gradient-parser-1.1.0.tgz", + "integrity": "sha512-SaEcbgQscHtGJ1QL+ajgDTmmqU2f6T+00jZRcFlVHUW2Asivc84LNUev/UQFyu117AsdyrtI+qpwLvgjJXJxmw==", + "license": "MIT" + }, + "node_modules/@types/highlight-words-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/highlight-words-core/-/highlight-words-core-1.2.1.tgz", + "integrity": "sha512-9VZUA5omXBfn+hDxFjUDu1FOJTBM3LmvqfDey+Z6Aa8B8/JmF5SMj6FBrjfgJ/Q3YXOZd3qyTDfJyMZSs/wCUA==", + "license": "MIT" + }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/istanbul-lib-report": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/istanbul-lib-coverage": "*" + } + }, + "node_modules/@types/istanbul-reports": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/istanbul-lib-report": "*" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*", + "@types/node": "*" + } + }, + "node_modules/@types/mousetrap": { + "version": "1.6.15", + "resolved": "https://registry.npmjs.org/@types/mousetrap/-/mousetrap-1.6.15.tgz", + "integrity": "sha512-qL0hyIMNPow317QWW/63RvL1x5MVMV+Ru3NaY9f/CuEpCqrmb7WeuK2071ZY5hczOnm38qExWM2i2WtkXLSqFw==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.9.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz", + "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==", + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.31", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.31.tgz", + "integrity": "sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==", + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/stack-utils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "license": "MIT", + "peer": true + }, + "node_modules/@types/yargs": { + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/yargs-parser": "*" + } + }, + "node_modules/@types/yargs-parser": { + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "license": "MIT", + "peer": true + }, + "node_modules/@use-gesture/core": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.3.1.tgz", + "integrity": "sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==", + "license": "MIT" + }, + "node_modules/@use-gesture/react": { + "version": "10.3.1", + "resolved": "https://registry.npmjs.org/@use-gesture/react/-/react-10.3.1.tgz", + "integrity": "sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==", + "license": "MIT", + "dependencies": { + "@use-gesture/core": "10.3.1" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } + }, + "node_modules/@wordpress/a11y": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/a11y/-/a11y-4.48.0.tgz", + "integrity": "sha512-MXwBc2sYaemZCn1dqVutTbLdM6iy4bx/HS9hHR/+pRpaSVJUlguZ1aQ0BaoIbE4u0uOezGGc5d2bDfWCti3Dww==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/dom-ready": "^4.48.0", + "@wordpress/i18n": "^6.21.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/api-fetch": { + "version": "7.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/api-fetch/-/api-fetch-7.48.0.tgz", + "integrity": "sha512-WYoIikKQPdRqrbLB9b9diM80q4g80NqqMPwVYZY9c7vbhJvj5c0hkA5zAlwba/iRbwqDjpRiZMKp8XntYLzMWw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/i18n": "^6.21.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/url": "^4.48.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/autop": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/autop/-/autop-4.48.0.tgz", + "integrity": "sha512-Fn0WzWJjwIFxSfF9RqB3L1XbKudWLGHc4DMTAN4KAfyVl+86FszP85UYJq28EWIMblPO4V1roNXt6ZbShmGsOw==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/base-styles": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@wordpress/base-styles/-/base-styles-9.1.0.tgz", + "integrity": "sha512-QONqtlA7IRYb6cbCjwTEiXJwfkWPpHl6PSS+F1TDeDP0L7m+hXfpRbH1qfKjSffWlyDaBqLFWwXZ3evpeFw5bg==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/blob": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/blob/-/blob-4.48.0.tgz", + "integrity": "sha512-nMFyrFqdAMLeM2QzVN14UuWtBcbiftrcCmPZydZq05wcWs0pUIcd0Xe3D7o77GIfhlpRFOOC4iT7ORJE/YeWiA==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/block-editor": { + "version": "15.21.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-editor/-/block-editor-15.21.0.tgz", + "integrity": "sha512-tSv8htNGX4LZfC91BCgcxpe2uU9wZAMGDFmpLYu6qSLg8ArhXJtbPt9YUigSPQJrM8XjD/29nW2PG779incBQA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@react-spring/web": "^9.4.5", + "@wordpress/a11y": "^4.48.0", + "@wordpress/base-styles": "^9.1.0", + "@wordpress/blob": "^4.48.0", + "@wordpress/block-serialization-default-parser": "^5.48.0", + "@wordpress/blocks": "^15.21.0", + "@wordpress/commands": "^1.48.0", + "@wordpress/components": "^35.0.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/data": "^10.48.0", + "@wordpress/dataviews": "^16.0.0", + "@wordpress/date": "^5.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/dom": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/escape-html": "^3.48.0", + "@wordpress/global-styles-engine": "^1.15.0", + "@wordpress/hooks": "^4.48.0", + "@wordpress/html-entities": "^4.48.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/image-cropper": "^1.12.0", + "@wordpress/interactivity": "^6.48.0", + "@wordpress/is-shallow-equal": "^5.48.0", + "@wordpress/keyboard-shortcuts": "^5.48.0", + "@wordpress/keycodes": "^4.48.0", + "@wordpress/notices": "^5.48.0", + "@wordpress/preferences": "^4.48.0", + "@wordpress/priority-queue": "^3.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/rich-text": "^7.48.0", + "@wordpress/style-engine": "^2.48.0", + "@wordpress/token-list": "^3.48.0", + "@wordpress/ui": "^0.15.0", + "@wordpress/upload-media": "^0.33.0", + "@wordpress/url": "^4.48.0", + "@wordpress/warning": "^3.48.0", + "@wordpress/wordcount": "^4.48.0", + "change-case": "^4.1.2", + "clsx": "^2.1.1", + "colord": "^2.7.0", + "deepmerge": "^4.3.0", + "diff": "^4.0.2", + "fast-deep-equal": "^3.1.3", + "memize": "^2.1.0", + "parsel-js": "^1.1.2", + "postcss": "^8.4.21", + "postcss-prefix-selector": "^1.16.0", + "postcss-urlrebase": "^1.4.0", + "react-autosize-textarea": "^7.1.0", + "react-easy-crop": "^5.0.6", + "remove-accents": "^0.5.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/block-library": { + "version": "9.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-library/-/block-library-9.48.0.tgz", + "integrity": "sha512-bcnC8yAFxqeLdN/gpS4M1zyJIA9yTO7hrDw3KFVGBysFzFWJ3mOtNXgGBIKEyPBKaK413PmITT4rro3A+1bXtQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@arraypress/waveform-player": "1.2.1", + "@wordpress/a11y": "^4.48.0", + "@wordpress/api-fetch": "^7.48.0", + "@wordpress/autop": "^4.48.0", + "@wordpress/base-styles": "^9.1.0", + "@wordpress/blob": "^4.48.0", + "@wordpress/block-editor": "^15.21.0", + "@wordpress/blocks": "^15.21.0", + "@wordpress/components": "^35.0.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/core-data": "^7.48.0", + "@wordpress/data": "^10.48.0", + "@wordpress/date": "^5.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/dom": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/escape-html": "^3.48.0", + "@wordpress/hooks": "^4.48.0", + "@wordpress/html-entities": "^4.48.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/interactivity": "^6.48.0", + "@wordpress/interactivity-router": "^2.48.0", + "@wordpress/keyboard-shortcuts": "^5.48.0", + "@wordpress/keycodes": "^4.48.0", + "@wordpress/latex-to-mathml": "^1.16.0", + "@wordpress/notices": "^5.48.0", + "@wordpress/patterns": "^2.48.0", + "@wordpress/primitives": "^4.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/reusable-blocks": "^5.48.0", + "@wordpress/rich-text": "^7.48.0", + "@wordpress/server-side-render": "^6.24.0", + "@wordpress/shortcode": "^4.48.0", + "@wordpress/ui": "^0.15.0", + "@wordpress/upload-media": "^0.33.0", + "@wordpress/url": "^4.48.0", + "@wordpress/viewport": "^6.48.0", + "@wordpress/wordcount": "^4.48.0", + "change-case": "^4.1.2", + "clsx": "^2.1.1", + "colord": "^2.7.0", + "fast-average-color": "^9.1.1", + "fast-deep-equal": "^3.1.3", + "html-react-parser": "5.2.11", + "memize": "^2.1.0", + "remove-accents": "^0.5.0", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/block-serialization-default-parser": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/block-serialization-default-parser/-/block-serialization-default-parser-5.48.0.tgz", + "integrity": "sha512-bbG7qlz3BZNnZRLtwwFl/VK/ynDtZ3XDbLiTCXrOGF3ij4RdA+Vng3nTNBxxPKLy8gB5t8Fgl5DGK3MXhn+RWA==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/blocks": { + "version": "15.21.0", + "resolved": "https://registry.npmjs.org/@wordpress/blocks/-/blocks-15.21.0.tgz", + "integrity": "sha512-nq+lOxGFAET150U2dbC/yf5M1l52NlGFffKDr3ChmKscGh4R5KRYfv4fmqQVYujbapWCNJHZJXI63yrSFUOhMA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/autop": "^4.48.0", + "@wordpress/blob": "^4.48.0", + "@wordpress/block-serialization-default-parser": "^5.48.0", + "@wordpress/data": "^10.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/dom": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/hooks": "^4.48.0", + "@wordpress/html-entities": "^4.48.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/is-shallow-equal": "^5.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/rich-text": "^7.48.0", + "@wordpress/shortcode": "^4.48.0", + "@wordpress/warning": "^3.48.0", + "change-case": "^4.1.2", + "colord": "^2.7.0", + "fast-deep-equal": "^3.1.3", + "hpq": "^1.3.0", + "is-plain-object": "^5.0.0", + "memize": "^2.1.0", + "react-is": "^18.3.0", + "remove-accents": "^0.5.0", + "showdown": "^1.9.1", + "simple-html-tokenizer": "^0.5.7", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/commands": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/commands/-/commands-1.48.0.tgz", + "integrity": "sha512-wSo0Sj0Y7Z+yNfhp8QouB7rBSC9d3+Vb2/RKB/480+WlidFvCDgsFJPojNenqXdPlUTVcbgjsj0jLls8BbwHbA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/base-styles": "^9.1.0", + "@wordpress/components": "^35.0.0", + "@wordpress/data": "^10.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/keyboard-shortcuts": "^5.48.0", + "@wordpress/preferences": "^4.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/warning": "^3.48.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/components": { + "version": "35.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/components/-/components-35.0.0.tgz", + "integrity": "sha512-zXhErp2/alcdvQST6pq/kkZGkiOvTGbYqRc3FuoQIDpCJJE70r243PRxokDvZ5ikHvBtg26kARN+JhXdzN4qjw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@ariakit/react": "^0.4.22", + "@date-fns/utc": "^2.1.1", + "@emotion/cache": "^11.14.0", + "@emotion/css": "^11.13.5", + "@emotion/native": "^11.11.0", + "@emotion/react": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/styled": "^11.14.1", + "@emotion/utils": "^1.4.2", + "@floating-ui/react-dom": "2.0.8", + "@types/gradient-parser": "1.1.0", + "@types/highlight-words-core": "1.2.1", + "@types/react": "^18.3.27", + "@use-gesture/react": "^10.3.1", + "@wordpress/a11y": "^4.48.0", + "@wordpress/base-styles": "^9.1.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/date": "^5.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/dom": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/escape-html": "^3.48.0", + "@wordpress/hooks": "^4.48.0", + "@wordpress/html-entities": "^4.48.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/is-shallow-equal": "^5.48.0", + "@wordpress/keycodes": "^4.48.0", + "@wordpress/primitives": "^4.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/rich-text": "^7.48.0", + "@wordpress/style-runtime": "^0.4.0", + "@wordpress/ui": "^0.15.0", + "@wordpress/warning": "^3.48.0", + "change-case": "^4.1.2", + "clsx": "^2.1.1", + "colord": "^2.7.0", + "csstype": "^3.2.3", + "date-fns": "^4.1.0", + "deepmerge": "^4.3.0", + "fast-deep-equal": "^3.1.3", + "framer-motion": "^11.15.0", + "gradient-parser": "1.1.1", + "highlight-words-core": "^1.2.2", + "is-plain-object": "^5.0.0", + "memize": "^2.1.0", + "path-to-regexp": "^6.2.1", + "re-resizable": "^6.4.0", + "react-colorful": "^5.6.1", + "react-day-picker": "^9.7.0", + "remove-accents": "^0.5.0", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/compose": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@wordpress/compose/-/compose-8.1.0.tgz", + "integrity": "sha512-CAEQxrh3f19ku0SAnYAAiKcUe1zqaK9f0c8vJh+6qrpQnUjl7xLXj5TJOukXlzFH3Z9VZn6fJVfXqmDNAYIhQA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/mousetrap": "^1.6.8", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/dom": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/is-shallow-equal": "^5.48.0", + "@wordpress/keycodes": "^4.48.0", + "@wordpress/priority-queue": "^3.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/undo-manager": "^1.48.0", + "change-case": "^4.1.2", + "mousetrap": "^1.6.5", + "use-memo-one": "^1.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/core-data": { + "version": "7.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/core-data/-/core-data-7.48.0.tgz", + "integrity": "sha512-coukurBp/mTSugI1PRKwunJsk9/sVilwdFv5h4yFWisVIMclZQ0GJg+MOLiK18fWEtqjPD2J1j7Aoz12QEx1Lw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/api-fetch": "^7.48.0", + "@wordpress/block-editor": "^15.21.0", + "@wordpress/blocks": "^15.21.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/data": "^10.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/html-entities": "^4.48.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/is-shallow-equal": "^5.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/rich-text": "^7.48.0", + "@wordpress/sync": "^1.48.0", + "@wordpress/undo-manager": "^1.48.0", + "@wordpress/url": "^4.48.0", + "@wordpress/warning": "^3.48.0", + "change-case": "^4.1.2", + "equivalent-key-map": "^0.2.2", + "fast-deep-equal": "^3.1.3", + "memize": "^2.1.0", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/data": { + "version": "10.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/data/-/data-10.48.0.tgz", + "integrity": "sha512-6SjfTBlXu5fuJWmmlHlwV2wcrcsWL+M5O227AoEvrPSLo96UuMj2kAx3cKLtP3xyOMDyd38koQSf6+SS522bTA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/compose": "^8.1.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/is-shallow-equal": "^5.48.0", + "@wordpress/priority-queue": "^3.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/redux-routine": "^5.48.0", + "deepmerge": "^4.3.0", + "equivalent-key-map": "^0.2.2", + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "redux": "^5.0.1", + "rememo": "^4.0.2", + "use-memo-one": "^1.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/dataviews": { + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/dataviews/-/dataviews-16.0.0.tgz", + "integrity": "sha512-02rbslxalTNasLV8w/zAifCsUU5Pug8GiduWIEKRiNtazvJ8duz8fIcQ2Jgl31ruRItcu3fcG7XUk1OtwsdcZQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@ariakit/react": "^0.4.21", + "@wordpress/base-styles": "^9.1.0", + "@wordpress/components": "^35.0.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/data": "^10.48.0", + "@wordpress/date": "^5.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/keycodes": "^4.48.0", + "@wordpress/primitives": "^4.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/ui": "^0.15.0", + "@wordpress/warning": "^3.48.0", + "clsx": "^2.1.1", + "colord": "^2.7.0", + "date-fns": "^4.1.0", + "deepmerge": "4.3.1", + "fast-deep-equal": "^3.1.3", + "remove-accents": "^0.5.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/date": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/date/-/date-5.48.0.tgz", + "integrity": "sha512-HgXtYAD2IOrPDY83xzkT/8abYj2nMlkbC+lfSpB4lExlSVrIbz5oYUtktH8k5EBZjVBMFsE7mdMQyQjUeCQbeQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/deprecated": "^4.48.0", + "moment": "^2.29.4", + "moment-timezone": "^0.5.40" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/deprecated": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/deprecated/-/deprecated-4.48.0.tgz", + "integrity": "sha512-aTa7oww6hvTjfIvxLsxlcwYj7skAGPnr1V2S0iBVQfiIn5wJPiGjM9hz4QEf6kyR44Vh0IYjW9wSxVuDMGZUdw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/hooks": "^4.48.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/dom": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom/-/dom-4.48.0.tgz", + "integrity": "sha512-9UARZ0YQfmhx9VAi+QynSwu5fOJoG4mmPNTpYW8jDmtKh+9c2YIi1YSQFuOa1sipj78ZLPaBxaceZ7dbxKc3UA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/deprecated": "^4.48.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/dom-ready": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/dom-ready/-/dom-ready-4.48.0.tgz", + "integrity": "sha512-jtH9/4FBTsfYLJDzgiXs41nceTrfvuLXqaWa5IN8drHvXZde6Dhz78m3KCZLrOB5DEE1tbyBNyZkcWM8HNVZ0Q==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/element": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/@wordpress/element/-/element-8.0.0.tgz", + "integrity": "sha512-lQ8TB2vBr6lzcwQ2zh0xy+FC77Demb3FqL81fzpSsLbGUF9hZiTyyUuwc6SG21gCkGGjVm2TnU9BuHax/8nDfQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@types/react": "^18.3.27", + "@types/react-dom": "^18.3.1", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/escape-html": "^3.48.0", + "change-case": "^4.1.2", + "is-plain-object": "^5.0.0", + "react": "^18.3.0", + "react-dom": "^18.3.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/escape-html": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/escape-html/-/escape-html-3.48.0.tgz", + "integrity": "sha512-phw399RofSqTqIM4DikmkDfgJ7exDYgPfDuxjv3D2YnUTTUsR+U9fA+pA+/rNUiZD1YOmVILQmkJt6oLaVM+nQ==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/global-styles-engine": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@wordpress/global-styles-engine/-/global-styles-engine-1.15.0.tgz", + "integrity": "sha512-okgCtWjuy4AH6+yu7Rn9p4t1l9Cc8dtaJbV4dDr3mIg9w77amw4gjOlqpx6TU3iM/2RW8GNzinhNMoK1zYZe6g==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/blocks": "^15.21.0", + "@wordpress/data": "^10.48.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/style-engine": "^2.48.0", + "colord": "^2.9.2", + "deepmerge": "^4.3.0", + "fast-deep-equal": "^3.1.3", + "is-plain-object": "^5.0.0", + "memize": "^2.1.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/hooks": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/hooks/-/hooks-4.48.0.tgz", + "integrity": "sha512-rU1yGEy0Mb+2oRG5QX/bKIIwKQmYAvATfUQeXIF20/mbR0qutYeVTCIvWEyb4pf71tvnQFiN18RWRXWsvKrDbQ==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/html-entities": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/html-entities/-/html-entities-4.48.0.tgz", + "integrity": "sha512-KGxdaLC36wE10GybSfjYGcyWiy+KQCYheB6T8jhZhQ9mlf2Zwx6aJgfZm/L6BLwNN33Efx+sJY3nvMIxI5UwnA==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/i18n": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/@wordpress/i18n/-/i18n-6.21.0.tgz", + "integrity": "sha512-IXGGUJqN6b7QddU0dZB3HLJKu6uDQuhLsrrzYpUYTjDhfa43XEaikA9xHNgZhqzRtOVYqsNHVliWcISvJ/xjZQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@tannin/sprintf": "^1.3.2", + "@wordpress/hooks": "^4.48.0", + "gettext-parser": "^1.3.1", + "memize": "^2.1.0", + "tannin": "^1.2.0" + }, + "bin": { + "pot-to-php": "tools/pot-to-php.js" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/icons": { + "version": "13.3.0", + "resolved": "https://registry.npmjs.org/@wordpress/icons/-/icons-13.3.0.tgz", + "integrity": "sha512-Y/iE3aeHQ4XkX0fffiTPCUfjT8wNw1I7hDJkKqpaLmkD+C5NKWixRrDVfRnaJqU/MxY8RdyVC/nGng2MLPNH0A==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^8.0.0", + "@wordpress/primitives": "^4.48.0", + "change-case": "4.1.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/image-cropper": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/@wordpress/image-cropper/-/image-cropper-1.12.0.tgz", + "integrity": "sha512-mOoMaVMC+HtxvPM2Iq+I3qXHoHhcsEEc1hiLUjfS8KIGUkm11LSk/LeHjhpnA1My8gAeUqYREFCwPoK91+rEFA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/components": "^35.0.0", + "@wordpress/element": "^8.0.0", + "@wordpress/i18n": "^6.21.0", + "clsx": "^2.1.1", + "dequal": "^2.0.3", + "react-easy-crop": "^5.4.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/interactivity": { + "version": "6.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/interactivity/-/interactivity-6.48.0.tgz", + "integrity": "sha512-61VSanGsCASE1yBtMHC7s1HSZw24sF+HZNWiPsP95ZP9EKWlhTHAyKyhv99+PJUnSL/oSNYQNQeQDwABMPc7oA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@preact/signals": "^1.3.0", + "preact": "^10.29.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/interactivity-router": { + "version": "2.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/interactivity-router/-/interactivity-router-2.48.0.tgz", + "integrity": "sha512-II5THt2l+MP0aP3bSZou0//7OYmffy8kKucD3hfhRILi0FKHLXc378HM+uwd9FOoPPDmnB2+0y4D4JMcz214EQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^4.48.0", + "@wordpress/interactivity": "^6.48.0", + "es-module-lexer": "^1.5.4" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/is-shallow-equal": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/is-shallow-equal/-/is-shallow-equal-5.48.0.tgz", + "integrity": "sha512-7ipiZ1+m84RfuVhiMbtKm6RN571W3ERV/pTL+fSG2qOVhLqccFmliuFHTKQG+0KIhV8DegOlE6eoKOenf+I9ng==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/keyboard-shortcuts": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/keyboard-shortcuts/-/keyboard-shortcuts-5.48.0.tgz", + "integrity": "sha512-j4EXbAykqrmauDRKSHvr1FimH4thsHUXVfGjNPzDqF0JQGqCgribqslKANNYJXrDQplt2aHhO0XHYZXxpNfgbQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/data": "^10.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/keycodes": "^4.48.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/keycodes": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/keycodes/-/keycodes-4.48.0.tgz", + "integrity": "sha512-u3Uxxe3rDAqEmerAiJ2X94s7iO3ZVgS+10MFyD4nWhfuB/C6m/M2TqHPgZiKvyDH04EIhe+pIF2KFO4pq7NWsw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/i18n": "^6.21.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/latex-to-mathml": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@wordpress/latex-to-mathml/-/latex-to-mathml-1.16.0.tgz", + "integrity": "sha512-WEXC9GKgMLXXqbezKZWw9cwozS4/dM23VDresod1nqfvgMSpvtgHsFP98wJWg2iQyA6SIksSuD/SROSaBCZ17A==", + "license": "GPL-2.0-or-later", + "dependencies": { + "temml": "^0.10.33" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/notices": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/notices/-/notices-5.48.0.tgz", + "integrity": "sha512-s1vRbDlJDcFFQnlUaYIZDiVlMpBnlmZk4y2EjRawoCZApiz1V+Yx/mHDqIrFrM7l50ZI3Dh63wDdu2qzNgc7wQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^4.48.0", + "@wordpress/components": "^35.0.0", + "@wordpress/data": "^10.48.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/patterns": { + "version": "2.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/patterns/-/patterns-2.48.0.tgz", + "integrity": "sha512-6RCHrdfS8cMw+JIyhISYqNux7tvavDhzZMP+2RTNKrqVqEXHxO0MCtk/NX5L9GfJ3kr46QVBrrZUqB2X/o+wJg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^4.48.0", + "@wordpress/base-styles": "^9.1.0", + "@wordpress/block-editor": "^15.21.0", + "@wordpress/blocks": "^15.21.0", + "@wordpress/components": "^35.0.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/core-data": "^7.48.0", + "@wordpress/data": "^10.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/html-entities": "^4.48.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/notices": "^5.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/url": "^4.48.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/preferences": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/preferences/-/preferences-4.48.0.tgz", + "integrity": "sha512-ae8SOpc+NTFf5dB1bgN4RwMCzCQC/gX0d72SDxqtBeU1N52+sihunob9bhPLAEimKS/nMR/kU+YS9j9y5jyZ0A==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^4.48.0", + "@wordpress/base-styles": "^9.1.0", + "@wordpress/components": "^35.0.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/data": "^10.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/private-apis": "^1.48.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/primitives": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/primitives/-/primitives-4.48.0.tgz", + "integrity": "sha512-dfF7IZotIqb6LUiGs7oPwKbSF8RPoC0JDSIrtxvgwFA/yvbc/pDIp/Zs0O8GvxZNxu4JIVnKskOhoLq7lAeziQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^8.0.0", + "clsx": "^2.1.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/priority-queue": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/priority-queue/-/priority-queue-3.48.0.tgz", + "integrity": "sha512-NuGrfSSnBC794erb3xSEKrzWLGCNLa+ukob0pyVRtnebU7fPgrhx4NCBCXYK1vTcAta3NAkOVRfUZgcmLFYA6g==", + "license": "GPL-2.0-or-later", + "dependencies": { + "requestidlecallback": "^0.3.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/private-apis": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/private-apis/-/private-apis-1.48.0.tgz", + "integrity": "sha512-HHOSXLCAlBggfMozwWtX36wgsSt22g2tZwpka47Rjzr3hNY1BZ6SrrFJumiNxooy5PDKbRgcF092PAF82hdJXg==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/redux-routine": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/redux-routine/-/redux-routine-5.48.0.tgz", + "integrity": "sha512-MxRgJJyddivxvVhPrn8yEFXTH3WLtoRGNCMiBRJwoIr4GkY8iOFSfRaqOJEkE1zrP4JK6qGFmv1xMvWt78c7ow==", + "license": "GPL-2.0-or-later", + "dependencies": { + "is-plain-object": "^5.0.0", + "is-promise": "^4.0.0", + "rungen": "^0.3.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "redux": ">=4" + } + }, + "node_modules/@wordpress/reusable-blocks": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/reusable-blocks/-/reusable-blocks-5.48.0.tgz", + "integrity": "sha512-7WdKxKGP7PWSUuatuUoLrum+gapJ/Dqq2zp0zMGtjR+mH5/TQ483TR/TyDT09H49uWYgLSST+pT++KVo5PUNxg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/base-styles": "^9.1.0", + "@wordpress/block-editor": "^15.21.0", + "@wordpress/blocks": "^15.21.0", + "@wordpress/components": "^35.0.0", + "@wordpress/core-data": "^7.48.0", + "@wordpress/data": "^10.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/notices": "^5.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/url": "^4.48.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/rich-text": { + "version": "7.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/rich-text/-/rich-text-7.48.0.tgz", + "integrity": "sha512-rMiTTpRnpdynL9BnuI2MkSXzd12Js8gYSnlbVwxNNKNeFEXT+3Ah2oNCGvSb82pD/73Bl5BIGC5395D5a3X9yw==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/a11y": "^4.48.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/data": "^10.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/dom": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/escape-html": "^3.48.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/keycodes": "^4.48.0", + "@wordpress/private-apis": "^1.48.0", + "colord": "2.9.3", + "memize": "^2.1.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/server-side-render": { + "version": "6.24.0", + "resolved": "https://registry.npmjs.org/@wordpress/server-side-render/-/server-side-render-6.24.0.tgz", + "integrity": "sha512-cmn8cWW+N4Qpf/wnSjPSWW/QJ/82K1pvxDh5tihd5ovYiKZFTD0P/Z37UMTE+NdC7AzYQGiWzDgUq3Zo33DadQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/api-fetch": "^7.48.0", + "@wordpress/blocks": "^15.21.0", + "@wordpress/components": "^35.0.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/data": "^10.48.0", + "@wordpress/deprecated": "^4.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/url": "^4.48.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/shortcode": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/shortcode/-/shortcode-4.48.0.tgz", + "integrity": "sha512-frF+OdYHJBptTdJzFGOBs7tTpvIFf01Q0vNHdzzZAFMaeA1SzwotRj8mntNlSg2aeX5HNkhzxdDzGNA5wdqQxA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "memize": "^2.0.1" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/style-engine": { + "version": "2.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/style-engine/-/style-engine-2.48.0.tgz", + "integrity": "sha512-KC2WaAH1ElIbHx/6A3PkagR7yBZS9ftlIorKphFWwZtpHZu1niZoSzsSuk/gaTsV0AZBwiZA2RNtf0C6SDmCww==", + "license": "GPL-2.0-or-later", + "dependencies": { + "change-case": "^4.1.2" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/style-runtime": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@wordpress/style-runtime/-/style-runtime-0.4.0.tgz", + "integrity": "sha512-frzAg1rsn8X0KNgrxxLxszLvWCKY0Nk2e8j8Mjm2pI2URmS8Et7NefuXP3JnHBD4U1L1Ug9yKO/FA65ojQ7CEA==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@wordpress/sync": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/sync/-/sync-1.48.0.tgz", + "integrity": "sha512-nkDL58Xzl4UoDAhlHJu3pkpHljQiw88hKNJZBUr6DsBIIRhWR4wLE0aiCW7oeGwZLYaFC0Stxv1QpDQ2oSS48A==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/api-fetch": "^7.48.0", + "@wordpress/hooks": "^4.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/undo-manager": "^1.48.0", + "diff": "^8.0.3", + "fast-deep-equal": "^3.1.3", + "lib0": "0.2.99", + "y-protocols": "^1.0.7", + "yjs": "13.6.29" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/sync/node_modules/diff": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.4.tgz", + "integrity": "sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/@wordpress/theme": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@wordpress/theme/-/theme-0.15.0.tgz", + "integrity": "sha512-qoozJ4YEPb0LvTBnTMj8a7kPlQtT2LeGL7b/vKJkvnB9dIEUOED5c0rpeRZJoK9b77fpUH5GwYzPE3IWiQ6l2w==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/element": "^8.0.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/style-runtime": "^0.4.0", + "colorjs.io": "^0.6.0", + "memize": "^2.1.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0", + "stylelint": "^16.8.2" + }, + "peerDependenciesMeta": { + "stylelint": { + "optional": true + } + } + }, + "node_modules/@wordpress/token-list": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/token-list/-/token-list-3.48.0.tgz", + "integrity": "sha512-KrtNnVI9YAqBspjzKR3ELBexOQvIzxqfEuq6CasXr9w7vHn7YkEGSwbeZGNz5dEVirPFDnwAK2uVpuT8dvFiWA==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/ui": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@wordpress/ui/-/ui-0.15.0.tgz", + "integrity": "sha512-7aAx1ovnC6JOb4Qfcnfk8ESfB0RTm6rqsdFrUn7TEY3LON/aEQisCb/bd7Yb8s9txb1GfaJYkgjiTvrr0M6EWA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@base-ui/react": "^1.4.1", + "@wordpress/a11y": "^4.48.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/element": "^8.0.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/icons": "^13.3.0", + "@wordpress/keycodes": "^4.48.0", + "@wordpress/primitives": "^4.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/style-runtime": "^0.4.0", + "@wordpress/theme": "^0.15.0", + "clsx": "^2.1.1", + "tabbable": "^6.4.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/undo-manager": { + "version": "1.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/undo-manager/-/undo-manager-1.48.0.tgz", + "integrity": "sha512-HqPGxMvZeWZJ6AVaCqZhfGpH6tqq5+hMlaqh4aCO0SvZ2Gvc6fbXEoVpqWfKozO1DyJW2GnRf8At8PpPt2IopQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/is-shallow-equal": "^5.48.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/upload-media": { + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@wordpress/upload-media/-/upload-media-0.33.0.tgz", + "integrity": "sha512-jtAbDbk6yuW74HqavA90lr59eSENTdwAKjkCgjZlLLt4zHSFXAxYuDWLk+ouaW0xnjqfWKl2/ByQGyjKes/YRA==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/blob": "^4.48.0", + "@wordpress/compose": "^8.1.0", + "@wordpress/data": "^10.48.0", + "@wordpress/element": "^8.0.0", + "@wordpress/i18n": "^6.21.0", + "@wordpress/preferences": "^4.48.0", + "@wordpress/private-apis": "^1.48.0", + "@wordpress/url": "^4.48.0", + "@wordpress/vips": "^2.1.0", + "uuid": "^14.0.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, + "node_modules/@wordpress/url": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/url/-/url-4.48.0.tgz", + "integrity": "sha512-NfhCvFyJnKQ7XnqLlFXbigwZzhnNQZPgS+mpXTkttq/d0/b62TgvjQd5XIu5wiEkWXye7rmZfdkRmG8fWmEb3Q==", + "license": "GPL-2.0-or-later", + "dependencies": { + "remove-accents": "^0.5.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/viewport": { + "version": "6.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/viewport/-/viewport-6.48.0.tgz", + "integrity": "sha512-mP9BAg4xsFMiActGBjmADqcws+URFloJEfOFiCDe8y1BqWHdeNaUBC1cjXcgYj4hjmcij/lCBVdccKHg6BEAgg==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/compose": "^8.1.0", + "@wordpress/data": "^10.48.0", + "@wordpress/element": "^8.0.0" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + }, + "peerDependencies": { + "react": "^18.0.0" + } + }, + "node_modules/@wordpress/vips": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@wordpress/vips/-/vips-2.1.0.tgz", + "integrity": "sha512-XaLh9CLDv6xcimDuXr4roUgAFsZ34jaW9I0zichRonjSmxtZCiSbHUaPtmz0pkBuoJe4Uiv/GTIBMnMBxb2Z5g==", + "license": "GPL-2.0-or-later", + "dependencies": { + "@wordpress/worker-threads": "^1.8.0", + "wasm-vips": "^0.0.16" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/warning": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/warning/-/warning-3.48.0.tgz", + "integrity": "sha512-En+A99j8aySNzUH0iXok0H2Xi+Uw2useKqYsvPm33VEMa0a0XIwa2I9srK5STp8RydCm1dK+/41K9e5xeFu23Q==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/wordcount": { + "version": "4.48.0", + "resolved": "https://registry.npmjs.org/@wordpress/wordcount/-/wordcount-4.48.0.tgz", + "integrity": "sha512-P9xSlfxL0I5nDPUazuekfJ0tkYWnvrqPDO3YOIEsD4LDNKWWxtzYLmHj4GOEYeQZ3KnJ2wu4VPxDAsFj15cMSg==", + "license": "GPL-2.0-or-later", + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wordpress/worker-threads": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@wordpress/worker-threads/-/worker-threads-1.8.0.tgz", + "integrity": "sha512-GyXj5tLYH5aon7yIJbqODeGmIowrjAohA2UrEJLU4UKbJdVAWyx9dlqF/bGs3sUKkkldPxYU7Wk2WIATfu0VGQ==", + "license": "GPL-2.0-or-later", + "dependencies": { + "comctx": "^1.4.3" + }, + "engines": { + "node": ">=18.12.0", + "npm": ">=8.19.2" + } + }, + "node_modules/@wp-playground/blueprints": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@wp-playground/blueprints/-/blueprints-3.1.38.tgz", + "integrity": "sha512-OdmiDVK2EEzsohrf4xJxNYKcuubYB23t+bcwPUBQpzcVILiidLqljmRghrkGNex5ky58KTnuLn9gubKGWWmGjg==", + "dev": true, + "dependencies": { + "@php-wasm/logger": "3.1.38", + "@php-wasm/progress": "3.1.38", + "@php-wasm/stream-compression": "3.1.38", + "@php-wasm/universal": "3.1.38", + "@php-wasm/util": "3.1.38", + "@php-wasm/web-service-worker": "3.1.38", + "@wp-playground/common": "3.1.38", + "@wp-playground/storage": "3.1.38", + "@wp-playground/wordpress": "3.1.38", + "ajv": "8.18.0" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@wp-playground/blueprints/node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@wp-playground/cli": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@wp-playground/cli/-/cli-3.1.38.tgz", + "integrity": "sha512-xvTQ053ToDa+jd47Bxmuambzfx4lm0ILXDXGoxkd62oaMsyB8+EczfwZhLLECaCDwVzlCM9XO0mOWEJ8YNC9KQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/cli-util": "3.1.38", + "@php-wasm/logger": "3.1.38", + "@php-wasm/node": "3.1.38", + "@php-wasm/progress": "3.1.38", + "@php-wasm/universal": "3.1.38", + "@php-wasm/util": "3.1.38", + "@php-wasm/xdebug-bridge": "3.1.38", + "@wp-playground/blueprints": "3.1.38", + "@wp-playground/common": "3.1.38", + "@wp-playground/storage": "3.1.38", + "@wp-playground/tools": "3.1.38", + "@wp-playground/wordpress": "3.1.38", + "express": "4.22.2", + "fs-extra": "11.1.1", + "tmp-promise": "3.0.3", + "wasm-feature-detect": "1.8.0", + "yargs": "17.7.2" + }, + "bin": { + "wp-playground-cli": "wp-playground.js" + } + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@wp-playground/cli/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@wp-playground/cli/node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "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/cli/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==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/@wp-playground/common": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@wp-playground/common/-/common-3.1.38.tgz", + "integrity": "sha512-v2nO0h9US0ohIOuSOeYwn8GmOkYkvoUdGAsKnRMPkmXd4zRcvduvfuhSunZXZYIkHiZc7pPawPUtuHml5oGHFQ==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/universal": "3.1.38", + "@php-wasm/util": "3.1.38" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@wp-playground/storage": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@wp-playground/storage/-/storage-3.1.38.tgz", + "integrity": "sha512-QSDb8f5eGg2ZE6dq2hWHJHTj5kVVlPZDoCTAv5f+VE8gauBc/8FKKQgZgGPW/3t8LcSt8GLYG50Uw3B+xOwHIg==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/stream-compression": "3.1.38", + "@php-wasm/universal": "3.1.38", + "@php-wasm/util": "3.1.38", + "@zip.js/zip.js": "2.7.57", + "isomorphic-git": "1.37.6", + "octokit": "3.1.2", + "pako": "^1.0.10" + } + }, + "node_modules/@wp-playground/tools": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@wp-playground/tools/-/tools-3.1.38.tgz", + "integrity": "sha512-teegXL7ZDC6RO2r7i0Qa9VOTGynPrDaoN300kkIgO7K8DKDNfm7lblrL1AhdKxQ4wtTavCw2uqdt2y+sUqp5IA==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@wp-playground/blueprints": "3.1.38" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "node_modules/@wp-playground/wordpress": { + "version": "3.1.38", + "resolved": "https://registry.npmjs.org/@wp-playground/wordpress/-/wordpress-3.1.38.tgz", + "integrity": "sha512-LIJPoXF68TXZxX7mUTCULFcS9enMA7zPMxoXvizZsG9ORGjFiMACeX1BsKaneZWIZjozZtZBlFwMb63+K6M5Ig==", + "dev": true, + "license": "GPL-2.0-or-later", + "dependencies": { + "@php-wasm/logger": "3.1.38", + "@php-wasm/universal": "3.1.38", + "@php-wasm/util": "3.1.38", + "@wp-playground/common": "3.1.38" + }, + "engines": { + "node": ">=20.10.0", + "npm": ">=10.2.3" + } + }, + "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", + "integrity": "sha512-BtonQ1/jDnGiMed6OkV6rZYW78gLmLswkHOzyMrMb+CAR7CZO8phOHO6c2qw6qb1g1betN7kwEHhhZk30dv+NA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "bun": ">=0.7.0", + "deno": ">=1.0.0", + "node": ">=16.5.0" + } + }, + "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" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "license": "MIT", + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=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", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/anser": { + "version": "1.4.10", + "resolved": "https://registry.npmjs.org/anser/-/anser-1.4.10.tgz", + "integrity": "sha512-hCv9AqTQ8ycjpSd3upOJd7vFwW1JaoYQ7tpham03GJ1ca8/65rqn0RpaWpItOAd6ylW9wAw6luXYPJIyPFVOww==", + "license": "MIT", + "peer": true + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "peer": true, + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anynum": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/anynum/-/anynum-1.0.0.tgz", + "integrity": "sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT" + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "license": "MIT", + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/autosize": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/autosize/-/autosize-4.0.4.tgz", + "integrity": "sha512-5yxLQ22O0fCRGoxGfeLSNt3J8LB1v+umtpMnPW6XjkTWXKoN0AmXAIhelJcDtFT/Y/wYWmfE+oqU10Q0b8FhaQ==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/babel-jest": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", + "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/transform": "^29.7.0", + "@types/babel__core": "^7.1.14", + "babel-plugin-istanbul": "^6.1.1", + "babel-preset-jest": "^29.6.3", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "slash": "^3.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.8.0" + } + }, + "node_modules/babel-plugin-istanbul": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", + "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@babel/helper-plugin-utils": "^7.0.0", + "@istanbuljs/load-nyc-config": "^1.0.0", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-instrument": "^5.0.4", + "test-exclude": "^6.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/babel-plugin-jest-hoist": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", + "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/template": "^7.3.3", + "@babel/types": "^7.3.3", + "@types/babel__core": "^7.1.14", + "@types/babel__traverse": "^7.0.6" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "node_modules/babel-plugin-syntax-hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-hermes-parser/-/babel-plugin-syntax-hermes-parser-0.32.0.tgz", + "integrity": "sha512-m5HthL++AbyeEA2FcdwOLfVFvWYECOBObLHNqdR8ceY4TsEdn4LdX2oTvbB2QJSSElE2AWA/b2MXZ/PF/CqLZg==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-parser": "0.32.0" + } + }, + "node_modules/babel-preset-current-node-syntax": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/plugin-syntax-async-generators": "^7.8.4", + "@babel/plugin-syntax-bigint": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", + "@babel/plugin-syntax-json-strings": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", + "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", + "@babel/plugin-syntax-object-rest-spread": "^7.8.3", + "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", + "@babel/plugin-syntax-optional-chaining": "^7.8.3", + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" + }, + "peerDependencies": { + "@babel/core": "^7.0.0 || ^8.0.0-0" + } + }, + "node_modules/babel-preset-jest": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", + "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", + "license": "MIT", + "peer": true, + "dependencies": { + "babel-plugin-jest-hoist": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT", + "peer": true + }, + "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", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.35", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.35.tgz", + "integrity": "sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "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==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/body-parser": { + "version": "1.20.5", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.5.tgz", + "integrity": "sha512-3grm+/2tUOvu2cjJkvsIxrv/wVpfXQW4PsQHYm7yk4vfpu7Ekl6nEsYBoJUL6qDwZUx8wUhQ8tR2qz+ad9c9OA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/body-parser/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/body-parser/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/body-parser/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/body-parser/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.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==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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==", + "license": "MIT", + "peer": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bser": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", + "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "node-int64": "^0.4.0" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "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==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "license": "MIT", + "peer": true + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.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==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "get-intrinsic": "^1.3.0", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, + "node_modules/camelcase": { + "version": "5.3.1", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", + "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelize": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz", + "integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001797", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001797.tgz", + "integrity": "sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0", + "peer": true + }, + "node_modules/capital-case": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/capital-case/-/capital-case-1.0.4.tgz", + "integrity": "sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "license": "MIT", + "peer": true, + "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/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", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk/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", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/chalk/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", + "peer": true + }, + "node_modules/change-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/change-case/-/change-case-4.1.2.tgz", + "integrity": "sha512-bSxY2ws9OtviILG1EiY5K7NNxkqg/JnRnFxLtKQ96JaviiIxi7djMrSd0ECT9AC+lttClmYwKw53BWpOMblo7A==", + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "capital-case": "^1.0.4", + "constant-case": "^3.0.4", + "dot-case": "^3.0.4", + "header-case": "^2.0.4", + "no-case": "^3.0.4", + "param-case": "^3.0.4", + "pascal-case": "^3.1.2", + "path-case": "^3.0.4", + "sentence-case": "^3.0.4", + "snake-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/chrome-launcher": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.15.2.tgz", + "integrity": "sha512-zdLEwNo3aUVzIhKhTtXfxhdvZhUghrnmkvcAq2NoDd+LeOHKf03H5jwZ8T/STsAlzyALkBVK552iaG1fGf1xVQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0" + }, + "bin": { + "print-chrome-path": "bin/print-chrome-path.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/chromium-edge-launcher": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-edge-launcher/-/chromium-edge-launcher-0.2.0.tgz", + "integrity": "sha512-JfJjUnq25y9yg4FABRRVPmBGWPZZi+AQXT4mxupb67766/0UlhG8PAZCz6xzEMXTbW3CsSoE8PcCWA49n35mKg==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "@types/node": "*", + "escape-string-regexp": "^4.0.0", + "is-wsl": "^2.2.0", + "lighthouse-logger": "^1.0.0", + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + } + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "peer": true, + "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", + "integrity": "sha512-bLSptAy2P0s6hU4PzuIMKmMJJSE6gLXGH1cntDu7bWJUksvuM+7ReOK61mozULErYvP6a15rnYl0zFDef+pyPw==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cliui": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-5.0.0.tgz", + "integrity": "sha512-PYeGSEmmHM6zvoef2w8TPzlrnNpXIjTipYK780YswmIP9vjxmd6Y2a3CB2Ks6/AU8NHjZugXvo8w3oWM2qnwXA==", + "license": "ISC", + "dependencies": { + "string-width": "^3.1.0", + "strip-ansi": "^5.2.0", + "wrap-ansi": "^5.1.0" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cmdk": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cmdk/-/cmdk-1.1.1.tgz", + "integrity": "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "^1.1.1", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-id": "^1.1.0", + "@radix-ui/react-primitive": "^2.0.2" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "react-dom": "^18 || ^19 || ^19.0.0-rc" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "license": "MIT" + }, + "node_modules/colord": { + "version": "2.9.3", + "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", + "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==", + "license": "MIT" + }, + "node_modules/colorjs.io": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/colorjs.io/-/colorjs.io-0.6.1.tgz", + "integrity": "sha512-8lyR2wHzuIykCpqHKgluGsqQi5iDm3/a2IgP2GBZrasn2sBRkE4NOGsglZxWLs/jZQoNkmA/KM/8NV16rLUdBg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/color" + } + }, + "node_modules/comctx": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/comctx/-/comctx-1.7.4.tgz", + "integrity": "sha512-c5j2twNsFePTxuyPuR1x7UhCxu+BaFtUk8S1okJW9/qcy02PN29P3O8dy4XTtyQIgXYRTZinOYPcZJYMAbg4WQ==", + "license": "MIT" + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/computed-style": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/computed-style/-/computed-style-0.1.4.tgz", + "integrity": "sha512-WpAmaKbMNmS3OProfHIdJiNleNJdgUrJfbKArXua28QF7+0CoZjlLn0lp6vlc+dl5r2/X9GQiQRQQU4BzSa69w==" + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "license": "MIT", + "peer": true + }, + "node_modules/connect": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/connect/-/connect-3.7.0.tgz", + "integrity": "sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "finalhandler": "1.1.2", + "parseurl": "~1.3.3", + "utils-merge": "1.0.1" + }, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/connect/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", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/connect/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/constant-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/constant-case/-/constant-case-3.0.4.tgz", + "integrity": "sha512-I2hSBi7Vvs7BEuJDr5dDHfzb/Ruj3FyvFyh7KLilAjNQw3Be+xgqUBA2W6scVEcL0hL1dwPRtIqEPVUCKkSsyQ==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case": "^2.0.2" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "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==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "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==", + "license": "MIT", + "peer": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-color-keywords": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz", + "integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg==", + "license": "ISC", + "engines": { + "node": ">=4" + } + }, + "node_modules/css-to-react-native": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.2.0.tgz", + "integrity": "sha512-e8RKaLXMOFii+02mOlqwjbD00KSEKqblnpO9e++1aXS1fPQOpS1YoqdVHBqPjHNoxeF2mimzVqawm2KCbEdtHQ==", + "license": "MIT", + "dependencies": { + "camelize": "^1.0.0", + "css-color-keywords": "^1.0.0", + "postcss-value-parser": "^4.0.2" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/cssstyle": { + "version": "5.3.7", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.7.tgz", + "integrity": "sha512-7D2EPVltRrsTkhpQmksIu+LxeWAIEk6wRDMJ1qljlv+CKHJM+cJLlfhWIzNA44eAsHXSNe3+vO6DW1yCYx8SuQ==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^4.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "css-tree": "^3.1.0", + "lru-cache": "^11.2.4" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.1.tgz", + "integrity": "sha512-euIQENZg6x8mj3fO6o9+fOW8MimUI4PpD/fZBhJfeioZVy9TUpM4UY7KjQNVZFlqwJ0UdzRDzkycB997HEq1BQ==", + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^15.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/data-urls/node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/date-fns": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-4.4.0.tgz", + "integrity": "sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/date-fns-jalali": { + "version": "4.1.0-0", + "resolved": "https://registry.npmjs.org/date-fns-jalali/-/date-fns-jalali-4.1.0-0.tgz", + "integrity": "sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deepmerge": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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" + } + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "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", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/diff": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.4.tgz", + "integrity": "sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff3": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/diff3/-/diff3-0.0.3.tgz", + "integrity": "sha512-iSq8ngPOt0K53A6eVr4d5Kn6GNrM2nQZtC740pzIriHtn4pOQ2lyzEXQMBeVcWERN0ye7fhBsk9PbLLQOnUx/g==", + "dev": true, + "license": "MIT" + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/dom-serializer/node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "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/electron-to-chromium": { + "version": "1.5.371", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.371.tgz", + "integrity": "sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==", + "license": "ISC", + "peer": true + }, + "node_modules/emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha512-CwBLREIQ7LvYFB0WyRvwhq5N5qPhc6PMjD6bYggFlI5YyDgl+0vxq5VHbMOFqLg7hfWzmu8T5Z1QofhmTIhItA==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/equivalent-key-map": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/equivalent-key-map/-/equivalent-key-map-0.2.2.tgz", + "integrity": "sha512-xvHeyCDbZzkpN4VHQj/n+j2lOwL0VWszG30X4cOrc9Y7Tuo2qCdZK/0AMod23Z5dCtNUbaju6p0rwOhHUk05ew==", + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-stack-parser": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz", + "integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "stackframe": "^1.3.4" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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" + } + }, + "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/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "license": "BSD-2-Clause", + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "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" + } + }, + "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" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/express/node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/express/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/express/node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fast-average-color": { + "version": "9.5.2", + "resolved": "https://registry.npmjs.org/fast-average-color/-/fast-average-color-9.5.2.tgz", + "integrity": "sha512-FbaU8iPTPljP7tmnVhXbCyASNw/zxnmaNDf88gn5pTXlNvejl9w4FapeWMh6UNDwIjhJJU28EPfQWwW032YgPA==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "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-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.2.tgz", + "integrity": "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-builder": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.2.0.tgz", + "integrity": "sha512-00aAWieqff+ZJhsXA4g1g7M8k+7AYoMUUHF+/zFb5U6Uv/P0Vl4QZo84/IcufzYalLuEj9928bXN9PbbFzMF0Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "path-expression-matcher": "^1.5.0", + "xml-naming": "^0.1.0" + } + }, + "node_modules/fast-xml-parser": { + "version": "5.8.0", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.8.0.tgz", + "integrity": "sha512-6bIM7fsJxeo3uXv7OncQYsBAMPJ7V16Slahl/6M98C/i2q+vB1+4a0MtrvYwDFEUrwDSbAmeLDRXsOBwrL7yAg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "@nodable/entities": "^2.1.0", + "fast-xml-builder": "^1.2.0", + "path-expression-matcher": "^1.5.0", + "strnum": "^2.3.0", + "xml-naming": "^0.1.0" + }, + "bin": { + "fxparser": "src/cli/cli.js" + } + }, + "node_modules/fb-dotslash": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/fb-dotslash/-/fb-dotslash-0.5.8.tgz", + "integrity": "sha512-XHYLKk9J4BupDxi9bSEhkfss0m+Vr9ChTrjhf9l2iw3jB5C7BnY4GVPoMcqbrTutsKJso6yj2nAB6BI/F2oZaA==", + "license": "(MIT OR Apache-2.0)", + "peer": true, + "bin": { + "dotslash": "bin/dotslash" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/fb-watchman": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", + "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "bser": "2.1.1" + } + }, + "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==", + "license": "MIT", + "peer": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/finalhandler/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", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/finalhandler/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "node_modules/find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==", + "license": "MIT", + "dependencies": { + "locate-path": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/flow-enums-runtime": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/flow-enums-runtime/-/flow-enums-runtime-0.0.6.tgz", + "integrity": "sha512-3PYnM29RFXwvAN6Pc/scUfkI7RwhQ/xqyLUyPNlXUp9S40zI8nup9tUSrTLSVnWGBN38FNiGWbwZOB6uR4OGdw==", + "license": "MIT", + "peer": true + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/framer-motion": { + "version": "11.18.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz", + "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==", + "license": "MIT", + "dependencies": { + "motion-dom": "^11.18.1", + "motion-utils": "^11.18.1", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, + "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" + } + }, + "node_modules/fs-ext-extra-prebuilt": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/fs-ext-extra-prebuilt/-/fs-ext-extra-prebuilt-2.2.7.tgz", + "integrity": "sha512-Q7rayYRBDIvDF01HWOwSSjoaP+05N1g+o3BXL1Zf8Frw2JkjSmi4EtvCBITuW30l6hB2m2TW1pehdh8wyU/+gw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "nan": "^2.24.0" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "license": "ISC", + "peer": true + }, + "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": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "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" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6.9.0" + } + }, + "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.*" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/get-package-type": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", + "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gettext-parser": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/gettext-parser/-/gettext-parser-1.4.0.tgz", + "integrity": "sha512-sedZYLHlHeBop/gZ1jdg59hlUEcpcZJofLq2JFwJT1zTqAU3l2wFv6IsuwFHGqbiT9DWzMUW4/em2+hspnmMMA==", + "license": "MIT", + "dependencies": { + "encoding": "^0.1.12", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "license": "ISC", + "peer": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/gradient-parser": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/gradient-parser/-/gradient-parser-1.1.1.tgz", + "integrity": "sha512-Hu0YfNU+38EsTmnUfLXUKFMXq9yz7htGYpF4x+dlbBhUCvIvzLt0yVLT/gJRmvLKFJdqNFrz4eKkIUjIXSr7Tw==", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "license": "MIT", + "peer": true, + "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", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/header-case": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/header-case/-/header-case-2.0.4.tgz", + "integrity": "sha512-H/vuk5TEEVZwrR0lp2zed9OCo1uAILMlx0JEMgC26rzyJJ3N1v6XkwHHXJQdR2doSjcGPM6OKPYoJgf0plJ11Q==", + "license": "MIT", + "dependencies": { + "capital-case": "^1.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/hermes-compiler": { + "version": "250829098.0.9", + "resolved": "https://registry.npmjs.org/hermes-compiler/-/hermes-compiler-250829098.0.9.tgz", + "integrity": "sha512-hZ5O7PDz1vQ99TS7HD3FJ9zVynfU1y+VWId6U1Pldvd8hmAYrNec/XLPYJKD3dLOW6NXak6aAQAuMuSo3ji0tQ==", + "license": "MIT", + "peer": true + }, + "node_modules/hermes-estree": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.32.0.tgz", + "integrity": "sha512-KWn3BqnlDOl97Xe1Yviur6NbgIZ+IP+UVSpshlZWkq+EtoHg6/cwiDj/osP9PCEgFE15KBm1O55JRwbMEm5ejQ==", + "license": "MIT", + "peer": true + }, + "node_modules/hermes-parser": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.32.0.tgz", + "integrity": "sha512-g4nBOWFpuiTqjR3LZdRxKUkij9iyveWeuks7INEsMX741f3r9xxrOe8TeQfUxtda0eXmiIFiMQzoeSQEno33Hw==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.32.0" + } + }, + "node_modules/highlight-words-core": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/highlight-words-core/-/highlight-words-core-1.2.3.tgz", + "integrity": "sha512-m1O9HW3/GNHxzSIXWw1wCNXXsgLlxrP0OI6+ycGUhiUHkikqW3OrwVHz+lxeNBe5yqLESdIcj8PowHQ2zLvUvQ==", + "license": "MIT" + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/hpq": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/hpq/-/hpq-1.4.0.tgz", + "integrity": "sha512-ycJQMRaRPBcfnoT1gS5I1XCvbbw9KO94Y0vkwksuOjcJMqNZtb03MF2tCItLI2mQbkZWSSeFinoRDPmjzv4tKg==", + "license": "MIT" + }, + "node_modules/html-dom-parser": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.1.2.tgz", + "integrity": "sha512-9nD3Rj3/FuQt83AgIa1Y3ruzspwFFA54AJbQnohXN+K6fL1/bhcDQJJY5Ne4L4A163ADQFVESd/0TLyNoV0mfg==", + "license": "MIT", + "dependencies": { + "domhandler": "5.0.3", + "htmlparser2": "10.0.0" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/html-react-parser": { + "version": "5.2.11", + "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.2.11.tgz", + "integrity": "sha512-WnSQVn/D1UTj64nSz5y8MriL+MrbsZH80Ytr1oqKqs8DGZnphWY1R1pl3t7TY3rpqTSu+FHA21P80lrsmrdNBA==", + "license": "MIT", + "dependencies": { + "domhandler": "5.0.3", + "html-dom-parser": "5.1.2", + "react-property": "2.0.2", + "style-to-js": "1.1.21" + }, + "peerDependencies": { + "@types/react": "0.14 || 15 || 16 || 17 || 18 || 19", + "react": "0.14 || 15 || 16 || 17 || 18 || 19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/htmlparser2": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.0.0.tgz", + "integrity": "sha512-TwAZM+zE5Tq3lrEHvOlvwgj1XLWQCtaaibSN11Q+gGBAS7Y1uZSWwXXRe4iF6OXnaq1riyQAPFOBtYc77Mxq0g==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.1", + "entities": "^6.0.0" + } + }, + "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", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/http-errors/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" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/image-size": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.2.1.tgz", + "integrity": "sha512-rH+46sQJ2dlwfjfhCyNx5thzrv+dtmBIhPHk0zgRUukHzZ/kRueTJXoYYsclBaKcSMBWuGbOFXtioLpzTb5euw==", + "license": "MIT", + "peer": true, + "dependencies": { + "queue": "6.0.2" + }, + "bin": { + "image-size": "bin/image-size.js" + }, + "engines": { + "node": ">=16.x" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.8.19" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "license": "ISC", + "peer": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "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": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.2.tgz", + "integrity": "sha512-AMB1mvwR1pyBFY/nSevUX6y8nJWS63/SzUKD3JyQn97s4xgIdgQPT75IRouIiBAN4yLQBUShNYVW0+UG25daCw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, + "node_modules/invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "license": "MIT", + "peer": true, + "dependencies": { + "loose-envify": "^1.0.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "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==", + "license": "MIT", + "peer": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "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==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-plain-object": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz", + "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "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==", + "license": "MIT", + "peer": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC", + "peer": true + }, + "node_modules/isomorphic-git": { + "version": "1.37.6", + "resolved": "https://registry.npmjs.org/isomorphic-git/-/isomorphic-git-1.37.6.tgz", + "integrity": "sha512-qr1NFCPsVTZ6YGqTXw0CzamnsHyH9QQ1OTEfeXIweSljRUMzuHFCJdUn0wc6OcjtTDns6knxjPb7N6LmJeftOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-lock": "^1.4.1", + "clean-git-ref": "^2.0.1", + "crc-32": "^1.2.0", + "diff3": "0.0.3", + "ignore": "^5.1.4", + "minimisted": "^2.0.0", + "pako": "^1.0.10", + "pify": "^4.0.1", + "readable-stream": "^4.0.0", + "sha.js": "^2.4.12", + "simple-get": "^4.0.1" + }, + "bin": { + "isogit": "cli.cjs" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/isomorphic.js": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/isomorphic.js/-/isomorphic.js-0.2.5.tgz", + "integrity": "sha512-PIeMbHqMt4DnUP3MA/Flc0HElYjMXArsw1qwJZcm9sqR8mq3l8NYizFMty0pWwE/tzIGH3EKK5+jes5mAr85yw==", + "license": "MIT", + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", + "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "@babel/core": "^7.12.3", + "@babel/parser": "^7.14.7", + "@istanbuljs/schema": "^0.1.2", + "istanbul-lib-coverage": "^3.2.0", + "semver": "^6.3.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-instrument/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "peer": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" + } + }, + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", + "license": "MIT", + "peer": true, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", + "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/types": "^29.6.3", + "camelcase": "^6.2.0", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "leven": "^3.1.0", + "pretty-format": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-validate/node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/jest-worker": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", + "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@types/node": "*", + "jest-util": "^29.7.0", + "merge-stream": "^2.0.0", + "supports-color": "^8.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/jest-worker/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsc-safe-url": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/jsc-safe-url/-/jsc-safe-url-0.2.4.tgz", + "integrity": "sha512-0wM3YBWtYePOjfyXQH5MWQ8H7sdk5EXSwZvmSLKk2RboVQ2Bu239jycHDz5J/8Blf3K0Qnoy2b6xD+z10MFB+Q==", + "license": "0BSD", + "peer": true + }, + "node_modules/jsdom": { + "version": "27.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.0.1.tgz", + "integrity": "sha512-SNSQteBL1IlV2zqhwwolaG9CwhIhTvVHWg3kTss/cLE7H/X4644mtPQqYvCfsSrGQWt9hSZcgOXX8bOZaMN+kA==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/dom-selector": "^6.7.2", + "cssstyle": "^5.3.1", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "parse5": "^8.0.0", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, + "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/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "peer": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/lib0": { + "version": "0.2.99", + "resolved": "https://registry.npmjs.org/lib0/-/lib0-0.2.99.tgz", + "integrity": "sha512-vwztYuUf1uf/1zQxfzRfO5yzfNKhTtgOByCruuiQQxWQXnPb8Itaube5ylofcV0oM0aKal9Mv+S1s1Ky0UYP1w==", + "license": "MIT", + "dependencies": { + "isomorphic.js": "^0.2.4" + }, + "bin": { + "0ecdsa-generate-keypair": "bin/0ecdsa-generate-keypair.js", + "0gentesthtml": "bin/gentesthtml.js", + "0serve": "bin/0serve.js" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + }, + "node_modules/lighthouse-logger": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/lighthouse-logger/-/lighthouse-logger-1.4.2.tgz", + "integrity": "sha512-gPWxznF6TKmUHrOQjlVo2UbaL2EJ71mb2CCeRs/2qBpi4L/g4LUVc9+3lKQ6DTUZwJswfM7ainGrLO1+fOqa2g==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "debug": "^2.6.9", + "marky": "^1.2.2" + } + }, + "node_modules/lighthouse-logger/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", + "peer": true, + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/lighthouse-logger/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT", + "peer": true + }, + "node_modules/line-height": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/line-height/-/line-height-0.3.1.tgz", + "integrity": "sha512-YExecgqPwnp5gplD2+Y8e8A5+jKpr25+DzMbFdI1/1UAr0FJrTFv4VkHLf8/6B590i1wUPJWMKKldkd/bdQ//w==", + "license": "MIT", + "dependencies": { + "computed-style": "~0.1.3" + }, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==", + "license": "MIT", + "dependencies": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.throttle/-/lodash.throttle-4.1.1.tgz", + "integrity": "sha512-wIkUCfVKpVsWo3JSZlc+8MB5it+2AN5W8J7YVMST30UrvcQNZ1Okbj+rbVniijTWE6FGYy4XJq/rHkas8qJMLQ==", + "license": "MIT", + "peer": true + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/lru-cache": { + "version": "11.5.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz", + "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==", + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/makeerror": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", + "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", + "license": "BSD-3-Clause", + "peer": true, + "dependencies": { + "tmpl": "1.0.5" + } + }, + "node_modules/marky": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/marky/-/marky-1.3.0.tgz", + "integrity": "sha512-ocnPZQLNpvbedwTy9kNrQEsknEfgvcLMvOtz3sFeWApDq1MXH1TqkCIx58xlpESsfwQOnuBO9beyQuNGzVvuhQ==", + "license": "Apache-2.0", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "license": "CC0-1.0" + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/memize": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/memize/-/memize-2.1.1.tgz", + "integrity": "sha512-8Nl+i9S5D6KXnruM03Jgjb+LwSupvR13WBr4hJegaaEyobvowCVupi79y2WSiWvO1mzBWxPwEYE5feCe8vyA5w==", + "license": "MIT" + }, + "node_modules/memoize-one": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-5.2.1.tgz", + "integrity": "sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==", + "license": "MIT", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "license": "MIT", + "peer": true + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/metro": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro/-/metro-0.83.7.tgz", + "integrity": "sha512-SPaPEyvTsTmd0LpT7RaZciQyDw2i/JB7+iY9L5VfBo72+psescFxBqpI1TL9dnL+pmnfkU+l/J1mEEGLeF65EQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "accepts": "^2.0.0", + "ci-info": "^2.0.0", + "connect": "^3.6.5", + "debug": "^4.4.0", + "error-stack-parser": "^2.0.6", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "hermes-parser": "0.35.0", + "image-size": "^1.0.2", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "jsc-safe-url": "^0.2.2", + "lodash.throttle": "^4.1.1", + "metro-babel-transformer": "0.83.7", + "metro-cache": "0.83.7", + "metro-cache-key": "0.83.7", + "metro-config": "0.83.7", + "metro-core": "0.83.7", + "metro-file-map": "0.83.7", + "metro-resolver": "0.83.7", + "metro-runtime": "0.83.7", + "metro-source-map": "0.83.7", + "metro-symbolicate": "0.83.7", + "metro-transform-plugins": "0.83.7", + "metro-transform-worker": "0.83.7", + "mime-types": "^3.0.1", + "nullthrows": "^1.1.1", + "serialize-error": "^2.1.0", + "source-map": "^0.5.6", + "throat": "^5.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "metro": "src/cli.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-babel-transformer": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-babel-transformer/-/metro-babel-transformer-0.83.7.tgz", + "integrity": "sha512-sBqBkt6kNut/88bv+Ucvm4yqdPetbvAEsHzi3MAgJEifOSYYzX5Z5Kgw3TFOrwf/mHJTOBG2ONlaMHoyfP15TA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "flow-enums-runtime": "^0.0.6", + "hermes-parser": "0.35.0", + "metro-cache-key": "0.83.7", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-babel-transformer/node_modules/hermes-estree": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", + "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro-babel-transformer/node_modules/hermes-parser": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", + "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.35.0" + } + }, + "node_modules/metro-cache": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-cache/-/metro-cache-0.83.7.tgz", + "integrity": "sha512-E9SRePXQ1Zvlj79VcOk57q7VC7rMHMFQ+jhmPHBiq+dJ0bJB5BL87lWZF6oh5X76Cci5tpDuQNaDwwuSCToEeg==", + "license": "MIT", + "peer": true, + "dependencies": { + "exponential-backoff": "^3.1.1", + "flow-enums-runtime": "^0.0.6", + "https-proxy-agent": "^7.0.5", + "metro-core": "0.83.7" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-cache-key": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-cache-key/-/metro-cache-key-0.83.7.tgz", + "integrity": "sha512-W1c2Nmx8MiJTJt+eWhMO08z9VKi3kZOaz99IYGdqeqDgY9j+yZjXl62rUav4Di0heZfh4/n2s722PqRL1OODeg==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-config": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-config/-/metro-config-0.83.7.tgz", + "integrity": "sha512-83mjWFbFOt2GeJ6pFIum5mSnc1uTsZJAtD8o4ej0s4NVsYsA7fB+pHvTfHhFrpeMONaobu2riKavkPei05Er/Q==", + "license": "MIT", + "peer": true, + "dependencies": { + "connect": "^3.6.5", + "flow-enums-runtime": "^0.0.6", + "jest-validate": "^29.7.0", + "metro": "0.83.7", + "metro-cache": "0.83.7", + "metro-core": "0.83.7", + "metro-runtime": "0.83.7", + "yaml": "^2.6.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-config/node_modules/yaml": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", + "license": "ISC", + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/metro-core": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-core/-/metro-core-0.83.7.tgz", + "integrity": "sha512-6yn3w1wnltT6RQl7p7YES2l95ArC+mWrOssEiH8p5/DDrJS65/szf9LsC9JrBv8c5DdvSY3V3f0GRYg0Ox7hCg==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "lodash.throttle": "^4.1.1", + "metro-resolver": "0.83.7" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-file-map": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-file-map/-/metro-file-map-0.83.7.tgz", + "integrity": "sha512-+j0F1m+FQYVAQ6syf+mwhIPV5GoFQrkInX8bppuc50IzNsZbMrp8R5H/Sx/K2daQ3YEa9F/XwkeZT8gzJfgeCw==", + "license": "MIT", + "peer": true, + "dependencies": { + "debug": "^4.4.0", + "fb-watchman": "^2.0.0", + "flow-enums-runtime": "^0.0.6", + "graceful-fs": "^4.2.4", + "invariant": "^2.2.4", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "nullthrows": "^1.1.1", + "walker": "^1.0.7" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-minify-terser": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-minify-terser/-/metro-minify-terser-0.83.7.tgz", + "integrity": "sha512-MfJar2IS4tBRuLb9svwb0Gu5l9BsH+pcRm8eGcEi/wy8MzZinfinh5dFLt2nWkocnulIgtGB5NkFDdbXqMXKhQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "terser": "^5.15.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-resolver": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-resolver/-/metro-resolver-0.83.7.tgz", + "integrity": "sha512-WSJIENlMcoSsuz66IfBHOkgfp3KJt2UW2TnEHPf1b8pIG2eEXNOVmo2+03A0H17WY2XGXWgxL0CG7FAopqgB1A==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-runtime": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-runtime/-/metro-runtime-0.83.7.tgz", + "integrity": "sha512-9GKkJURaB2iyYoEExKnedzAHzxmKtSi+k0tsZUvMoU27tBZJElchYt7JH/Ai/XzYAI9lCAaV7u5HZSI8J5Z+wQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.25.0", + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-source-map": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-source-map/-/metro-source-map-0.83.7.tgz", + "integrity": "sha512-JgA1h7oc1a1jydBe1GhVFsUoMYo3wLPk7oRA32rjlDsq+sP2JLt9x2p2lWbNSxTm/u8NV4VRid3hvEJgcX8tKw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-symbolicate": "0.83.7", + "nullthrows": "^1.1.1", + "ob1": "0.83.7", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-symbolicate": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-symbolicate/-/metro-symbolicate-0.83.7.tgz", + "integrity": "sha512-g4suyxw20WOHWI680c+Kq4wC/NF+Hx5pRH9afrMp+sMTxqLeKcPR1Xf4wMhsjlbvx7LbIREdke6q928jEjvJWw==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6", + "invariant": "^2.2.4", + "metro-source-map": "0.83.7", + "nullthrows": "^1.1.1", + "source-map": "^0.5.6", + "vlq": "^1.0.0" + }, + "bin": { + "metro-symbolicate": "src/index.js" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-plugins": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-transform-plugins/-/metro-transform-plugins-0.83.7.tgz", + "integrity": "sha512-Ss0FpBiZDjX2kwhukMDl5sNdYK8T/06IPqxNE4H6PTlRlfs9q11cef13c/xESY/Pm4VCkp1yJUZO3kXzvMxQFA==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro-transform-worker": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/metro-transform-worker/-/metro-transform-worker-0.83.7.tgz", + "integrity": "sha512-UegCo7ygB2fT64mRK2nbAjQVJ1zSwIIHy8d96jJv2nKZFDaViYBiughEdu5HM/Ceq0WN3LZrZk3zhl9aoiLYFw==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/core": "^7.25.2", + "@babel/generator": "^7.29.1", + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "flow-enums-runtime": "^0.0.6", + "metro": "0.83.7", + "metro-babel-transformer": "0.83.7", + "metro-cache": "0.83.7", + "metro-cache-key": "0.83.7", + "metro-minify-terser": "0.83.7", + "metro-source-map": "0.83.7", + "metro-transform-plugins": "0.83.7", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/metro/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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/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", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/metro/node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/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", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/metro/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", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/metro/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", + "peer": true + }, + "node_modules/metro/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", + "peer": true + }, + "node_modules/metro/node_modules/hermes-estree": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.35.0.tgz", + "integrity": "sha512-xVx5Opwy8Oo1I5yGpVRhCvWL/iV3M+ylksSKVNlxxD90cpDpR/AR1jLYqK8HWihm065a6UI3HeyAmYzwS8NOOg==", + "license": "MIT", + "peer": true + }, + "node_modules/metro/node_modules/hermes-parser": { + "version": "0.35.0", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.35.0.tgz", + "integrity": "sha512-9JLjeHxBx8T4CAsydZR49PNZUaix+WpQJwu9p2010lu+7Kwl6D/7wYFFJxoz+aXkaaClp9Zfg6W6/zVlSJORaA==", + "license": "MIT", + "peer": true, + "dependencies": { + "hermes-estree": "0.35.0" + } + }, + "node_modules/metro/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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/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", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/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", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/metro/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", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/metro/node_modules/ws": { + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", + "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/metro/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", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/metro/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", + "peer": true, + "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/metro/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", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "peer": true, + "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", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "peer": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minimisted": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/minimisted/-/minimisted-2.0.1.tgz", + "integrity": "sha512-1oPjfuLQa2caorJUM8HV8lGgWCc0qqAO1MNv/k05G4qslmsndV/5WdNZrqCiyqiz3wohia2Ij2B7w2Dr7/IyrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.5" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "license": "MIT", + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.48", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.48.tgz", + "integrity": "sha512-f22b8LV1gbTO2ms2j2z13MuPogNoh5UzxL3nzNAYKGraILnbGc9NEE6dyiiiLv46DGRb8A4kg8UKWLjPthxBHw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, + "node_modules/motion-dom": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz", + "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==", + "license": "MIT", + "dependencies": { + "motion-utils": "^11.18.1" + } + }, + "node_modules/motion-utils": { + "version": "11.18.1", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz", + "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==", + "license": "MIT" + }, + "node_modules/mousetrap": { + "version": "1.6.5", + "resolved": "https://registry.npmjs.org/mousetrap/-/mousetrap-1.6.5.tgz", + "integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==", + "license": "Apache-2.0 WITH LLVM-exception" + }, + "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", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.27.0.tgz", + "integrity": "sha512-hC+0LidcL3XE4rp1C4H54KujgXKzbfyTngZTwBByQxsOxCEKZT0MPQ4hOKUH2jU1OYstqdDH4onyHPDzcV0XdQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-int64": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", + "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", + "license": "MIT", + "peer": true + }, + "node_modules/node-releases": { + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-wheel": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/normalize-wheel/-/normalize-wheel-1.0.1.tgz", + "integrity": "sha512-1OnlAPZ3zgrk8B91HyRj+eVv+kS5u+Z0SCsak6Xil/kmgEia50ga7zfkumayonZrImffAxPU/5WcyGhzetHNPA==", + "license": "BSD-3-Clause" + }, + "node_modules/nullthrows": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/nullthrows/-/nullthrows-1.1.1.tgz", + "integrity": "sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==", + "license": "MIT", + "peer": true + }, + "node_modules/ob1": { + "version": "0.83.7", + "resolved": "https://registry.npmjs.org/ob1/-/ob1-0.83.7.tgz", + "integrity": "sha512-9M5kpuOLyTPogMtZiQUIxdAZxl7Dxs6tVBbJErSumsqGMuhVSoUbkfeZ3XNPpLpwBBtqY5QDUzGwggLHX3slQg==", + "license": "MIT", + "peer": true, + "dependencies": { + "flow-enums-runtime": "^0.0.6" + }, + "engines": { + "node": ">=20.19.4" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/octokit": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/octokit/-/octokit-3.1.2.tgz", + "integrity": "sha512-MG5qmrTL5y8KYwFgE1A4JWmgfQBaIETE/lOlfwNYx1QOtCQHGVxkRJmdUJltFc1HVn73d61TlMhMyNTOtMl+ng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@octokit/app": "^14.0.2", + "@octokit/core": "^5.0.0", + "@octokit/oauth-app": "^6.0.0", + "@octokit/plugin-paginate-graphql": "^4.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0", + "@octokit/plugin-retry": "^6.0.0", + "@octokit/plugin-throttling": "^8.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", + "license": "MIT", + "peer": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.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" + } + }, + "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==", + "license": "MIT", + "peer": true, + "dependencies": { + "is-docker": "^2.0.0", + "is-wsl": "^2.1.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "license": "MIT", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==", + "license": "MIT", + "dependencies": { + "p-limit": "^2.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/p-try": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", + "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse5": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.1.tgz", + "integrity": "sha512-z1e/HMG90obSGeidlli3hj7cbocou0/wa5HacvI3ASx34PecNjNQeaHNo5WIZpWofN9kgkqV1q5YvXe3F0FoPw==", + "license": "MIT", + "dependencies": { + "entities": "^8.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-8.0.0.tgz", + "integrity": "sha512-zwfzJecQ/Uej6tusMqwAqU/6KL2XaB2VZ2Jg54Je6ahNBGNH6Ek6g3jjNCF0fG9EWQKGZNddNjU5F1ZQn/sBnA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/parsel-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/parsel-js/-/parsel-js-1.2.2.tgz", + "integrity": "sha512-AVJMlwQ4bL2Y0VvYJGk+Fp7eX4SCH2uFoNApmn4yKWACUewZ+alwW3tyoe1r5Z3aLYQTuAuPZIyGghMfO/Tlxw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/LeaVerou" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/leaverou" + } + ], + "license": "MIT" + }, + "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" + } + }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/path-case/-/path-case-3.0.4.tgz", + "integrity": "sha512-qO4qCFjXqVTrcbPt/hQfhTQ+VhFsqNKOPtytgNKkKxSoEp3XPUQ8ObFuePylOIok5gjn69ry8XiULxCwot3Wfg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-expression-matcher": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.5.0.tgz", + "integrity": "sha512-cbrerZV+6rvdQrrD+iGMcZFEiiSrbv9Tfdkvnusy6y0x0GKBXREFg/Y65GhIfm0tnLntThhzCnfKwp1WRjeCyQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.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==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "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==", + "license": "MIT", + "peer": true, + "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", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 6" + } + }, + "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" + }, + "bin": { + "pixelmatch": "bin/pixelmatch" + } + }, + "node_modules/playwright": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz", + "integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==", + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.60.0" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "fsevents": "2.3.2" + } + }, + "node_modules/playwright-core": { + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz", + "integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==", + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=18" + } + }, + "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" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-prefix-selector": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/postcss-prefix-selector/-/postcss-prefix-selector-1.16.1.tgz", + "integrity": "sha512-Umxu+FvKMwlY6TyDzGFoSUnzW+NOfMBLyC1tAkIjgX+Z/qGspJeRjVC903D7mx7TuBpJlwti2ibXtWuA7fKMeQ==", + "license": "MIT", + "peerDependencies": { + "postcss": ">4 <9" + } + }, + "node_modules/postcss-urlrebase": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/postcss-urlrebase/-/postcss-urlrebase-1.4.0.tgz", + "integrity": "sha512-rRaxMmWvXrn8Rk1PqsxmaJwldRHsr0WbbASKKCZYxXwotHkM/5X/6IrwaEe8pdzpbNGCEY86yhYMN0MhgOkADA==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.2.0" + }, + "peerDependencies": { + "postcss": "^8.3.0" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/preact": { + "version": "10.29.2", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.29.2.tgz", + "integrity": "sha512-7tNmwg/7mzzAoB/8kSg6Hl37JraAZw3Z3A0JSY7VXlZwo82Xn0G7wKbNNs2qoF4ZEEsQGTwDAroNdqKs1ofJxQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, + "node_modules/pretty-format": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", + "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/schemas": "^29.6.3", + "ansi-styles": "^5.0.0", + "react-is": "^18.0.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "license": "MIT", + "peer": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", + "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", + "license": "MIT", + "peer": true, + "dependencies": { + "inherits": "~2.0.3" + } + }, + "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/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/raw-body/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/re-resizable": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/re-resizable/-/re-resizable-6.11.2.tgz", + "integrity": "sha512-2xI2P3OHs5qw7K0Ud1aLILK6MQxW50TcO+DetD9eIV58j84TqYeHoZcL9H4GXFXXIh7afhH8mv5iUCXII7OW7A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.13.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-autosize-textarea": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/react-autosize-textarea/-/react-autosize-textarea-7.1.0.tgz", + "integrity": "sha512-BHpjCDkuOlllZn3nLazY2F8oYO1tS2jHnWhcjTWQdcKiiMU6gHLNt/fzmqMSyerR0eTdKtfSIqtSeTtghNwS+g==", + "license": "MIT", + "dependencies": { + "autosize": "^4.0.2", + "line-height": "^0.3.1", + "prop-types": "^15.5.6" + }, + "peerDependencies": { + "react": "^0.14.0 || ^15.0.0 || ^16.0.0", + "react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0" + } + }, + "node_modules/react-colorful": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/react-colorful/-/react-colorful-5.7.0.tgz", + "integrity": "sha512-fuesYIemttah97XmsIHmz4OORDHiSFzyc9HMAIrCHJou2jaRQmL8cFJ76K4zQhhj8jzwOBlOi4BaGTjjOZCfTg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/react-day-picker": { + "version": "9.14.0", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-9.14.0.tgz", + "integrity": "sha512-tBaoDWjPwe0M5pGrum4H0SR6Lyk+BO9oHnp9JbKpGKW2mlraNPgP9BMfsg5pWpwrssARmeqk7YBl2oXutZTaHA==", + "license": "MIT", + "dependencies": { + "@date-fns/tz": "^1.4.1", + "@tabby_ai/hijri-converter": "1.0.5", + "date-fns": "^4.1.0", + "date-fns-jalali": "4.1.0-0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/react-devtools-core": { + "version": "6.1.5", + "resolved": "https://registry.npmjs.org/react-devtools-core/-/react-devtools-core-6.1.5.tgz", + "integrity": "sha512-ePrwPfxAnB+7hgnEr8vpKxL9cmnp7F322t8oqcPshbIQQhDKgFDW4tjhF2wjVbdXF9O/nyuy3sQWd9JGpiLPvA==", + "license": "MIT", + "peer": true, + "dependencies": { + "shell-quote": "^1.6.1", + "ws": "^7" + } + }, + "node_modules/react-devtools-core/node_modules/ws": { + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", + "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-easy-crop": { + "version": "5.5.7", + "resolved": "https://registry.npmjs.org/react-easy-crop/-/react-easy-crop-5.5.7.tgz", + "integrity": "sha512-kYo4NtMeXFQB7h1U+h5yhUkE46WQbQdq7if54uDlbMdZHdRgNehfvaFrXnFw5NR1PNoUOJIfTwLnWmEx/MaZnA==", + "license": "MIT", + "dependencies": { + "normalize-wheel": "^1.0.1", + "tslib": "^2.0.1" + }, + "peerDependencies": { + "react": ">=16.4.0", + "react-dom": ">=16.4.0" + } + }, + "node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "license": "MIT" + }, + "node_modules/react-native": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/react-native/-/react-native-0.84.1.tgz", + "integrity": "sha512-0PjxOyXRu3tZ8EobabxSukvhKje2HJbsZikR0U+pvS0pYZza2hXKjcSBiBdFN4h9D0S3v6a8kkrDK6WTRKMwzg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@jest/create-cache-key-function": "^29.7.0", + "@react-native/assets-registry": "0.84.1", + "@react-native/codegen": "0.84.1", + "@react-native/community-cli-plugin": "0.84.1", + "@react-native/gradle-plugin": "0.84.1", + "@react-native/js-polyfills": "0.84.1", + "@react-native/normalize-colors": "0.84.1", + "@react-native/virtualized-lists": "0.84.1", + "abort-controller": "^3.0.0", + "anser": "^1.4.9", + "ansi-regex": "^5.0.0", + "babel-jest": "^29.7.0", + "babel-plugin-syntax-hermes-parser": "0.32.0", + "base64-js": "^1.5.1", + "commander": "^12.0.0", + "flow-enums-runtime": "^0.0.6", + "hermes-compiler": "250829098.0.9", + "invariant": "^2.2.4", + "jest-environment-node": "^29.7.0", + "memoize-one": "^5.0.0", + "metro-runtime": "^0.83.3", + "metro-source-map": "^0.83.3", + "nullthrows": "^1.1.1", + "pretty-format": "^29.7.0", + "promise": "^8.3.0", + "react-devtools-core": "^6.1.5", + "react-refresh": "^0.14.0", + "regenerator-runtime": "^0.13.2", + "scheduler": "0.27.0", + "semver": "^7.1.3", + "stacktrace-parser": "^0.1.10", + "tinyglobby": "^0.2.15", + "whatwg-fetch": "^3.0.0", + "ws": "^7.5.10", + "yargs": "^17.6.2" + }, + "bin": { + "react-native": "cli.js" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.1.1", + "react": "^19.2.3" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native/node_modules/@react-native/virtualized-lists": { + "version": "0.84.1", + "resolved": "https://registry.npmjs.org/@react-native/virtualized-lists/-/virtualized-lists-0.84.1.tgz", + "integrity": "sha512-sJoDunzhci8ZsqxlUiKoLut4xQeQcmbIgvDHGQKeBz6uEq9HgU+hCWOijMRr6sLP0slQVfBAza34Rq7IbXZZOA==", + "license": "MIT", + "peer": true, + "dependencies": { + "invariant": "^2.2.4", + "nullthrows": "^1.1.1" + }, + "engines": { + "node": ">= 20.19.4" + }, + "peerDependencies": { + "@types/react": "^19.2.0", + "react": "*", + "react-native": "*" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-native/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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native/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", + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/react-native/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", + "peer": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/react-native/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", + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/react-native/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", + "peer": true + }, + "node_modules/react-native/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", + "peer": true + }, + "node_modules/react-native/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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native/node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT", + "peer": true + }, + "node_modules/react-native/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", + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native/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", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/react-native/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", + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/react-native/node_modules/ws": { + "version": "7.5.11", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.11.tgz", + "integrity": "sha512-zS54Oen9bITtp7kp2XM3AydrCIq1D+HwJOuH+c+e4LfpL/lotP5osijd+UoMnxwAam1GN8R4KtLAyIrIcBNpiA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/react-native/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", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/react-native/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", + "peer": true, + "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/react-native/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", + "peer": true, + "engines": { + "node": ">=12" + } + }, + "node_modules/react-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz", + "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.14.2", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", + "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/redux": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", + "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==", + "license": "MIT" + }, + "node_modules/regenerator-runtime": { + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", + "license": "MIT", + "peer": true + }, + "node_modules/rememo": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/rememo/-/rememo-4.0.2.tgz", + "integrity": "sha512-NVfSP9NstE3QPNs/TnegQY0vnJnstKQSpcrsI2kBTB3dB2PkdfKdTa+abbjMIDqpc63fE5LfjLgfMst0ULMFxQ==", + "license": "MIT" + }, + "node_modules/remove-accents": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/remove-accents/-/remove-accents-0.5.0.tgz", + "integrity": "sha512-8g3/Otx1eJaVD12e31UbJj1YzdtVvzH85HV7t+9MJYk/u3XmkOUJ5Ys9wQrf9PCPK8+xn4ymzqYCiZl6QWKn+A==", + "license": "MIT" + }, + "node_modules/requestidlecallback": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/requestidlecallback/-/requestidlecallback-0.3.0.tgz", + "integrity": "sha512-TWHFkT7S9p7IxLC5A1hYmAYQx2Eb9w1skrXmQ+dS1URyvR8tenMLl4lHbqEOUnpEYxNKpkVMXUgknVpBZWXXfQ==", + "license": "MIT" + }, + "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" + } + }, + "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" + } + }, + "node_modules/require-main-filename": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz", + "integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==", + "license": "ISC" + }, + "node_modules/reselect": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz", + "integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==", + "license": "MIT" + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "license": "ISC", + "peer": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "license": "MIT" + }, + "node_modules/rungen": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/rungen/-/rungen-0.3.2.tgz", + "integrity": "sha512-zWl10xu2D7zoR8zSC2U6bg5bYF6T/Wk7rxwp8IPaJH7f0Ge21G03kNHVgHR7tyVkSSfAOG0Rqf/Cl38JftSmtw==", + "license": "MIT" + }, + "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", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "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==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/semver": { + "version": "7.8.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.4.tgz", + "integrity": "sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/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" + } + }, + "node_modules/send/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/send/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" + } + }, + "node_modules/send/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" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/send/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" + } + }, + "node_modules/sentence-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/sentence-case/-/sentence-case-3.0.4.tgz", + "integrity": "sha512-8LS0JInaQMCRoQ7YUytAo/xUu5W2XnQxV2HI/6uM6U7CITS1RqPElr30V6uIqyMKM9lJGRVFy5/4CuzcixNYSg==", + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3", + "upper-case-first": "^2.0.2" + } + }, + "node_modules/serialize-error": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-2.1.0.tgz", + "integrity": "sha512-ghgmKt5o4Tly5yEG/UJp8qTd0AN7Xalw4XBtDEKP655B699qMEtra1WlXeE6WIvdEG481JvRxULKsInq/iNysw==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "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", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/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" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "license": "ISC" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "dev": true, + "license": "(MIT AND BSD-3-Clause)", + "dependencies": { + "inherits": "^2.0.4", + "safe-buffer": "^5.2.1", + "to-buffer": "^1.2.0" + }, + "bin": { + "sha.js": "bin.js" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "license": "MIT", + "peer": true, + "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==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.4.tgz", + "integrity": "sha512-VsC6n6vz1ihYYyZZwX7YZSF5l5x36ca17OC+a69h94YqB7X6XLwf+5MOgynYir2SLFUbl8gIYvBo8K8RoNQ6bQ==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/showdown": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/showdown/-/showdown-1.9.1.tgz", + "integrity": "sha512-9cGuS382HcvExtf5AHk7Cb4pAeQQ+h0eTr33V1mu+crYWV4KvWAw6el92bDrqGEk5d46Ai/fhbEUwqJ/mTCNEA==", + "license": "BSD-3-Clause", + "dependencies": { + "yargs": "^14.2" + }, + "bin": { + "showdown": "bin/showdown.js" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "license": "ISC", + "peer": true + }, + "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==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "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==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-html-tokenizer": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/simple-html-tokenizer/-/simple-html-tokenizer-0.5.11.tgz", + "integrity": "sha512-C2WEK/Z3HoSFbYq8tI7ni3eOo/NneSPRoPpcM7WdLjFOArFuyXEjAoCdOC3DgMfRyziZQ1hCNR4mrNdWEvD0og==", + "license": "MIT" + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/snake-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/snake-case/-/snake-case-3.0.4.tgz", + "integrity": "sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==", + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "license": "BSD-3-Clause", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "license": "BSD-3-Clause", + "peer": true + }, + "node_modules/stack-utils": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", + "license": "MIT", + "peer": true, + "dependencies": { + "escape-string-regexp": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/stack-utils/node_modules/escape-string-regexp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", + "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/stackframe": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz", + "integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==", + "license": "MIT", + "peer": true + }, + "node_modules/stacktrace-parser": { + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.11.tgz", + "integrity": "sha512-WjlahMgHmCJpqzU8bIBy4qtsZdU9lRlcZE3Lvyej6t4tuOuv1vk57OW3MBrj6hXBFx/nNoC9MPMTcr5YA7NQbg==", + "license": "MIT", + "peer": true, + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha512-vafcv6KjVZKSgz06oM/H6GDBrAtz8vdhQakGjFIvNrHA6y3HCF1CInLy+QLq8dTJPQ1b+KDUqDFctkdRW44e1w==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/strnum": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.4.0.tgz", + "integrity": "sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "dependencies": { + "anynum": "^1.0.0" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "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==", + "license": "MIT", + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" + }, + "node_modules/tabbable": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz", + "integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==", + "license": "MIT" + }, + "node_modules/tannin": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/tannin/-/tannin-1.2.0.tgz", + "integrity": "sha512-U7GgX/RcSeUETbV7gYgoz8PD7Ni4y95pgIP/Z6ayI3CfhSujwKEBlGFTCRN+Aqnuyf4AN2yHL+L8x+TCGjb9uA==", + "license": "MIT", + "dependencies": { + "@tannin/plural-forms": "^1.1.0" + } + }, + "node_modules/temml": { + "version": "0.10.34", + "resolved": "https://registry.npmjs.org/temml/-/temml-0.10.34.tgz", + "integrity": "sha512-f3b5CaPwPvMviA+CtHy0qoIGWvzpRrNpXmGRc/Y1jc9gAYy+xOlndJFyn7Vfcz7cBcS8QRvv8z0EEH59sHCQxg==", + "license": "MIT", + "engines": { + "node": ">=18.13.0" + } + }, + "node_modules/terser": { + "version": "5.48.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.48.0.tgz", + "integrity": "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q==", + "license": "BSD-2-Clause", + "peer": true, + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "license": "MIT", + "peer": true + }, + "node_modules/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "license": "ISC", + "peer": true, + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/throat": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/throat/-/throat-5.0.0.tgz", + "integrity": "sha512-fcwX4mndzpLQKBS1DVYhGAcYaYt7vsHNIvQV+WXMvnow5cgjPphq5CaayLaGsjRdSCKZFNGt7/GYAuXaNOiYCA==", + "license": "MIT", + "peer": true + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "peer": true, + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tldts": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.2.tgz", + "integrity": "sha512-kCwffuaH8ntKtygnWe1b4BJKWiCUH30n5KfoTr6IchcXOwR7chAOFJxFrH3vjANafUYrIA4a7SDL+nn7SiR4Sw==", + "license": "MIT", + "dependencies": { + "tldts-core": "^7.4.2" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.2.tgz", + "integrity": "sha512-nwEyF4vl4RSJjwSjBUmOSxc3BFPoIFdlRthJ6e+5v9P3bHNsoD06UjuqMUspqp7vsEZ1beaHi1km+optiE17yA==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/tmpl": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", + "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", + "license": "BSD-3-Clause", + "peer": true + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "isarray": "^2.0.5", + "safe-buffer": "^5.2.1", + "typed-array-buffer": "^1.0.3" + }, + "engines": { + "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==", + "license": "MIT", + "peer": true, + "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", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "license": "(MIT OR CC0-1.0)", + "peer": true, + "engines": { + "node": ">=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==", + "dev": true, + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "license": "MIT" + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/jsonwebtoken": "^9.0.0", + "jsonwebtoken": "^9.0.2" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "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" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/upper-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case/-/upper-case-2.0.2.tgz", + "integrity": "sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/upper-case-first": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/upper-case-first/-/upper-case-first-2.0.2.tgz", + "integrity": "sha512-514ppYHBaKwfJRK/pNC6c/OxfGa0obSnAl106u97Ed0I625Nin96KAjttZF6ZL3e1XLtphxnqrOi9iWgm+u+bg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-memo-one": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", + "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "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" + } + }, + "node_modules/uuid": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-14.0.0.tgz", + "integrity": "sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist-node/bin/uuid" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vlq": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-1.0.1.tgz", + "integrity": "sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==", + "license": "MIT", + "peer": true + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/walker": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", + "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "makeerror": "1.0.12" + } + }, + "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==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wasm-vips": { + "version": "0.0.16", + "resolved": "https://registry.npmjs.org/wasm-vips/-/wasm-vips-0.0.16.tgz", + "integrity": "sha512-4/bEq8noAFt7DX3VT+Vt5AgNtnnOLwvmrDbduWfiv9AV+VYkbUU4f9Dam9e6khRqPinyClFHCqiwATTTJEiGwA==", + "license": "MIT", + "engines": { + "node": ">=16.4.0" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-fetch": { + "version": "3.6.20", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.6.20.tgz", + "integrity": "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg==", + "license": "MIT", + "peer": true + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "license": "MIT", + "dependencies": { + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-module": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz", + "integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==", + "license": "ISC" + }, + "node_modules/which-typed-array": { + "version": "1.1.22", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.22.tgz", + "integrity": "sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.9", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/wrap-ansi": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-5.1.0.tgz", + "integrity": "sha512-QC1/iN/2/RPVJ5jYK8BGttj5z83LmSKmvbvrXPNCLZSEb32KKVDJDl/MOt2N01qU2H/FkzEa9PKto1BqDjtd7Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.0", + "string-width": "^3.0.0", + "strip-ansi": "^5.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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/write-file-atomic": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", + "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "license": "ISC", + "peer": true, + "dependencies": { + "imurmurhash": "^0.1.4", + "signal-exit": "^3.0.7" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/ws": { + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xml-naming": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/xml-naming/-/xml-naming-0.1.0.tgz", + "integrity": "sha512-k8KO9hrMyNk6tUWqUfkTEZbezRRpONVOzUTnc97VnCvyj6Tf9lyUR9EDAIeiVLv56jsMcoXEwjW8Kv5yPY52lw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], + "license": "MIT", + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/xml2js": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, + "node_modules/y-protocols": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/y-protocols/-/y-protocols-1.0.7.tgz", + "integrity": "sha512-YSVsLoXxO67J6eE/nV4AtFtT3QEotZf5sK5BHxFBXso7VDUT3Tx07IfA6hsu5Q5OmBdMkQVmFZ9QOA7fikWvnw==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.85" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + }, + "peerDependencies": { + "yjs": "^13.0.0" + } + }, + "node_modules/y18n": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz", + "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==", + "license": "ISC" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC", + "peer": true + }, + "node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "node_modules/yargs": { + "version": "14.2.3", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-14.2.3.tgz", + "integrity": "sha512-ZbotRWhF+lkjijC/VhmOT9wSgyBQ7+zr13+YLkhfsSiTriYsMzkTUFP18pFhWwBeMa5gUc1MzbhrO6/VB7c9Xg==", + "license": "MIT", + "dependencies": { + "cliui": "^5.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^2.0.1", + "require-directory": "^2.1.1", + "require-main-filename": "^2.0.0", + "set-blocking": "^2.0.0", + "string-width": "^3.0.0", + "which-module": "^2.0.0", + "y18n": "^4.0.0", + "yargs-parser": "^15.0.1" + } + }, + "node_modules/yargs-parser": { + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-15.0.3.tgz", + "integrity": "sha512-/MVEVjTXy/cGAjdtQf8dW3V9b97bPN7rNn8ETj6BmAQL7ibC7O1Q9SPJbGjgh3SlwoBNXMzj/ZGIj8mBgl12YA==", + "license": "ISC", + "dependencies": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "node_modules/yjs": { + "version": "13.6.29", + "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.29.tgz", + "integrity": "sha512-kHqDPdltoXH+X4w1lVmMtddE3Oeqq48nM40FD5ojTd8xYhQpzIDcfE2keMSU5bAgRPJBe225WTUdyUgj1DtbiQ==", + "license": "MIT", + "dependencies": { + "lib0": "^0.2.99" + }, + "engines": { + "node": ">=16.0.0", + "npm": ">=8.0.0" + }, + "funding": { + "type": "GitHub Sponsors ❤", + "url": "https://github.com/sponsors/dmonad" + } + } + } +} diff --git a/apps/cli/ai/html-to-blocks-engine/package.json b/apps/cli/ai/html-to-blocks-engine/package.json new file mode 100644 index 0000000000..9b044d97b9 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/package.json @@ -0,0 +1,27 @@ +{ + "name": "html-to-wordpress-blocks", + "version": "0.1.0", + "private": true, + "type": "module", + "description": "Codex/Claude plugin tools for transforming designed HTML into editable WordPress blocks.", + "bin": { + "html-to-blocks-mcp": "./tools/mcp-server.mjs" + }, + "scripts": { + "check": "node --check tools/mcp-server.mjs && node --check tools/lib/workspace.mjs && node --check tools/lib/capture.mjs && node --check tools/lib/wp-serialize.mjs && node --check tools/content/model.mjs", + "test": "node --test 'tools/theme/**/*.test.mjs' 'tools/content/**/*.test.mjs'" + }, + "dependencies": { + "@wordpress/block-library": "^9.48.0", + "@wordpress/blocks": "^15.21.0", + "@wordpress/element": "^8.0.0", + "ajv": "^8.20.0", + "jsdom": "^27.0.1", + "pixelmatch": "^7.1.0", + "playwright": "^1.54.0", + "pngjs": "^7.0.0" + }, + "devDependencies": { + "@wp-playground/cli": "^3.1.38" + } +} diff --git a/apps/cli/ai/html-to-blocks-engine/run-tool.mjs b/apps/cli/ai/html-to-blocks-engine/run-tool.mjs new file mode 100644 index 0000000000..2a5cf69bae --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/run-tool.mjs @@ -0,0 +1,36 @@ +// Thin CLI shim so the Studio agent (which cannot consume MCP servers) can +// invoke the html-to-blocks engine as a child process. Usage: +// node run-tool.mjs +// Reads args from the given file (argv, not stdin, so the importing of +// mcp-server.mjs — which attaches a dormant stdin listener — does not race +// with our input). Prints a single JSON line to stdout: {ok, result|error}. +import fs from 'node:fs'; +import { handlers } from './mcp-server.mjs'; + +const toolName = process.argv[2]; +const argsPath = process.argv[3]; + +function emit( payload, code = 0 ) { + // Write then exit in the flush callback so stdout is fully delivered + // before the process tears down (process.exit can truncate otherwise). + process.stdout.write( JSON.stringify( payload ), () => process.exit( code ) ); +} + +( async () => { + try { + if ( ! toolName || ! handlers[ toolName ] ) { + return emit( { ok: false, error: `Unknown engine tool: ${ toolName }` } ); + } + const args = + argsPath && fs.existsSync( argsPath ) + ? JSON.parse( fs.readFileSync( argsPath, 'utf8' ) ) + : {}; + const result = await handlers[ toolName ]( args ); + emit( { ok: true, result } ); + } catch ( error ) { + emit( { + ok: false, + error: error instanceof Error ? error.stack || error.message : String( error ), + } ); + } +} )(); diff --git a/apps/cli/ai/html-to-blocks-engine/theme/evidence.mjs b/apps/cli/ai/html-to-blocks-engine/theme/evidence.mjs new file mode 100644 index 0000000000..637e8684e1 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/evidence.mjs @@ -0,0 +1,174 @@ +// tools/theme/evidence.mjs +import path from 'node:path'; +import fs from 'node:fs'; +import { readJson, readIfExists, writeJson, resolvePath, findFiles } from '../lib/workspace.mjs'; + +export function parseCss(css) { + const out = []; + const src = css.replace(/\/\*[\s\S]*?\*\//g, ''); + let i = 0; + walk(null); + return out; + + function walk(media) { + while (i < src.length) { + while (i < src.length && /\s/.test(src[i])) i += 1; + if (i >= src.length) return; + if (src[i] === '}') { // closing of @media (stray "}" at top level is skipped) + i += 1; + if (media !== null) return; + continue; + } + const open = src.indexOf('{', i); + if (open === -1) { i = src.length; return; } + const semi = src.indexOf(';', i); + if (semi !== -1 && semi < open) { // blockless at-statement: @import, @charset, @layer a, b; + i = semi + 1; + continue; + } + const head = src.slice(i, open).trim(); + if (head.startsWith('@media')) { + i = open + 1; + walk(head.replace(/^@media/, '').trim()); + continue; + } + if (head.startsWith('@')) { // @keyframes, @font-face, @supports: skip block (keyframes nest) + i = skipBlock(open); + if (head.startsWith('@keyframes')) out.push({ selector: head, media, declarations: [], atRule: 'keyframes' }); + continue; + } + const close = src.indexOf('}', open); + const body = src.slice(open + 1, close); + out.push({ + selector: head, media, + declarations: body.split(';').map((d) => d.trim()).filter(Boolean) + .map((d) => { const k = d.indexOf(':'); return [d.slice(0, k).trim(), d.slice(k + 1).trim()]; }), + }); + i = close + 1; + } + } + function skipBlock(open) { + let depth = 1, j = open + 1; + while (j < src.length && depth > 0) { if (src[j] === '{') depth += 1; if (src[j] === '}') depth -= 1; j += 1; } + return j; + } +} + +const BUCKETS = [ + ['pseudo', (r) => /::|:before|:after/.test(r.selector)], + ['media-query', (r) => r.media !== null], + ['interaction', (r) => /:hover|:focus|:active|:checked/.test(r.selector) || r.declarations.some(([p]) => p === 'transition' || p === 'animation' || p === 'animation-play-state') || r.atRule === 'keyframes'], + ['position', (r) => r.declarations.some(([p, v]) => p === 'position' && /fixed|absolute|sticky/.test(v))], + ['blend', (r) => r.declarations.some(([p]) => p === 'mix-blend-mode' || p === 'filter' || p === 'backdrop-filter')], + ['grid', (r) => r.declarations.some(([p, v]) => (p === 'display' && /grid/.test(v)) || p.startsWith('grid-'))], +]; +export function classifyRule(rule) { + return BUCKETS.filter(([, fn]) => fn(rule)).map(([name]) => name); +} + +export function loadPageTrees(workspaceRoot) { + const pagesDir = path.join(workspaceRoot, 'wordpress/pages'); + if (fs.existsSync(pagesDir)) { + return fs.readdirSync(pagesDir).filter((f) => f.endsWith('.block-tree.json')).sort() + .map((f) => ({ page: f.replace(/\.block-tree\.json$/, ''), tree: readJson(path.join(pagesDir, f)) })); + } + return [{ page: 'index', tree: readJson(path.join(workspaceRoot, 'wordpress/block-tree.json')) }]; +} + +const COLOR_RE = /#[0-9a-fA-F]{3,8}\b|rgba?\([^)]*\)/g; + +export function analyzeThemeEvidence(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const pages = loadPageTrees(workspaceRoot); + const acc = { colors: new Map(), fontSizes: new Map(), spacing: new Map(), fontFamilies: new Map() }; + const supportUsage = {}; + + // 1. tree scan + for (const { page, tree } of pages) { + walkBlocks(tree.blocks, [], (block, blockPath) => { + const style = block.attrs?.style || {}; + for (const p of stylePaths(style)) { + const key = `${p.path}`; + supportUsage[block.blockName] ??= {}; + supportUsage[block.blockName][key] = (supportUsage[block.blockName][key] || 0) + 1; + const ref = { kind: 'attr', page, blockName: block.blockName, path: `${blockPath.join('.')}:${p.path}` }; + if (p.path.startsWith('color.')) record(acc.colors, String(p.value).toLowerCase(), ref); + else if (p.path.startsWith('typography.fontSize')) record(acc.fontSizes, p.value, ref); + else if (p.path.startsWith('spacing.')) record(acc.spacing, p.value, ref); + } + }); + } + + // 2. css scan + const cssFiles = [path.join(workspaceRoot, 'wordpress/style.css'), + ...findFiles(path.join(workspaceRoot, 'wordpress/blocks'), 'style.css')]; + const customProperties = {}; + const cssRules = []; + for (const file of cssFiles) { + const css = readIfExists(file); + if (!css) continue; + const rel = path.relative(workspaceRoot, file); + for (const rule of parseCss(css)) { + cssRules.push({ file: rel, selector: rule.selector, media: rule.media, buckets: classifyRule(rule), declarationCount: rule.declarations.length }); + for (const [prop, value] of rule.declarations) { + const ref = { kind: 'css', file: rel, selector: rule.selector, prop }; + if (prop.startsWith('--')) customProperties[prop] = { value, definedIn: rel, refs: [] }; + for (const m of value.match(COLOR_RE) || []) record(acc.colors, m.toLowerCase(), ref); + if (prop === 'font-family') record(acc.fontFamilies, value, ref); + if (prop === 'font-size') record(acc.fontSizes, value, ref); + if (/^(padding|margin|gap|row-gap|column-gap)/.test(prop)) record(acc.spacing, value, ref); + for (const m of value.match(/var\((--[a-z0-9-]+)/gi) || []) { + const name = m.slice(4); + (customProperties[name] ??= { value: null, definedIn: null, refs: [] }).refs.push(ref); + } + } + } + } + + // 3. name colors after custom properties that hold them + const report = { + generatedAt: new Date().toISOString(), + pages: pages.map((p) => p.page), + customProperties, + colors: finalize(acc.colors, (entry) => ({ + names: Object.entries(customProperties).filter(([, v]) => (v.value || '').toLowerCase() === entry.value).map(([k]) => k), + })), + fontFamilies: finalize(acc.fontFamilies), + fontSizes: finalize(acc.fontSizes), + spacing: finalize(acc.spacing), + supportUsage, + cssRules, + summary: { + liftableRules: cssRules.filter((r) => r.buckets.length === 0).length, + unliftableRules: cssRules.filter((r) => r.buckets.length > 0).length, + }, + }; + if (args.write !== false) writeJson(path.join(workspaceRoot, 'reports/theme-evidence.json'), report); + return report; +} + +function record(map, value, ref) { + const v = String(value).trim(); + const entry = map.get(v) || { value: v, count: 0, attrRefs: [], cssRefs: [] }; + entry.count += 1; + (ref.kind === 'attr' ? entry.attrRefs : entry.cssRefs).push(ref); + map.set(v, entry); +} +function finalize(map, extra = () => ({})) { + return [...map.values()].sort((a, b) => b.count - a.count).map((e) => ({ ...e, ...extra(e) })); +} +export function walkBlocks(blocks, blockPath, fn) { + (blocks || []).forEach((block, index) => { + const p = [...blockPath, index]; + fn(block, p); + walkBlocks(block.innerBlocks, p, fn); + }); +} +export function stylePaths(style, prefix = '', out = []) { + for (const [key, value] of Object.entries(style || {})) { + const p = prefix ? `${prefix}.${key}` : key; + if (value && typeof value === 'object') stylePaths(value, p, out); + else out.push({ path: p, value }); + } + return out; +} diff --git a/apps/cli/ai/html-to-blocks-engine/theme/fonts.mjs b/apps/cli/ai/html-to-blocks-engine/theme/fonts.mjs new file mode 100644 index 0000000000..50f8cf4124 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/fonts.mjs @@ -0,0 +1,63 @@ +// tools/theme/fonts.mjs +import fs from 'node:fs'; +import path from 'node:path'; +import { slug } from '../lib/workspace.mjs'; + +const WOFF2_UA = 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0 Safari/537.36'; + +export function extractGoogleFontsImport(css) { + const m = css.match(/@import\s+url\(\s*['"]?(https:\/\/fonts\.googleapis\.com\/css2[^'")]+)/); + return m ? m[1] : null; +} + +export function parseFontFaces(css2) { + const faces = []; + for (const block of css2.match(/@font-face\s*{[^}]*}/g) || []) { + const get = (re) => (block.match(re) || [])[1] || null; + faces.push({ + fontFamily: get(/font-family:\s*'([^']+)'/), + fontStyle: get(/font-style:\s*([a-z]+)/) || 'normal', + fontWeight: get(/font-weight:\s*([0-9 ]+)/) || '400', + unicodeRange: get(/unicode-range:\s*([^;]+);/), + url: get(/src:\s*url\(([^)]+\.woff2)\)/), + }); + } + return faces.filter((f) => f.fontFamily && f.url); +} + +export async function fetchThemeFonts({ importUrl, sourceCss, targetDir, fetchImpl = fetch }) { + const url = importUrl || extractGoogleFontsImport(sourceCss || ''); + if (!url) throw new Error('No Google Fonts @import found; pass importUrl explicitly or skip font bundling.'); + let css2; + try { + const res = await fetchImpl(url, { headers: { 'User-Agent': WOFF2_UA } }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + css2 = await res.text(); + } catch (error) { + throw new Error(`Font fetch failed (offline?). Fidelity requires bundled fonts; the run is blocked. Cause: ${error.message}`); + } + const faces = parseFontFaces(css2); + fs.mkdirSync(targetDir, { recursive: true }); + const families = new Map(); + const counters = new Map(); + for (const face of faces) { + const famSlug = slug(face.fontFamily); + const base = `${famSlug}-${face.fontWeight.replace(/\s+/g, '-')}-${face.fontStyle}`; + const n = counters.get(base) || 0; + counters.set(base, n + 1); + const fileName = `${base}-${n}.woff2`; + const res = await fetchImpl(face.url, { headers: { 'User-Agent': WOFF2_UA } }); + if (!res.ok) throw new Error(`Font file fetch failed: ${face.url} (HTTP ${res.status})`); + fs.writeFileSync(path.join(targetDir, fileName), Buffer.from(await res.arrayBuffer())); + const fam = families.get(face.fontFamily) || { name: face.fontFamily, slug: famSlug, fontFamily: `'${face.fontFamily}'`, fontFace: [] }; + fam.fontFace.push({ + fontFamily: face.fontFamily, + fontStyle: face.fontStyle, + fontWeight: face.fontWeight, + ...(face.unicodeRange ? { unicodeRange: face.unicodeRange } : {}), + src: [`file:./assets/fonts/${fileName}`], + }); + families.set(face.fontFamily, fam); + } + return { importUrl: url, fontFamilies: [...families.values()] }; +} diff --git a/apps/cli/ai/html-to-blocks-engine/theme/generate/blocks-plugin.mjs b/apps/cli/ai/html-to-blocks-engine/theme/generate/blocks-plugin.mjs new file mode 100644 index 0000000000..de422426e3 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/generate/blocks-plugin.mjs @@ -0,0 +1,65 @@ +// tools/theme/generate/blocks-plugin.mjs +import fs from 'node:fs'; +import path from 'node:path'; +import { readJson, writeFile, writeJson } from '../../lib/workspace.mjs'; + +export function writeBlocksPlugin({ workspaceRoot, slug, themeName, outDir, transformCss = (css) => css }) { + const srcRoot = path.join(workspaceRoot, 'wordpress/blocks'); + const blockDirs = fs.existsSync(srcRoot) + ? fs.readdirSync(srcRoot).filter((d) => fs.existsSync(path.join(srcRoot, d, 'block.json'))).sort() + : []; + if (blockDirs.length === 0) return { blocks: [] }; + + const blocks = []; + for (const dir of blockDirs) { + const dest = path.join(outDir, 'blocks', dir); + fs.mkdirSync(dest, { recursive: true }); + const blockJson = readJson(path.join(srcRoot, dir, 'block.json')); + delete blockJson.editorScript; // enqueued below with explicit wp-* deps (no asset.php in no-build blocks) + writeJson(path.join(dest, 'block.json'), blockJson); + const indexSrc = path.join(srcRoot, dir, 'index.js'); + if (fs.existsSync(indexSrc)) fs.copyFileSync(indexSrc, path.join(dest, 'index.js')); + const cssSrc = path.join(srcRoot, dir, 'style.css'); + if (fs.existsSync(cssSrc)) { + // block CSS references the run's design tokens; the theme moves + // those to --wp--custom--*, so the same rename must apply here + writeFile(path.join(dest, 'style.css'), transformCss(fs.readFileSync(cssSrc, 'utf8'))); + } + blocks.push(blockJson.name); + } + + const php = ` page), + }); + for (const page of pages) { + writeFile(path.join(outDir, `content/${page.slug}.html`), `${page.markup.trim()}\n`); + } + writeFile(path.join(outDir, `${slug}-content.php`), contentPluginPhp({ slug, themeName, prefix })); + return { prefix, pageCount: pages.length }; +} + +function contentPluginPhp({ slug, themeName, prefix }) { + return ` array()); +} + +function ${prefix}_import_pages() { + $state = get_option(${prefix.toUpperCase()}_OPTION, array()); + $results = array(); + foreach (${prefix}_manifest()['pages'] as $page) { + $slug = $page['slug']; + if (isset($state[$slug]) && get_post($state[$slug]['post_id'])) { + $results[$slug] = array('status' => 'already-imported', 'permalink' => get_permalink($state[$slug]['post_id'])); + continue; + } + $existing = get_page_by_path($slug); + if ($existing && !get_post_meta($existing->ID, ${prefix.toUpperCase()}_META, true)) { + $results[$slug] = array('status' => 'slug-collision', 'permalink' => null); + continue; + } + $markup = file_get_contents(__DIR__ . '/content/' . $slug . '.html'); + $markup = str_replace('{{THEME_URI}}', get_stylesheet_directory_uri(), $markup); + // wp_insert_post unslashes its input; without wp_slash the JSON + // escapes in block comments (& etc.) lose their backslash and + // the editor sees corrupted attribute values + $post_id = wp_insert_post(array( + 'post_type' => 'page', + 'post_status' => 'publish', + 'post_title' => wp_slash($page['title']), + 'post_name' => $slug, + 'post_content' => wp_slash($markup), + )); + if (is_wp_error($post_id)) { + $results[$slug] = array('status' => 'error: ' . $post_id->get_error_message(), 'permalink' => null); + continue; + } + update_post_meta($post_id, ${prefix.toUpperCase()}_META, '1'); + if (!empty($page['template'])) { + update_post_meta($post_id, '_wp_page_template', $page['template']); + } + if (!empty($page['front'])) { + update_option('show_on_front', 'page'); + update_option('page_on_front', $post_id); + } + $state[$slug] = array('post_id' => $post_id, 'imported_at' => time()); + $results[$slug] = array('status' => 'imported', 'permalink' => get_permalink($post_id)); + } + update_option(${prefix.toUpperCase()}_OPTION, $state); + return $results; +} + +function ${prefix}_remove_pages() { + $state = get_option(${prefix.toUpperCase()}_OPTION, array()); + foreach ($state as $slug => $entry) { + $post = get_post($entry['post_id']); + if ($post && get_post_meta($post->ID, ${prefix.toUpperCase()}_META, true)) { + if ((int) get_option('page_on_front') === $post->ID) { + update_option('show_on_front', 'posts'); + update_option('page_on_front', 0); + } + wp_delete_post($post->ID, true); + } + unset($state[$slug]); + } + update_option(${prefix.toUpperCase()}_OPTION, $state); +} + +function ${prefix}_page_status($page, $state) { + if (!isset($state[$page['slug']])) return 'not imported'; + $entry = $state[$page['slug']]; + $post = get_post($entry['post_id']); + if (!$post) return 'not imported'; + if (strtotime($post->post_modified_gmt) > (int) $entry['imported_at'] + 5) return 'modified since import'; + return 'imported'; +} + +add_action('admin_menu', function () { + add_management_page( + '${themeName} content', '${themeName} content', 'manage_options', '${slug}-content', + function () { + if (!current_user_can('manage_options')) return; + if (isset($_POST['${prefix}_action']) && wp_verify_nonce($_POST['_wpnonce'] ?? '', '${prefix}')) { + if ($_POST['${prefix}_action'] === 'import') ${prefix}_import_pages(); + if ($_POST['${prefix}_action'] === 'remove') ${prefix}_remove_pages(); + } + $state = get_option(${prefix.toUpperCase()}_OPTION, array()); + echo '

    ${themeName} content

    '; + foreach (${prefix}_manifest()['pages'] as $page) { + echo ''; + } + echo '
    PageSlugStatus
    ' . esc_html($page['title']) . ($page['front'] ? ' (front page)' : '') . '' + . esc_html($page['slug']) . '' . esc_html(${prefix}_page_status($page, $state)) . '
    '; + wp_nonce_field('${prefix}'); + echo ' '; + echo ''; + echo '
    '; + } + ); +}); +`; +} diff --git a/apps/cli/ai/html-to-blocks-engine/theme/generate/theme-files.mjs b/apps/cli/ai/html-to-blocks-engine/theme/generate/theme-files.mjs new file mode 100644 index 0000000000..4ff8b70d28 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/generate/theme-files.mjs @@ -0,0 +1,145 @@ +// tools/theme/generate/theme-files.mjs +export function styleCss({ name, slug, description = '' }, customCss = '') { + return `/* +Theme Name: ${name} +Description: ${description} +Version: 1.0.0 +Requires at least: 6.6 +Requires PHP: 7.4 +License: GPL-2.0-or-later +License URI: https://www.gnu.org/licenses/gpl-2.0.html +Text Domain: ${slug} +*/ + +${customCss.trim()} +`; +} + +export function functionsPhp({ slug, customBlocks = [] }) { + const fn = slug.replace(/-/g, '_'); + const notice = customBlocks.length === 0 ? '' : ` +add_action('admin_notices', function () { + if (WP_Block_Type_Registry::get_instance()->is_registered('${customBlocks[0]}')) { + return; + } + echo '

    The active theme needs its companion blocks plugin (registers ${customBlocks.join(', ')}). Custom blocks will not render until it is activated.

    '; +}); +`; + return `get('Version')); +}); + +// Templates and parts ship {{THEME_URI}} placeholders for bundled assets; +// resolve them to the absolute theme URL when blocks render. +add_filter('render_block', function ($content) { + return str_replace('{{THEME_URI}}', get_stylesheet_directory_uri(), $content); +}); + +add_action('after_setup_theme', function () { + add_editor_style('style.css'); +}); + +// The source design's typography is authored verbatim (straight quotes, +// spaced dashes); texturizing would shift glyphs in display-size headings. +add_filter('run_wptexturize', '__return_false'); +${notice}`; +} + +export function buildThemeJson({ settings = {}, styles = {}, fontFamilies = [], templateParts = [], customTemplates = [] }) { + const merged = { + $schema: 'https://schemas.wp.org/trunk/theme.json', + version: 3, + settings: { + appearanceTools: true, + ...settings, + typography: { ...(settings.typography || {}), fontFamilies }, + }, + styles, + templateParts, + customTemplates, + }; + if (merged.templateParts.length === 0) delete merged.templateParts; + if (merged.customTemplates.length === 0) delete merged.customTemplates; + return merged; +} + +export function templateMarkup(entries) { + return entries.map((entry) => { + if (entry.type === 'part') { + const attrs = { slug: entry.slug, ...(entry.tagName ? { tagName: entry.tagName } : {}) }; + return ``; + } + if (entry.type === 'post-content') return ''; + if (entry.type === 'raw') return entry.markup.trim(); + if (entry.type === 'blocks') return entry.markup.trim(); + if (entry.type === 'tree') throw new Error('tree entries must be serialized by the scaffold before templateMarkup'); + throw new Error(`Unknown template entry type: ${entry.type}`); + }).join('\n') + '\n'; +} + +// Generic-situation defaults (spec: standing template set). Bodies are +// data-only block TREES — the scaffold serializes them through +// @wordpress/blocks so the emitted markup is canonical by construction +// (hand-written markup here is the one place save() drift could enter). +// Chrome entries get prepended by the scaffold. +const defaultShell = (innerBlocks) => ({ + blockName: 'core/group', + attrs: { + tagName: 'main', + layout: { type: 'constrained' }, + style: { spacing: { padding: { top: '6rem', bottom: '6rem' } } }, + }, + innerBlocks, +}); + +export const DEFAULT_TEMPLATES = { + archive: [{ + type: 'tree', + blocks: [defaultShell([ + { blockName: 'core/query-title', attrs: { type: 'archive' }, innerBlocks: [] }, + { + blockName: 'core/query', + attrs: { query: { perPage: 10, postType: 'post', inherit: true } }, + innerBlocks: [ + { + blockName: 'core/post-template', + attrs: {}, + innerBlocks: [ + { blockName: 'core/post-title', attrs: { isLink: true }, innerBlocks: [] }, + { blockName: 'core/post-date', attrs: {}, innerBlocks: [] }, + { blockName: 'core/post-excerpt', attrs: {}, innerBlocks: [] }, + ], + }, + { + blockName: 'core/query-pagination', + attrs: {}, + innerBlocks: [ + { blockName: 'core/query-pagination-previous', attrs: {}, innerBlocks: [] }, + { blockName: 'core/query-pagination-numbers', attrs: {}, innerBlocks: [] }, + { blockName: 'core/query-pagination-next', attrs: {}, innerBlocks: [] }, + ], + }, + ], + }, + ])], + }], + single: [{ + type: 'tree', + blocks: [defaultShell([ + { blockName: 'core/post-title', attrs: {}, innerBlocks: [] }, + { blockName: 'core/post-date', attrs: {}, innerBlocks: [] }, + { blockName: 'core/post-content', attrs: { layout: { type: 'default' } }, innerBlocks: [] }, + ])], + }], + 404: [{ + type: 'tree', + blocks: [defaultShell([ + { blockName: 'core/heading', attrs: { level: 1, content: 'Page not found' }, innerBlocks: [] }, + { blockName: 'core/paragraph', attrs: { content: 'The page you are looking for does not exist.' }, innerBlocks: [] }, + { blockName: 'core/search', attrs: { label: 'Search', showLabel: false, buttonText: 'Search' }, innerBlocks: [] }, + ])], + }], +}; diff --git a/apps/cli/ai/html-to-blocks-engine/theme/parts.mjs b/apps/cli/ai/html-to-blocks-engine/theme/parts.mjs new file mode 100644 index 0000000000..8f1e495f02 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/parts.mjs @@ -0,0 +1,92 @@ +// tools/theme/parts.mjs +import path from 'node:path'; +import crypto from 'node:crypto'; +import { writeJson, resolvePath } from '../lib/workspace.mjs'; +import { loadPageTrees } from './evidence.mjs'; + +const CONTENT_KEYS = new Set(['content', 'text', 'caption', 'label', 'alt', 'okText', 'noteText', 'submitText', 'brand', 'items', 'links', 'fields', 'url', 'href', 'anchor']); + +function sha(value) { return crypto.createHash('sha1').update(JSON.stringify(value)).digest('hex'); } + +function exactShape(block) { + return [block.blockName, sortedEntries(block.attrs || {}), (block.innerBlocks || []).map(exactShape)]; +} +function structuralShape(block) { + const attrs = block.attrs || {}; + return [ + block.blockName, + String(attrs.className || '').split(/\s+/).filter(Boolean).sort(), + Object.keys(attrs).filter((k) => !CONTENT_KEYS.has(k)).sort(), + (block.innerBlocks || []).map(structuralShape), + ]; +} +function sortedEntries(obj) { + return Object.keys(obj).sort().map((k) => [k, obj[k] && typeof obj[k] === 'object' ? sortedEntries(obj[k]) : obj[k]]); +} +export function exactHash(block) { return sha(exactShape(block)); } +export function structuralHash(block) { return sha(structuralShape(block)); } + +export function diffSubtrees(occurrences) { + // walk all occurrence subtrees in lockstep; report paths where exact values differ + const variance = []; + walk(occurrences.map((o) => o.block), ''); + return variance; + + function walk(nodes, prefix) { + const attrsList = nodes.map((n) => n.attrs || {}); + const keys = new Set(attrsList.flatMap((a) => Object.keys(a))); + for (const key of keys) { + const values = attrsList.map((a) => JSON.stringify(a[key])); + if (new Set(values).size > 1) { + variance.push({ + path: prefix ? `${prefix}:attrs.${key}` : `attrs.${key}`, + values: Object.fromEntries(occurrences.map((o, i) => [o.page, attrsList[i][key]])), + }); + } + } + const childCount = Math.max(...nodes.map((n) => (n.innerBlocks || []).length)); + for (let c = 0; c < childCount; c += 1) { + walk(nodes.map((n) => (n.innerBlocks || [])[c] || { attrs: {}, innerBlocks: [] }), prefix ? `${prefix}.${c}` : String(c)); + } + } +} + +export function inferTemplateParts(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const pages = loadPageTrees(workspaceRoot); + const byStructure = new Map(); + for (const { page, tree } of pages) { + tree.blocks.forEach((block, index) => { + const key = structuralHash(block); + const entry = byStructure.get(key) || []; + entry.push({ + page, index, block, + first: index === 0, last: index === tree.blocks.length - 1, + tagName: block.attrs?.tagName || null, blockName: block.blockName, + className: block.attrs?.className || '', + exact: exactHash(block), + }); + byStructure.set(key, entry); + }); + } + const groups = []; + const singletons = []; + for (const [structural, occurrences] of byStructure) { + const pagesIn = new Set(occurrences.map((o) => o.page)); + if (pagesIn.size < 2) { + singletons.push(...occurrences.map(({ block, exact, ...rest }) => rest)); + continue; + } + const exactSet = new Set(occurrences.map((o) => o.exact)); + groups.push({ + structuralHash: structural, + kind: exactSet.size === 1 ? 'exact' : 'structural', + occurrences: occurrences.map(({ block, exact, ...rest }) => rest), + variance: exactSet.size === 1 ? [] : diffSubtrees(occurrences), + }); + } + groups.sort((a, b) => b.occurrences.length - a.occurrences.length); + const report = { generatedAt: new Date().toISOString(), pages: pages.map((p) => p.page), groups, singletons }; + if (args.write !== false) writeJson(path.join(workspaceRoot, 'reports/template-parts.json'), report); + return report; +} diff --git a/apps/cli/ai/html-to-blocks-engine/theme/playground.mjs b/apps/cli/ai/html-to-blocks-engine/theme/playground.mjs new file mode 100644 index 0000000000..8b2cca9a45 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/playground.mjs @@ -0,0 +1,230 @@ +// tools/theme/playground.mjs +import fs from 'node:fs'; +import path from 'node:path'; +import { spawn } from 'node:child_process'; +import { performance } from 'node:perf_hooks'; +import { resolvePath, readJson, writeJson } from '../lib/workspace.mjs'; +import { loadCaptureDeps, serveDirectory, captureUrl, comparePngs, DEFAULT_VIEWPORTS, launchBrowser } from '../lib/capture.mjs'; +import { PLUGIN_ROOT } from '../lib/workspace.mjs'; +import * as profile from '../lib/profile.mjs'; + +// Above this many ms, the server-ready wait almost certainly included a cold +// WordPress build download (first run); below it the Playground CLI hit its +// cache. Used to infer and tag the run's cold/warm state for the profiler. +const COLD_SERVER_WAIT_MS = 20000; + +export function buildBlueprint({ slug, hasBlocksPlugin, contentModel }) { + const prefix = slug.replace(/-/g, '_') + '_content'; + return { + landingPage: '/', + steps: [ + // The content-model plugin registers the CPTs/taxonomies the hydrated + // query loops iterate, and seeds them so those loops render real + // entries — without it a hydrated archive/grid renders empty. + ...(contentModel ? [{ step: 'activatePlugin', pluginPath: `${contentModel.slug}/${contentModel.slug}.php` }] : []), + ...(hasBlocksPlugin ? [{ step: 'activatePlugin', pluginPath: `${slug}-blocks/${slug}-blocks.php` }] : []), + { step: 'activatePlugin', pluginPath: `${slug}-content/${slug}-content.php` }, + { step: 'activateTheme', themeFolderName: slug }, + // wp_set_current_user(1): without unfiltered_html, kses strips the + // form/select/input markup from imported page content — a real + // admin clicking the Import button has that capability + ...(contentModel ? [{ step: 'runPHP', code: ` `--mount=${dir}:/wordpress/wp-content/plugins/${path.basename(dir)}`), + ]; +} + +export function pageUrl(base, page) { + return page.front ? `${base}/` : `${base}/?pagename=${page.slug}`; +} + +// Detect a content-model plugin produced by the content-modeling skill so its +// CPTs/taxonomies/seeds are available to hydrated query loops. Returns the +// mount dir, plugin slug, and PHP function prefix, or null when absent. +export function resolveContentModelPlugin(workspaceRoot) { + const manifestPath = path.join(workspaceRoot, 'content-model/plugin-manifest.json'); + if (!fs.existsSync(manifestPath)) return null; + const manifest = readJson(manifestPath); + const dir = path.join(workspaceRoot, manifest.pluginRoot); + if (!fs.existsSync(dir)) return null; + const pluginSlug = manifest.plugin.slug; + return { dir, slug: pluginSlug, prefix: pluginSlug.replace(/-/g, '_') }; +} + +export async function playgroundRender(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const slug = args.slug; + const themeDir = path.join(workspaceRoot, 'theme', slug); + const blocksDir = path.join(workspaceRoot, 'theme-plugin', `${slug}-blocks`); + const contentDir = path.join(workspaceRoot, 'theme-plugin', `${slug}-content`); + const manifest = readJson(path.join(contentDir, 'content/manifest.json')); + const hasBlocksPlugin = fs.existsSync(blocksDir); + const contentModel = resolveContentModelPlugin(workspaceRoot); + const port = args.port || 9400; + const base = `http://127.0.0.1:${port}`; + const outDir = path.join(workspaceRoot, 'reports/playground'); + fs.mkdirSync(outDir, { recursive: true }); + + const blueprintPath = path.join(outDir, 'blueprint.json'); + writeJson(blueprintPath, buildBlueprint({ slug, hasBlocksPlugin, contentModel })); + profile.setRunMeta({ tool: 'playground_render', slug }); + const proc = profile.span('playground.cli.spawn', () => spawn('npx', ['@wp-playground/cli', ...buildCliArgs({ + slug, themeDir, blueprintPath, port, + pluginDirs: [blocksDir, contentDir, contentModel && contentModel.dir].filter((d) => d && fs.existsSync(d)), + })], { cwd: PLUGIN_ROOT, stdio: ['ignore', 'pipe', 'pipe'] })); + let logs = ''; + proc.stdout.on('data', (d) => { logs += d; }); + proc.stderr.on('data', (d) => { logs += d; }); + + try { + // The server-ready wait subsumes the cold WordPress build download on a + // first run; timing it (and whether it crossed the cold threshold or the + // CLI logged a download) is how we infer and tag cold vs warm. + const serverWaitTok = profile.mark('playground.wait.server'); + const serverWaitStart = performance.now(); + await waitForServer(base, 120000, () => proc.exitCode); + const serverWaitMs = performance.now() - serverWaitStart; + const cold = serverWaitMs > COLD_SERVER_WAIT_MS || /\b(download|fetch)ing wordpress/i.test(logs); + profile.measure(serverWaitTok, { cold, waitMs: serverWaitMs }); + profile.setRunMeta({ cold }); + // the server answers before the blueprint's runPHP import step finishes; + // capturing early races the import (front page = blog index). The last + // manifest page resolving proves the import ran to completion. + const lastPage = manifest.pages[manifest.pages.length - 1]; + if (lastPage) { + const importTok = profile.mark('playground.wait.import'); + await waitForImport(pageUrl(base, { ...lastPage, front: false }), 120000, () => proc.exitCode); + profile.measure(importTok, { cold }); + } + const { chromium, PNG, pixelmatch } = await loadCaptureDeps(PLUGIN_ROOT); + const browser = await launchBrowser(chromium, { headless: true }, { tool: 'playground_render' }); + const server = await serveDirectory(workspaceRoot); // mockup screenshots through the same pipeline + const thresholds = { maxMismatchPercent: args.maxMismatchPercent ?? 1, maxHeightDelta: args.maxHeightDelta ?? 8 }; + const pagesReport = []; + try { + for (const page of manifest.pages) { + const mockupPath = page.mockupPath || inferMockupPath(workspaceRoot, page); + const results = []; + for (const viewport of args.viewports || DEFAULT_VIEWPORTS) { + const mockShot = path.join(outDir, `${page.slug}-mockup-${viewport.name}.png`); + const wpShot = path.join(outDir, `${page.slug}-wp-${viewport.name}.png`); + const diffShot = path.join(outDir, `${page.slug}-diff-${viewport.name}.png`); + const capMeta = { page: page.slug, viewport: viewport.name }; + await profile.span('playground.capture.mockup', () => captureUrl(browser, server.urlFor(path.join(workspaceRoot, mockupPath)), mockShot, viewport), capMeta); + await profile.span('playground.capture.wp', () => captureUrl(browser, pageUrl(base, page), wpShot, viewport), capMeta); + results.push(profile.span('playground.compare', () => comparePngs({ target: 'wordpress', mockupShot: mockShot, candidateShot: wpShot, diffShot, viewport, PNG, pixelmatch }), capMeta)); + } + const aggregate = { + maxMismatchPercent: Math.max(...results.map((r) => r.mismatchPercent)), + maxHeightDelta: Math.max(...results.map((r) => r.heightDelta)), + }; + pagesReport.push({ page: page.slug, mockupPath, results, aggregate, + passed: aggregate.maxMismatchPercent <= thresholds.maxMismatchPercent && aggregate.maxHeightDelta <= thresholds.maxHeightDelta }); + } + // editor validation: the real editor recomputes save() in the + // browser and flags drift our Node round-trip cannot see (kses + // escaping, content-filter mangling). Zero failures required. + await profile.span('playground.editorValidation', () => editorValidation(browser, base, manifest.pages, pagesReport), { pages: manifest.pages.length }); + } finally { + await browser.close(); + await server.close?.(); + } + const report = { + generatedAt: new Date().toISOString(), thresholds, pages: pagesReport, + aggregates: { + maxMismatchPercent: Math.max(...pagesReport.map((p) => p.aggregate.maxMismatchPercent)), + maxHeightDelta: Math.max(...pagesReport.map((p) => p.aggregate.maxHeightDelta)), + }, + passed: pagesReport.every((p) => p.passed && (p.editorValidation?.failures ?? 0) === 0), + }; + writeJson(path.join(workspaceRoot, 'reports/theme-comparison.json'), report); + return report; + } catch (error) { + throw new Error(`playground_render failed: ${error.message}\n--- playground logs (tail) ---\n${logs.slice(-2000)}`); + } finally { + proc.kill('SIGTERM'); + } +} + +async function editorValidation(browser, base, pages, pagesReport) { + const context = await browser.newContext(); + const page = await context.newPage(); + try { + await page.goto(`${base}/wp-login.php`); + await page.fill('#user_login', 'admin'); + await page.fill('#user_pass', 'password'); + await page.click('#wp-submit'); + await page.waitForLoadState('networkidle'); + for (const manifestPage of pages) { + const entry = pagesReport.find((p) => p.page === manifestPage.slug); + if (!entry) continue; + const failures = []; + const onConsole = (msg) => { + const text = msg.text(); + if (/Block validation failed/i.test(text)) { + failures.push(text.slice(0, 300)); + } + }; + page.on('console', onConsole); + // resolve the post id from the editor list is brittle; the edit + // link on the frontend admin bar is too — use post slug query + await page.goto(`${base}/wp-admin/edit.php?post_type=page&s=${encodeURIComponent(manifestPage.title)}`); + const editHref = await page.getAttribute('.row-title >> nth=0', 'href').catch(() => null); + if (editHref) { + await page.goto(editHref); + await page.waitForSelector('.block-editor-block-list__layout', { timeout: 60000 }).catch(() => {}); + await page.waitForTimeout(2000); + } else { + failures.push(`could not locate the page "${manifestPage.title}" in wp-admin`); + } + page.off('console', onConsole); + entry.editorValidation = { failures: failures.length, samples: failures.slice(0, 3) }; + if (failures.length > 0) entry.passed = false; + } + } finally { + await context.close(); + } +} + +async function waitForServer(base, timeoutMs, exited) { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + if (exited() !== null) throw new Error('playground process exited before becoming ready'); + try { + const res = await fetch(base, { redirect: 'manual' }); + if (res.status < 500) return; + } catch { /* not up yet */ } + await new Promise((r) => setTimeout(r, 1000)); + } + throw new Error(`playground server not ready after ${timeoutMs}ms`); +} + +async function waitForImport(url, timeoutMs, exited) { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + if (exited() !== null) throw new Error('playground process exited during content import'); + try { + const res = await fetch(url, { redirect: 'manual' }); + if (res.status === 200) return; + } catch { /* still importing */ } + await new Promise((r) => setTimeout(r, 1000)); + } + throw new Error(`content import did not complete within ${timeoutMs}ms (${url} never returned 200)`); +} + +function inferMockupPath(workspaceRoot, page) { + for (const candidate of [`mockup/${page.sourceFile || ''}`, `mockup/${page.page || page.slug}.html`, 'mockup/index.html']) { + if (candidate !== 'mockup/' && fs.existsSync(path.join(workspaceRoot, candidate))) return candidate; + } + throw new Error(`No mockup found for page ${page.slug}; pass mockupPath in the manifest page entry.`); +} diff --git a/apps/cli/ai/html-to-blocks-engine/theme/rewrites.mjs b/apps/cli/ai/html-to-blocks-engine/theme/rewrites.mjs new file mode 100644 index 0000000000..f58343947c --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/rewrites.mjs @@ -0,0 +1,56 @@ +// tools/theme/rewrites.mjs +const norm = (v) => String(v ?? '').trim().toLowerCase(); + +export function rewriteTreePresets(block, map) { + const out = { ...block, attrs: structuredClone(block.attrs || {}), innerBlocks: (block.innerBlocks || []).map((b) => rewriteTreePresets(b, map)) }; + const style = out.attrs.style || {}; + if (style.color) { + if (map.colors[norm(style.color.background)]) { out.attrs.backgroundColor = map.colors[norm(style.color.background)]; delete style.color.background; } + if (map.colors[norm(style.color.text)]) { out.attrs.textColor = map.colors[norm(style.color.text)]; delete style.color.text; } + if (Object.keys(style.color).length === 0) delete style.color; + } + if (style.typography?.fontSize && map.fontSizes[norm(style.typography.fontSize)]) { + out.attrs.fontSize = map.fontSizes[norm(style.typography.fontSize)]; + delete style.typography.fontSize; + if (Object.keys(style.typography).length === 0) delete style.typography; + } + if (style.spacing) rewriteSpacing(style.spacing, map.spacing); + if (Object.keys(style).length === 0) delete out.attrs.style; + return out; +} +function rewriteSpacing(node, spacingMap) { + for (const [key, value] of Object.entries(node)) { + if (value && typeof value === 'object') rewriteSpacing(value, spacingMap); + else if (spacingMap[norm(value)]) node[key] = `var:preset|spacing|${spacingMap[norm(value)]}`; + } +} + +export function rewriteCssVars(css, customMap) { + let out = css; + for (const [name, slugName] of Object.entries(customMap)) { + out = out.split(`var(${name})`).join(`var(--wp--custom--${slugName})`); + out = out.replace(new RegExp(`\\s*${name}\\s*:[^;}]+;?`, 'g'), ''); + } + return out; +} + +export function rewriteLinks(value, linkMap) { + let out = String(value); + for (const [file, permalink] of Object.entries(linkMap)) { + for (const enc of [file, file.replace(/ /g, '%20')]) { + out = out.split(`href="${enc}#`).join(`href="${permalink}#`); + out = out.split(`href="${enc}"`).join(`href="${permalink}"`); + if (out === enc) out = permalink; + if (out.startsWith(`${enc}#`)) out = permalink + out.slice(enc.length); + } + } + return out; +} + +export function rewriteMediaUrls(value, mediaMap, base) { + let out = String(value); + for (const [from, to] of Object.entries(mediaMap)) { + out = out.split(from).join(`${base}/${to}`); + } + return out; +} diff --git a/apps/cli/ai/html-to-blocks-engine/theme/scaffold.mjs b/apps/cli/ai/html-to-blocks-engine/theme/scaffold.mjs new file mode 100644 index 0000000000..dcbab2cbff --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/scaffold.mjs @@ -0,0 +1,185 @@ +// tools/theme/scaffold.mjs +import fs from 'node:fs'; +import path from 'node:path'; +import { resolvePath, writeFile, writeJson } from '../lib/workspace.mjs'; +import { ensureBlocksRegistered, serializeBlocks } from '../lib/wp-serialize.mjs'; +import { loadPageTrees } from './evidence.mjs'; +import { rewriteTreePresets, rewriteCssVars, rewriteLinks, rewriteMediaUrls } from './rewrites.mjs'; +import { styleCss, functionsPhp, buildThemeJson, templateMarkup, DEFAULT_TEMPLATES } from './generate/theme-files.mjs'; +import { writeBlocksPlugin } from './generate/blocks-plugin.mjs'; +import { writeContentPlugin } from './generate/content-plugin.mjs'; + +// The tool schema cannot fully express the nested decision shapes, so validate +// them here and fail with an actionable message (the agent retries against it) +// instead of a cryptic "Cannot read properties of undefined" deep in scaffolding. +function validateScaffoldArgs(args) { + const problems = []; + if (!Array.isArray(args.parts)) { + problems.push('`parts` must be an array (use [] if the site has no shared parts).'); + } else { + args.parts.forEach((p, i) => { + if (!p || typeof p.slug !== 'string') problems.push(`parts[${i}]: missing string "slug".`); + if (!p || !p.source || typeof p.source.page !== 'string' || typeof p.source.index !== 'number') { + problems.push(`parts[${i}] (slug=${p && p.slug}): requires "source": { "page": , "index": } naming the page key and the top-level block index this part is lifted from. See reports/template-parts.json occurrence groups for the page name and block index.`); + } + }); + } + if (!Array.isArray(args.pages)) { + problems.push('`pages` must be an array of { page, slug, title, front?, stripIndexes?, sourceFile? }.'); + } else { + args.pages.forEach((p, i) => { + if (!p || typeof p.page !== 'string') problems.push(`pages[${i}]: missing string "page" (the page key, e.g. "index").`); + if (!p || typeof p.slug !== 'string') problems.push(`pages[${i}]: missing string "slug".`); + }); + } + if (!args.templates || typeof args.templates !== 'object' || Array.isArray(args.templates)) { + problems.push('`templates` must be an object mapping template name -> array of entries.'); + } else { + for (const [t, entries] of Object.entries(args.templates)) { + if (!Array.isArray(entries)) { problems.push(`templates.${t} must be an array of entries.`); continue; } + entries.forEach((e, i) => { + if (!e || (e.type !== 'part' && e.type !== 'tree' && e.type !== 'blocks')) { + problems.push(`templates.${t}[${i}]: entry needs "type": "part" | "tree".`); + } else if (e.type === 'part' && typeof e.slug !== 'string') { + problems.push(`templates.${t}[${i}]: part entry needs string "slug" matching a parts[].slug.`); + } else if (e.type === 'tree' && !Array.isArray(e.blocks)) { + problems.push(`templates.${t}[${i}]: tree entry needs a "blocks" array.`); + } + }); + } + } + if (problems.length) { + throw new Error( + 'scaffold_block_theme argument errors:\n- ' + problems.join('\n- ') + + '\n\nExpected shapes:\n' + + ' parts: [{ "slug": "header", "area": "header", "source": { "page": "index", "index": 0 } }]\n' + + ' templates: { "index": [{ "type": "part", "slug": "header" }, { "type": "tree", "blocks": [/* core blocks, e.g. post-content */] }, { "type": "part", "slug": "footer" }] }\n' + + ' pages: [{ "page": "index", "slug": "home", "title": "Home", "front": true }]\n' + + 'Read reports/template-parts.json (occurrence groups give page + block index) to fill each part.source.' + ); + } +} + +export function scaffoldBlockTheme(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const { slug, name, tokenMap = {}, mediaMap = {} } = args; + const themeDir = path.join(workspaceRoot, 'theme', slug); + const files = []; + + validateScaffoldArgs(args); + // tokenMap.custom drives CSS custom-property renames; default it so a + // tokenMap without `custom` cannot crash rewriteCssVars (Object.entries). + const customRenames = tokenMap.custom || {}; + ensureBlocksRegistered(workspaceRoot); + const pages = new Map(loadPageTrees(workspaceRoot).map((p) => [p.page, p.tree])); + const linkMap = buildLinkMap(args.pages, workspaceRoot); + + const transformTree = (blocks) => blocks + .map((b) => rewriteTreePresets(b, tokenMap)) + .map((b) => deepMapStrings(b, (s) => rewriteCssVars(rewriteMediaUrls(rewriteLinks(s, linkMap), mediaMap, '{{THEME_URI}}'), customRenames))); + + // parts + for (const part of args.parts) { + const tree = pages.get(part.source.page); + if (!tree || !Array.isArray(tree.blocks)) throw new Error(`Part ${part.slug}: source.page "${part.source.page}" is not a known page. Known page keys: ${[...pages.keys()].join(', ')}.`); + const block = tree.blocks[part.source.index]; + if (!block) throw new Error(`Part ${part.slug}: no block at index ${part.source.index} on page "${part.source.page}" (it has ${tree.blocks.length} top-level blocks; valid indexes 0..${tree.blocks.length - 1}).`); + // Parts keep {{THEME_URI}} placeholders: relative URLs would resolve + // against the page URL, not the theme dir. functions.php replaces the + // placeholder with get_stylesheet_directory_uri() at render time. + const markup = serializeBlocks(transformTree([block]), {}); + writeFile(path.join(themeDir, `parts/${part.slug}.html`), markup); + files.push(`parts/${part.slug}.html`); + } + + // templates: agent-specified + standing defaults using index chrome + const templates = { ...args.templates }; + const chrome = (args.templates.index || []).filter((e) => e.type === 'part'); + const chromeTop = chrome.slice(0, Math.ceil(chrome.length / 2)); + const chromeBottom = chrome.slice(Math.ceil(chrome.length / 2)); + for (const [tplName, body] of Object.entries(DEFAULT_TEMPLATES)) { + templates[tplName] ??= [...chromeTop, ...body, ...chromeBottom]; + } + for (const [tplName, entries] of Object.entries(templates)) { + // tree entries (the generic defaults, or agent-authored bodies) are + // serialized through @wordpress/blocks so the markup is canonical + const resolved = entries.map((entry) => entry.type === 'tree' + ? { type: 'blocks', markup: serializeBlocks(entry.blocks, {}) } + : entry); + writeFile(path.join(themeDir, `templates/${tplName}.html`), templateMarkup(resolved)); + files.push(`templates/${tplName}.html`); + } + + // per-page content payload + const contentPages = args.pages.map((page) => { + const tree = pages.get(page.page); + if (!tree || !Array.isArray(tree.blocks)) throw new Error(`pages[].page "${page.page}" is not a known page. Known page keys: ${[...pages.keys()].join(', ')}.`); + const strip = new Set(page.stripIndexes || []); + const blocks = tree.blocks.filter((_, i) => !strip.has(i)); + return { ...page, markup: serializeBlocks(transformTree(blocks), {}) }; + }); + + // theme.json / style.css / functions.php + const themeJson = buildThemeJson({ + settings: args.themeSettings, + styles: deepMapStrings(args.themeStyles || {}, (s) => rewriteCssVars(s, customRenames)), + fontFamilies: args.fontFamilies || [], + // theme.json schema: templateParts items allow only name/title/area (name = parts/.html). + templateParts: args.parts.map(({ slug: s, area }) => ({ name: s, area })), + customTemplates: Object.keys(args.templates).filter((t) => !['index', 'archive', 'single', '404'].includes(t) && !t.startsWith('page-') && !t.startsWith('front-page')) + .map((t) => ({ name: t, title: t, postTypes: ['page'] })), + }); + writeJson(path.join(themeDir, 'theme.json'), themeJson); + const css = rewriteMediaUrls(rewriteCssVars(args.customCss || '', customRenames), mediaMap, '..'); + writeFile(path.join(themeDir, 'style.css'), styleCss({ name, slug, description: args.description }, css)); + const blocksResult = writeBlocksPlugin({ + workspaceRoot, slug, themeName: name, + outDir: path.join(workspaceRoot, 'theme-plugin', `${slug}-blocks`), + transformCss: (blockCss) => rewriteCssVars(blockCss, customRenames), + }); + writeFile(path.join(themeDir, 'functions.php'), functionsPhp({ slug, customBlocks: blocksResult.blocks })); + files.push('theme.json', 'style.css', 'functions.php'); + + // media copy (tolerant): resolve the source from the workspace root, fall + // back to mockup/ (import_provided_markup stages assets under mockup/), + // and skip-and-record a missing asset rather than aborting the whole theme. + const skippedMedia = []; + for (const [from, to] of Object.entries(mediaMap)) { + let src = path.join(workspaceRoot, from); + if (!fs.existsSync(src) && fs.existsSync(path.join(workspaceRoot, 'mockup', from))) { + src = path.join(workspaceRoot, 'mockup', from); + } + if (!fs.existsSync(src)) { skippedMedia.push(from); continue; } + const dest = path.join(themeDir, to); + fs.mkdirSync(path.dirname(dest), { recursive: true }); + fs.copyFileSync(src, dest); + files.push(to); + } + + const contentResult = writeContentPlugin({ slug, themeName: name, outDir: path.join(workspaceRoot, 'theme-plugin', `${slug}-content`), pages: contentPages }); + return { + themeDir, files, + blocksPlugin: blocksResult.blocks.length ? `theme-plugin/${slug}-blocks` : null, + contentPlugin: `theme-plugin/${slug}-content`, + pages: contentPages.map(({ markup, ...p }) => p), + skippedMedia, + next: 'Run validate_block_theme, then playground_render.', + }; +} + +function buildLinkMap(pages, workspaceRoot) { + // map each source page's mockup filename to its permalink path + const map = {}; + for (const page of pages) { + const file = page.sourceFile || `${page.page}.html`; + map[file] = page.front ? '/' : `/${page.slug}/`; + } + return map; +} + +export function deepMapStrings(value, fn) { + if (typeof value === 'string') return fn(value); + if (Array.isArray(value)) return value.map((v) => deepMapStrings(v, fn)); + if (value && typeof value === 'object') return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, deepMapStrings(v, fn)])); + return value; +} diff --git a/apps/cli/ai/html-to-blocks-engine/theme/theme-json-schema.json b/apps/cli/ai/html-to-blocks-engine/theme/theme-json-schema.json new file mode 100644 index 0000000000..a1f51ace92 --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/theme-json-schema.json @@ -0,0 +1,2729 @@ +{ + "title": "JSON schema for WordPress block theme global settings and styles", + "$schema": "http://json-schema.org/draft-07/schema#", + "definitions": { + "//": { + "explainer": "https://developer.wordpress.org/themes/advanced-topics/theme-json/", + "createTheme": "https://developer.wordpress.org/themes/", + "reference": "https://developer.wordpress.org/block-editor/how-to-guides/themes/theme-json/" + }, + "refComplete": { + "type": "object", + "properties": { + "ref": { + "description": "A reference to another property value. e.g. `styles.color.text`", + "type": "string" + } + }, + "additionalProperties": false + }, + "settingsAppearanceToolsProperties": { + "type": "object", + "properties": { + "appearanceTools": { + "description": "Setting that enables the following UI tools:\n\n- background: backgroundImage, backgroundSize\n- border: color, radius, style, width\n- color: link, heading, button, caption\n- dimensions: aspectRatio, minHeight\n- position: sticky\n- spacing: blockGap, margin, padding\n- typography: lineHeight", + "type": "boolean", + "default": false + } + } + }, + "settingsBackgroundProperties": { + "type": "object", + "properties": { + "background": { + "description": "Settings related to background.", + "type": "object", + "properties": { + "backgroundImage": { + "description": "Allow users to set a background image.", + "type": "boolean", + "default": false + }, + "backgroundSize": { + "description": "Allow users to set values related to the size of a background image, including size, position, and repeat controls.", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + } + } + }, + "settingsBorderProperties": { + "type": "object", + "properties": { + "border": { + "description": "Settings related to borders.", + "type": "object", + "properties": { + "color": { + "description": "Allow users to set custom border colors.", + "type": "boolean", + "default": false + }, + "radius": { + "description": "Allow users to set custom border radius.", + "type": "boolean", + "default": false + }, + "style": { + "description": "Allow users to set custom border styles.", + "type": "boolean", + "default": false + }, + "width": { + "description": "Allow users to set custom border widths.", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + } + } + }, + "settingsColorProperties": { + "type": "object", + "properties": { + "color": { + "description": "Settings related to colors.", + "type": "object", + "properties": { + "background": { + "description": "Allow users to set background colors.", + "type": "boolean", + "default": true + }, + "custom": { + "description": "Allow users to select custom colors.", + "type": "boolean", + "default": true + }, + "customDuotone": { + "description": "Allow users to create custom duotone filters.", + "type": "boolean", + "default": true + }, + "customGradient": { + "description": "Allow users to create custom gradients.", + "type": "boolean", + "default": true + }, + "defaultDuotone": { + "description": "Allow users to choose filters from the default duotone filter presets.", + "type": "boolean", + "default": true + }, + "defaultGradients": { + "description": "Allow users to choose colors from the default gradients.", + "type": "boolean", + "default": true + }, + "defaultPalette": { + "description": "Allow users to choose colors from the default palette.", + "type": "boolean", + "default": true + }, + "duotone": { + "description": "Duotone presets for the duotone picker.\nDoesn't generate classes or properties.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Name of the duotone preset, translatable.", + "type": "string" + }, + "slug": { + "description": "Kebab-case unique identifier for the duotone preset.", + "type": "string" + }, + "colors": { + "description": "List of colors from dark to light.", + "type": "array", + "items": { + "description": "CSS hex or rgb string.", + "type": "string" + } + } + }, + "required": [ "name", "slug", "colors" ], + "additionalProperties": false + } + }, + "gradients": { + "description": "Gradient presets for the gradient picker.\nGenerates a single class (`.has-{slug}-background`) and custom property (`--wp--preset--gradient--{slug}`) per preset value.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Name of the gradient preset, translatable.", + "type": "string" + }, + "slug": { + "description": "Kebab-case unique identifier for the gradient preset.", + "type": "string" + }, + "gradient": { + "description": "CSS gradient string.", + "type": "string" + } + }, + "required": [ "name", "slug", "gradient" ], + "additionalProperties": false + } + }, + "link": { + "description": "Allow users to set link colors in a block.", + "type": "boolean", + "default": false + }, + "palette": { + "description": "Color palette presets for the color picker.\nGenerates three classes (`.has-{slug}-color`, `.has-{slug}-background-color`, and `.has-{slug}-border-color`) and a single custom property (`--wp--preset--color--{slug}`) per preset value.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Name of the color preset, translatable.", + "type": "string" + }, + "slug": { + "description": "Kebab-case unique identifier for the color preset.", + "type": "string" + }, + "color": { + "description": "CSS hex or rgb(a) string.", + "type": "string" + } + }, + "required": [ "name", "slug", "color" ], + "additionalProperties": false + } + }, + "text": { + "description": "Allow users to set text colors in a block.", + "type": "boolean", + "default": true + }, + "heading": { + "description": "Allow users to set heading colors in a block.", + "type": "boolean", + "default": true + }, + "button": { + "description": "Allow users to set button colors in a block.", + "type": "boolean", + "default": true + }, + "caption": { + "description": "Allow users to set caption colors in a block.", + "type": "boolean", + "default": true + } + }, + "additionalProperties": false + } + } + }, + "settingsDimensionsProperties": { + "type": "object", + "properties": { + "dimensions": { + "description": "Settings related to dimensions.", + "type": "object", + "properties": { + "aspectRatio": { + "description": "Allow users to set an aspect ratio.", + "type": "boolean", + "default": false + }, + "defaultAspectRatios": { + "description": "Allow users to choose aspect ratios from the default set of aspect ratios.", + "type": "boolean", + "default": true + }, + "aspectRatios": { + "description": "Allow users to define aspect ratios for some blocks.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Name of the aspect ratio preset.", + "type": "string" + }, + "slug": { + "description": "Kebab-case unique identifier for the aspect ratio preset.", + "type": "string" + }, + "ratio": { + "description": "Aspect ratio expressed as a division or decimal.", + "type": "string" + } + } + } + }, + "minHeight": { + "description": "Allow users to set custom minimum height.", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + } + } + }, + "settingsLayoutProperties": { + "type": "object", + "properties": { + "layout": { + "description": "Settings related to layout.", + "type": "object", + "properties": { + "contentSize": { + "description": "Sets the max-width of the content.", + "type": "string" + }, + "wideSize": { + "description": "Sets the max-width of wide (`.alignwide`) content. Also used as the maximum viewport when calculating fluid font sizes", + "type": "string" + }, + "allowEditing": { + "description": "Disable the layout UI controls.", + "type": "boolean", + "default": true + }, + "allowCustomContentAndWideSize": { + "description": "Enable or disable the custom content and wide size controls.", + "type": "boolean", + "default": true + } + }, + "additionalProperties": false + } + } + }, + "settingsLightboxProperties": { + "type": "object", + "properties": { + "lightbox": { + "description": "Settings related to the lightbox.", + "type": "object", + "properties": { + "enabled": { + "description": "Defines whether the lightbox is enabled or not.", + "type": "boolean" + }, + "allowEditing": { + "description": "Defines whether to show the Lightbox UI in the block editor. If set to `false`, the user won't be able to change the lightbox settings in the block editor.", + "type": "boolean" + } + }, + "additionalProperties": false + } + } + }, + "settingsPositionProperties": { + "type": "object", + "properties": { + "position": { + "description": "Settings related to position.", + "type": "object", + "properties": { + "sticky": { + "description": "Allow users to set sticky position.", + "type": "boolean", + "default": false + } + }, + "additionalProperties": false + } + } + }, + "settingsShadowProperties": { + "type": "object", + "properties": { + "shadow": { + "description": "Settings related to shadows.", + "type": "object", + "properties": { + "defaultPresets": { + "description": "Allow users to choose shadows from the default shadow presets.", + "type": "boolean", + "default": true + }, + "presets": { + "description": "Shadow presets for the shadow picker.\nGenerates a single custom property (`--wp--preset--shadow--{slug}`) per preset value.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Name of the shadow preset, translatable.", + "type": "string" + }, + "slug": { + "description": "Kebab-case unique identifier for the shadow preset.", + "type": "string" + }, + "shadow": { + "description": "CSS box-shadow value", + "type": "string" + } + }, + "required": [ "name", "slug", "shadow" ], + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + }, + "settingsSpacingProperties": { + "type": "object", + "properties": { + "spacing": { + "description": "Settings related to spacing.", + "type": "object", + "properties": { + "blockGap": { + "description": "Enables `--wp--style--block-gap` to be generated from styles.spacing.blockGap.\nA value of `null` instead of `false` further disables layout styles from being generated.", + "oneOf": [ + { "type": "boolean" }, + { "type": "null" } + ], + "default": null + }, + "margin": { + "description": "Allow users to set a custom margin.", + "type": "boolean", + "default": false + }, + "padding": { + "description": "Allow users to set a custom padding.", + "type": "boolean", + "default": false + }, + "units": { + "description": "List of units the user can use for spacing values.", + "type": "array", + "items": { + "type": "string" + }, + "minItems": 1, + "default": [ "px", "em", "rem", "vh", "vw", "%" ] + }, + "customSpacingSize": { + "description": "Allow users to set custom space sizes.", + "type": "boolean", + "default": true + }, + "defaultSpacingSizes": { + "description": "Allow users to choose space sizes from the default space size presets.", + "type": "boolean", + "default": true + }, + "spacingSizes": { + "description": "Space size presets for the space size selector.\nGenerates a custom property (`--wp--preset--spacing--{slug}`) per preset value.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Name of the space size preset, translatable.", + "type": "string" + }, + "slug": { + "description": "Unique identifier for the space size preset. For best cross theme compatibility these should be in the form '10','20','30','40','50','60', etc. with '50' representing the 'Medium' size step. If all slugs begin with a number they will be merged with default and user slugs and sorted numerically.", + "type": "string" + }, + "size": { + "description": "CSS space-size value, including units.", + "type": "string" + } + }, + "additionalProperties": false + } + }, + "spacingScale": { + "description": "Settings to auto-generate space size presets for the space size selector.\nGenerates a custom property (--wp--preset--spacing--{slug}`) per preset value.", + "type": "object", + "properties": { + "operator": { + "description": "With + or * depending on whether scale is generated by increment or multiplier.", + "type": "string", + "enum": [ "+", "*" ], + "default": "*" + }, + "increment": { + "description": "The amount to increment each step by.", + "type": "number", + "exclusiveMinimum": 0, + "default": 1.5 + }, + "steps": { + "description": "Number of steps to generate in scale.", + "type": "integer", + "minimum": 1, + "maximum": 10, + "default": 7 + }, + "mediumStep": { + "description": "The value to medium setting in the scale.", + "type": "number", + "exclusiveMinimum": 0, + "default": 1.5 + }, + "unit": { + "description": "Unit that the scale uses, eg. rem, em, px.", + "type": "string", + "enum": [ + "px", + "em", + "rem", + "%", + "vw", + "svw", + "lvw", + "dvw", + "vh", + "svh", + "lvh", + "dvh", + "vi", + "svi", + "lvi", + "dvi", + "vb", + "svb", + "lvb", + "dvb", + "vmin", + "svmin", + "lvmin", + "dvmin", + "vmax", + "svmax", + "lvmax", + "dvmax" + ], + "default": "rem" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + } + } + }, + "settingsTypographyProperties": { + "type": "object", + "properties": { + "typography": { + "description": "Settings related to typography.", + "type": "object", + "properties": { + "defaultFontSizes": { + "description": "Allow users to choose font sizes from the default font size presets.", + "type": "boolean", + "default": true + }, + "customFontSize": { + "description": "Allow users to set custom font sizes.", + "type": "boolean", + "default": true + }, + "fontStyle": { + "description": "Allow users to set custom font styles.", + "type": "boolean", + "default": true + }, + "fontWeight": { + "description": "Allow users to set custom font weights.", + "type": "boolean", + "default": true + }, + "fluid": { + "description": "Enables fluid typography and allows users to set global fluid typography parameters.", + "oneOf": [ + { + "type": "object", + "properties": { + "minFontSize": { + "description": "Allow users to set a global minimum font size boundary in px, rem or em. Custom font sizes below this value will not be clamped, and all calculated minimum font sizes will be, at a minimum, this value.", + "type": "string" + }, + "maxViewportWidth": { + "description": "Allow users to set custom a max viewport width in px, rem or em, used to set the maximum size boundary of a fluid font size.", + "type": "string" + }, + "minViewportWidth": { + "description": "Allow users to set a custom min viewport width in px, rem or em, used to set the minimum size boundary of a fluid font size.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "boolean" + } + ], + "default": false + }, + "letterSpacing": { + "description": "Allow users to set custom letter spacing.", + "type": "boolean", + "default": true + }, + "lineHeight": { + "description": "Allow users to set custom line height.", + "type": "boolean", + "default": false + }, + "textAlign": { + "description": "Allow users to set the text align.", + "type": "boolean", + "default": true + }, + "textColumns": { + "description": "Allow users to set the number of text columns.", + "type": "boolean", + "default": false + }, + "textDecoration": { + "description": "Allow users to set custom text decorations.", + "type": "boolean", + "default": true + }, + "writingMode": { + "description": "Allow users to set the writing mode.", + "type": "boolean", + "default": false + }, + "textTransform": { + "description": "Allow users to set custom text transforms.", + "type": "boolean", + "default": true + }, + "dropCap": { + "description": "Enable drop cap.", + "type": "boolean", + "default": true + }, + "fontSizes": { + "description": "Font size presets for the font size selector.\nGenerates a single class (`.has-{slug}-color`) and custom property (`--wp--preset--font-size--{slug}`) per preset value.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Name of the font size preset, translatable.", + "type": "string" + }, + "slug": { + "description": "Kebab-case unique identifier for the font size preset.", + "type": "string" + }, + "size": { + "description": "CSS font-size value, including units.", + "type": "string" + }, + "fluid": { + "description": "Specifies the minimum and maximum font size value of a fluid font size. Set to `false` to bypass fluid calculations and use the static `size` value.", + "oneOf": [ + { + "type": "object", + "properties": { + "min": { + "description": "A min font size for fluid font size calculations in px, rem or em.", + "type": "string" + }, + "max": { + "description": "A max font size for fluid font size calculations in px, rem or em.", + "type": "string" + } + }, + "additionalProperties": false + }, + { + "type": "boolean" + } + ] + } + }, + "additionalProperties": false + } + }, + "fontFamilies": { + "description": "Font family presets for the font family selector.\nGenerates a single custom property (`--wp--preset--font-family--{slug}`) per preset value.", + "type": "array", + "items": { + "description": "Font family preset", + "type": "object", + "properties": { + "name": { + "description": "Name of the font family preset, translatable.", + "type": "string" + }, + "slug": { + "description": "Kebab-case unique identifier for the font family preset.", + "type": "string" + }, + "fontFamily": { + "description": "CSS font-family value.", + "type": "string" + }, + "fontFace": { + "description": "Array of font-face declarations.", + "type": "array", + "items": { + "type": "object", + "properties": { + "fontFamily": { + "description": "CSS font-family value.", + "type": "string", + "default": "" + }, + "fontStyle": { + "description": "CSS font-style value.", + "type": "string", + "default": "normal" + }, + "fontWeight": { + "description": "List of available font weights, separated by a space.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "integer" + } + ], + "default": "400" + }, + "fontDisplay": { + "description": "CSS font-display value.", + "type": "string", + "enum": [ + "auto", + "block", + "fallback", + "swap", + "optional" + ], + "default": "fallback" + }, + "src": { + "description": "Paths or URLs to the font files.", + "oneOf": [ + { + "type": "string" + }, + { + "type": "array", + "items": { + "type": "string" + } + } + ], + "default": [] + }, + "fontStretch": { + "description": "CSS font-stretch value.", + "type": "string" + }, + "ascentOverride": { + "description": "CSS ascent-override value.", + "type": "string" + }, + "descentOverride": { + "description": "CSS descent-override value.", + "type": "string" + }, + "fontVariant": { + "description": "CSS font-variant value.", + "type": "string" + }, + "fontFeatureSettings": { + "description": "CSS font-feature-settings value.", + "type": "string" + }, + "fontVariationSettings": { + "description": "CSS font-variation-settings value.", + "type": "string" + }, + "lineGapOverride": { + "description": "CSS line-gap-override value.", + "type": "string" + }, + "sizeAdjust": { + "description": "CSS size-adjust value.", + "type": "string" + }, + "unicodeRange": { + "description": "CSS unicode-range value.", + "type": "string" + } + }, + "required": [ "fontFamily", "src" ], + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + }, + "additionalProperties": false + } + } + }, + "settingsCustomProperties": { + "type": "object", + "properties": { + "custom": { + "$ref": "#/definitions/settingsCustomAdditionalProperties" + } + } + }, + "settingsProperties": { + "allOf": [ + { "$ref": "#/definitions/settingsAppearanceToolsProperties" }, + { "$ref": "#/definitions/settingsBackgroundProperties" }, + { "$ref": "#/definitions/settingsBorderProperties" }, + { "$ref": "#/definitions/settingsColorProperties" }, + { "$ref": "#/definitions/settingsDimensionsProperties" }, + { "$ref": "#/definitions/settingsLayoutProperties" }, + { "$ref": "#/definitions/settingsLightboxProperties" }, + { "$ref": "#/definitions/settingsPositionProperties" }, + { "$ref": "#/definitions/settingsShadowProperties" }, + { "$ref": "#/definitions/settingsSpacingProperties" }, + { "$ref": "#/definitions/settingsTypographyProperties" }, + { "$ref": "#/definitions/settingsCustomProperties" } + ] + }, + "settingsPropertyNames": { + "enum": [ + "appearanceTools", + "background", + "border", + "color", + "dimensions", + "layout", + "lightbox", + "position", + "shadow", + "spacing", + "typography", + "custom" + ] + }, + "settingsPropertiesComplete": { + "allOf": [ + { + "$ref": "#/definitions/settingsProperties" + }, + { + "type": "object", + "propertyNames": { + "$ref": "#/definitions/settingsPropertyNames" + } + } + ] + }, + "settingsBlocksPropertiesComplete": { + "description": "Settings defined on a per-block basis.", + "type": "object", + "properties": { + "core/archives": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/audio": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/avatar": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/block": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/button": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/buttons": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/calendar": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/categories": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/code": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/column": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/columns": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comment-author-avatar": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comment-author-name": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comment-content": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comment-date": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comment-edit-link": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comment-reply-link": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comments": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comments-pagination": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comments-pagination-next": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comments-pagination-numbers": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comments-pagination-previous": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comments-title": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/comment-template": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/cover": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/details": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/embed": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/file": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/freeform": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/gallery": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/group": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/heading": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/home-link": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/html": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/image": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/latest-comments": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/latest-posts": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/list": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/list-item": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/loginout": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/media-text": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/missing": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/more": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/navigation": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/navigation-link": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/navigation-submenu": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/nextpage": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/page-list": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/page-list-item": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/paragraph": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-author": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-author-biography": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-author-name": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-comment": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-comments-count": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-comments-form": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-comments-link": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-content": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-date": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-excerpt": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-featured-image": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-navigation-link": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-template": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-terms": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-time-to-read": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/post-title": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/preformatted": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/pullquote": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/query": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/query-no-results": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/query-pagination": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/query-pagination-next": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/query-pagination-numbers": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/query-pagination-previous": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/query-title": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/quote": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/read-more": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/rss": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/search": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/separator": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/shortcode": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/site-logo": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/site-tagline": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/site-title": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/social-link": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/social-links": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/spacer": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/table": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/table-of-contents": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/tag-cloud": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/template-part": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/term-description": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/text-columns": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/verse": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/video": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/widget-area": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/legacy-widget": { + "$ref": "#/definitions/settingsPropertiesComplete" + }, + "core/widget-group": { + "$ref": "#/definitions/settingsPropertiesComplete" + } + }, + "patternProperties": { + "^[a-z][a-z0-9-]*/[a-z][a-z0-9-]*$": { + "$ref": "#/definitions/settingsPropertiesComplete" + } + }, + "additionalProperties": false + }, + "settingsCustomAdditionalProperties": { + "description": "Generate custom CSS custom properties of the form `--wp--custom--{key}--{nested-key}: {value};`. `camelCased` keys are transformed to `kebab-case` as to follow the CSS property naming schema. Keys at different depth levels are separated by `--`, so keys should not include `--` in the name.", + "type": "object", + "additionalProperties": { + "oneOf": [ + { + "type": "string" + }, + { + "type": "number" + }, + { + "$ref": "#/definitions/settingsCustomAdditionalProperties" + } + ] + } + }, + "stylesProperties": { + "type": "object", + "properties": { + "background": { + "description": "Background styles.", + "type": "object", + "properties": { + "backgroundImage": { + "description": "Sets the `background-image` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" }, + { + "type": "object", + "properties": { + "url": { + "description": "A URL to an image file, or a path to a file relative to the theme root directory, and prefixed with `file:`, e.g., 'file:./path/to/file.png'.", + "type": "string" + } + }, + "additionalProperties": false + } + ] + }, + "backgroundPosition": { + "description": "Sets the `background-position` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "backgroundRepeat": { + "description": "Sets the `background-repeat` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "backgroundSize": { + "description": "Sets the `background-size` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "backgroundAttachment": { + "description": "Sets the `background-attachment` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + }, + "border": { + "description": "Border styles.", + "type": "object", + "properties": { + "color": { + "description": "Sets the `border-color` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "radius": { + "description": "Sets the `border-radius` CSS property.", + "anyOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" }, + { + "type": "object", + "properties": { + "topLeft": { + "description": "Sets the `border-top-left-radius` CSS property.", + "oneOf": [ + { "type": "string" }, + { + "$ref": "#/definitions/refComplete" + } + ] + }, + "topRight": { + "description": "Sets the `border-top-right-radius` CSS property.", + "oneOf": [ + { "type": "string" }, + { + "$ref": "#/definitions/refComplete" + } + ] + }, + "bottomLeft": { + "description": "Sets the `border-bottom-left-radius` CSS property.", + "oneOf": [ + { "type": "string" }, + { + "$ref": "#/definitions/refComplete" + } + ] + }, + "bottomRight": { + "description": "Sets the `border-bottom-right-radius` CSS property.", + "oneOf": [ + { "type": "string" }, + { + "$ref": "#/definitions/refComplete" + } + ] + } + } + } + ] + }, + "style": { + "description": "Sets the `border-style` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "width": { + "description": "Sets the `border-width` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "top": { + "type": "object", + "properties": { + "color": { + "description": "Sets the `border-top-color` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "style": { + "description": "Sets the `border-top-style` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "width": { + "description": "Sets the `border-top-width` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + }, + "right": { + "type": "object", + "properties": { + "color": { + "description": "Sets the `border-right-color` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "style": { + "description": "Sets the `border-right-style` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "width": { + "description": "Sets the `border-right-width` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + }, + "bottom": { + "type": "object", + "properties": { + "color": { + "description": "Sets the `border-bottom-color` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "style": { + "description": "Sets the `border-bottom-style` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "width": { + "description": "Sets the `border-bottom-width` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + }, + "left": { + "type": "object", + "properties": { + "color": { + "description": "Sets the `border-left-color` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "style": { + "description": "Sets the `border-left-style` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "width": { + "description": "Sets the `border-left-width` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "color": { + "description": "Color styles.", + "type": "object", + "properties": { + "background": { + "description": "Sets the `background-color` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "gradient": { + "description": "Sets the `background` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "text": { + "description": "Sets the `color` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + }, + "css": { + "description": "Sets custom CSS to apply styling not covered by other theme.json properties.", + "type": "string" + }, + "dimensions": { + "description": "Dimensions styles.", + "type": "object", + "properties": { + "aspectRatio": { + "description": "Sets the `aspect-ratio` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "minHeight": { + "description": "Sets the `min-height` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + } + }, + "filter": { + "description": "CSS and SVG filter styles.", + "type": "object", + "properties": { + "duotone": { + "description": "Sets the duotone filter.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + }, + "outline": { + "description": "Outline styles.", + "type": "object", + "properties": { + "color": { + "description": "Sets the `outline-color` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "offset": { + "description": "Sets the `outline-offset` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "style": { + "description": "Sets the `outline-style` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "width": { + "description": "Sets the `outline-width` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + }, + "shadow": { + "description": "Box shadow styles.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "spacing": { + "description": "Spacing styles.", + "type": "object", + "properties": { + "blockGap": { + "description": "Sets the `--wp--style--block-gap` CSS custom property when settings.spacing.blockGap is true.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "margin": { + "description": "Margin styles.", + "type": "object", + "properties": { + "top": { + "description": "Sets the `margin-top` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "right": { + "description": "Sets the `margin-right` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "bottom": { + "description": "Sets the `margin-bottom` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "left": { + "description": "Sets the `margin-left` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + }, + "padding": { + "description": "Padding styles.", + "type": "object", + "properties": { + "top": { + "description": "Sets the `padding-top` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "right": { + "description": "Sets the `padding-right` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "bottom": { + "description": "Sets the `padding-bottom` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "left": { + "description": "Sets the `padding-left` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false + }, + "typography": { + "description": "Typography styles.", + "type": "object", + "properties": { + "fontFamily": { + "description": "Sets the `font-family` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "fontSize": { + "description": "Sets the `font-size` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "fontStyle": { + "description": "Sets the `font-style` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "fontWeight": { + "description": "Sets the `font-weight` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "letterSpacing": { + "description": "Sets the `letter-spacing` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "lineHeight": { + "description": "Sets the `line-height` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "textAlign": { + "description": "Sets the `text-align` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "textColumns": { + "description": "Sets the `column-count` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "textDecoration": { + "description": "Sets the `text-decoration` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "writingMode": { + "description": "Sets the `writing-mode` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + }, + "textTransform": { + "description": "Sets the `text-transform` CSS property.", + "oneOf": [ + { "type": "string" }, + { "$ref": "#/definitions/refComplete" } + ] + } + }, + "additionalProperties": false + } + } + }, + "stylesPropertyNames": { + "enum": [ + "background", + "border", + "color", + "css", + "dimensions", + "filter", + "outline", + "shadow", + "spacing", + "typography" + ] + }, + "stylesPropertiesComplete": { + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "type": "object", + "propertyNames": { + "$ref": "#/definitions/stylesPropertyNames" + } + } + ] + }, + "stylesElementsPseudoSelectorsProperties": { + "type": "object", + "properties": { + ":active": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + ":any-link": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + ":focus": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + ":hover": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + ":link": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + ":visited": { + "$ref": "#/definitions/stylesPropertiesComplete" + } + } + }, + "stylesElementsPseudoSelectorsPropertyNames": { + "enum": [ + ":active", + ":any-link", + ":focus", + ":hover", + ":link", + ":visited" + ] + }, + "stylesElementsPropertiesComplete": { + "description": "Styles defined on a per-element basis using the element's selector.", + "type": "object", + "properties": { + "button": { + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "$ref": "#/definitions/stylesElementsPseudoSelectorsProperties" + }, + { + "type": "object", + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "$ref": "#/definitions/stylesElementsPseudoSelectorsPropertyNames" + } + ] + } + } + ] + }, + "link": { + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "$ref": "#/definitions/stylesElementsPseudoSelectorsProperties" + }, + { + "type": "object", + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "$ref": "#/definitions/stylesElementsPseudoSelectorsPropertyNames" + } + ] + } + } + ] + }, + "heading": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + "h1": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + "h2": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + "h3": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + "h4": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + "h5": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + "h6": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + "caption": { + "$ref": "#/definitions/stylesPropertiesComplete" + }, + "cite": { + "$ref": "#/definitions/stylesPropertiesComplete" + } + }, + "additionalProperties": false + }, + "stylesBlocksPropertiesComplete": { + "description": "Styles defined on a per-block basis using the block's selector.", + "type": "object", + "properties": { + "core/archives": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/audio": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/avatar": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/block": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/button": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/buttons": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/calendar": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/categories": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/code": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/column": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/columns": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comment-author-avatar": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comment-author-name": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comment-content": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comment-date": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comment-edit-link": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comment-reply-link": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comments": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comments-pagination": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comments-pagination-next": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comments-pagination-numbers": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comments-pagination-previous": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comments-title": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/comment-template": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/cover": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/details": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/embed": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/file": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/freeform": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/gallery": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/group": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/heading": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/home-link": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/html": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/image": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/latest-comments": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/latest-posts": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/list": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/list-item": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/loginout": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/media-text": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/missing": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/more": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/navigation": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/navigation-link": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/navigation-submenu": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/nextpage": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/page-list": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/page-list-item": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/paragraph": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-author": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-author-biography": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-author-name": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-comment": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-comments-count": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-comments-form": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-comments-link": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-content": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-date": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-excerpt": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-featured-image": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-navigation-link": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-template": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-terms": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-time-to-read": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/post-title": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/preformatted": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/pullquote": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/query": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/query-no-results": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/query-pagination": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/query-pagination-next": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/query-pagination-numbers": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/query-pagination-previous": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/query-title": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/quote": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/read-more": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/rss": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/search": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/separator": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/shortcode": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/site-logo": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/site-tagline": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/site-title": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/social-link": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/social-links": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/spacer": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/table": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/table-of-contents": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/tag-cloud": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/template-part": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/term-description": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/text-columns": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/verse": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/video": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/widget-area": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/legacy-widget": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + }, + "core/widget-group": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + } + }, + "patternProperties": { + "^[a-z][a-z0-9-]*/[a-z][a-z0-9-]*$": { + "$ref": "#/definitions/stylesPropertiesAndElementsComplete" + } + }, + "additionalProperties": false + }, + "stylesPropertiesAndElementsComplete": { + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "type": "object", + "properties": { + "elements": { + "$ref": "#/definitions/stylesElementsPropertiesComplete" + }, + "variations": { + "$ref": "#/definitions/stylesVariationsPropertiesComplete" + } + } + }, + { + "type": "object", + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements", "variations" ] + } + ] + } + } + ] + }, + "stylesVariationsProperties": { + "type": "object", + "patternProperties": { + "^[a-z][a-z0-9-]*$": { + "$ref": "#/definitions/stylesVariationProperties" + } + } + }, + "stylesVariationProperties": { + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "type": "object", + "properties": { + "elements": { + "$ref": "#/definitions/stylesElementsPropertiesComplete" + }, + "blocks": { + "$ref": "#/definitions/stylesVariationBlocksPropertiesComplete" + } + } + }, + { + "type": "object", + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements", "blocks" ] + } + ] + } + } + ] + }, + "stylesVariationsPropertiesComplete": { + "type": "object", + "patternProperties": { + "^[a-z][a-z0-9-]*$": { + "$ref": "#/definitions/stylesVariationPropertiesComplete" + } + } + }, + "stylesVariationPropertiesComplete": { + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "type": "object", + "properties": { + "elements": { + "$ref": "#/definitions/stylesElementsPropertiesComplete" + }, + "blocks": { + "$ref": "#/definitions/stylesVariationBlocksPropertiesComplete" + } + } + }, + { + "type": "object", + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements", "blocks" ] + } + ] + } + } + ] + }, + "stylesVariationBlocksPropertiesComplete": { + "type": "object", + "properties": { + "core/archives": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/audio": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/avatar": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/block": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/button": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/buttons": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/calendar": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/categories": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/code": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/column": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/columns": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comment-author-avatar": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comment-author-name": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comment-content": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comment-date": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comment-edit-link": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comment-reply-link": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comments": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comments-pagination": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comments-pagination-next": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comments-pagination-numbers": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comments-pagination-previous": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comments-title": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/comment-template": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/cover": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/details": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/embed": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/file": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/freeform": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/gallery": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/group": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/heading": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/home-link": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/html": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/image": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/latest-comments": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/latest-posts": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/list": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/list-item": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/loginout": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/media-text": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/missing": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/more": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/navigation": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/navigation-link": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/navigation-submenu": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/nextpage": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/page-list": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/page-list-item": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/paragraph": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-author": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-author-biography": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-author-name": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-comment": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-comments-count": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-comments-form": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-comments-link": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-content": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-date": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-excerpt": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-featured-image": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-navigation-link": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-template": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-terms": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-time-to-read": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/post-title": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/preformatted": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/pullquote": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/query": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/query-no-results": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/query-pagination": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/query-pagination-next": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/query-pagination-numbers": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/query-pagination-previous": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/query-title": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/quote": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/read-more": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/rss": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/search": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/separator": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/shortcode": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/site-logo": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/site-tagline": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/site-title": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/social-link": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/social-links": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/spacer": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/table": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/table-of-contents": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/tag-cloud": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/template-part": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/term-description": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/text-columns": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/verse": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/video": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/widget-area": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/legacy-widget": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + }, + "core/widget-group": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + } + }, + "patternProperties": { + "^[a-z][a-z0-9-]*/[a-z][a-z0-9-]*$": { + "$ref": "#/definitions/stylesVariationBlockPropertiesComplete" + } + }, + "additionalProperties": false + }, + "stylesVariationBlockPropertiesComplete": { + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "type": "object", + "properties": { + "elements": { + "$ref": "#/definitions/stylesElementsPropertiesComplete" + } + } + }, + { + "type": "object", + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements" ] + } + ] + } + } + ] + } + }, + "type": "object", + "properties": { + "$schema": { + "description": "JSON schema URI for theme.json.", + "type": "string" + }, + "version": { + "description": "Version of theme.json to use.", + "type": "integer", + "const": 3 + }, + "title": { + "description": "Title of the global styles variation. If not defined, the file name will be used.", + "type": "string" + }, + "slug": { + "description": "Slug of the global styles variation. If not defined, the kebab-case title will be used.", + "type": "string" + }, + "description": { + "description": "Description of the global styles variation.", + "type": "string" + }, + "blockTypes": { + "description": "List of block types that can use the block style variation this theme.json file represents.", + "type": "array", + "items": { + "type": "string" + } + }, + "settings": { + "description": "Settings for the block editor and individual blocks. These include things like:\n- Which customization options should be available to the user. \n- The default colors, font sizes... available to the user. \n- CSS custom properties and class names used in styles.\n- And the default layout of the editor (widths and available alignments).", + "allOf": [ + { + "$ref": "#/definitions/settingsProperties" + }, + { + "type": "object", + "properties": { + "useRootPaddingAwareAlignments": { + "description": "Enables root padding (the values from `styles.spacing.padding`) to be applied to the contents of full-width blocks instead of the root block.\n\nPlease note that when using this setting, `styles.spacing.padding` should always be set as an object with `top`, `right`, `bottom`, `left` values declared separately.", + "type": "boolean", + "default": false + }, + "blocks": { + "$ref": "#/definitions/settingsBlocksPropertiesComplete" + } + } + }, + { + "type": "object", + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/settingsPropertyNames" + }, + { + "enum": [ + "useRootPaddingAwareAlignments", + "blocks" + ] + } + ] + } + } + ] + }, + "styles": { + "description": "Organized way to set CSS properties. Styles in the top-level will be added in the `body` selector.", + "allOf": [ + { + "$ref": "#/definitions/stylesProperties" + }, + { + "type": "object", + "properties": { + "elements": { + "$ref": "#/definitions/stylesElementsPropertiesComplete" + }, + "blocks": { + "$ref": "#/definitions/stylesBlocksPropertiesComplete" + }, + "variations": { + "$ref": "#/definitions/stylesVariationsProperties" + } + } + }, + { + "type": "object", + "propertyNames": { + "anyOf": [ + { + "$ref": "#/definitions/stylesPropertyNames" + }, + { + "enum": [ "elements", "blocks", "variations" ] + } + ] + } + } + ] + }, + "customTemplates": { + "description": "Additional metadata for custom templates defined in the templates folder.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Filename, without extension, of the template in the templates folder.", + "type": "string" + }, + "title": { + "description": "Title of the template, translatable.", + "type": "string" + }, + "postTypes": { + "description": "List of post types that can use this custom template.", + "type": "array", + "items": { + "type": "string" + }, + "default": [ "page" ] + } + }, + "required": [ "name", "title" ], + "additionalProperties": false + } + }, + "templateParts": { + "description": "Additional metadata for template parts defined in the parts folder.", + "type": "array", + "items": { + "type": "object", + "properties": { + "name": { + "description": "Filename, without extension, of the template in the parts folder.", + "type": "string" + }, + "title": { + "description": "Title of the template, translatable.", + "type": "string" + }, + "area": { + "description": "The area the template part is used for. Block variations for `header` and `footer` values exist and will be used when the area is set to one of those.", + "type": "string", + "default": "uncategorized" + } + }, + "required": [ "name" ], + "additionalProperties": false + } + }, + "patterns": { + "description": "An array of pattern slugs to be registered from the Pattern Directory.", + "type": "array", + "items": { + "type": "string" + } + } + }, + "required": [ "version" ], + "additionalProperties": false +} diff --git a/apps/cli/ai/html-to-blocks-engine/theme/validate.mjs b/apps/cli/ai/html-to-blocks-engine/theme/validate.mjs new file mode 100644 index 0000000000..235daa8d3e --- /dev/null +++ b/apps/cli/ai/html-to-blocks-engine/theme/validate.mjs @@ -0,0 +1,157 @@ +// tools/theme/validate.mjs +import fs from 'node:fs'; +import path from 'node:path'; +import { createRequire } from 'node:module'; +import { fileURLToPath } from 'node:url'; +import { resolvePath, readJson, readIfExists, writeJson } from '../lib/workspace.mjs'; +import { ensureBlocksRegistered, loadWordPressBlocks } from '../lib/wp-serialize.mjs'; +import { fixBlockMarkup } from '../lib/fix-markup.mjs'; + +const require = createRequire(import.meta.url); + +export function validateBlockTheme(args) { + const workspaceRoot = resolvePath(args.workspaceRoot); + const themeDir = path.join(workspaceRoot, 'theme', args.slug); + const errors = []; + const must = (cond, msg) => { if (!cond) errors.push(msg); }; + + // required files + headers + must(fs.existsSync(path.join(themeDir, 'templates/index.html')), 'templates/index.html is missing'); + const styleCss = readIfExists(path.join(themeDir, 'style.css')) || ''; + for (const field of ['Theme Name', 'Version', 'Text Domain', 'Requires at least']) { + must(new RegExp(`${field}:\\s*\\S`).test(styleCss), `style.css header missing ${field}`); + } + const textDomain = (styleCss.match(/Text Domain:\s*(\S+)/) || [])[1]; + must(textDomain === args.slug, `style.css Text Domain (${textDomain}) must equal slug (${args.slug})`); + + // theme.json schema + const themeJsonPath = path.join(themeDir, 'theme.json'); + let themeJson = null; + if (fs.existsSync(themeJsonPath)) { + themeJson = readJson(themeJsonPath); + must(themeJson.version === 3, `theme.json version must be 3, got ${themeJson.version}`); + const Ajv = require('ajv'); + const ajv = new Ajv({ strict: false, allErrors: true }); + // fileURLToPath decodes percent-encoding (URL.pathname keeps "%20" for + // spaces, which breaks checkouts living under paths with spaces). + const schema = readJson(fileURLToPath(new URL('./theme-json-schema.json', import.meta.url))); + delete schema.$schema; // ajv8 rejects draft-04 marker; structure still validates + if (!ajv.validate(schema, themeJson)) { + for (const e of ajv.errors.slice(0, 20)) errors.push(`theme.json schema: ${e.instancePath} ${e.message}`); + } + } else { + errors.push('theme.json is missing'); + } + + // parse every template and part with core + plugin blocks registered + ensureBlocksRegistered(workspaceRoot, { blocksDir: path.join(workspaceRoot, 'theme-plugin', `${args.slug}-blocks`, 'blocks') }); + const wpBlocks = loadWordPressBlocks(); + // Grammar-level parser preserves unknown block names; wpBlocks.parse() would + // rewrite them to core/missing and hide the violation. + const { parse: parseGrammar } = require('@wordpress/block-serialization-default-parser'); + const known = new Set(wpBlocks.getBlockTypes().map((b) => b.name)); + for (const sub of ['templates', 'parts']) { + const dir = path.join(themeDir, sub); + if (!fs.existsSync(dir)) continue; + for (const file of fs.readdirSync(dir).filter((f) => f.endsWith('.html'))) { + const rel = `${sub}/${file}`; + const text = readIfExists(path.join(dir, file)); + const parsed = parseGrammar(text); + walkParsed(parsed, (b) => { + if (b.blockName === null && b.innerHTML.trim() !== '') errors.push(`${rel}: contains freeform/unparsed content`); + if (b.blockName && !known.has(b.blockName)) errors.push(`${rel}: unknown block ${b.blockName}`); + }); + // canonicality: parse() self-heals drifted markup through block + // deprecations (so isValid stays true), but the file on disk + // still fails editor validation. The reliable signal is the + // round trip: regenerated markup must match the file. + const roundTrip = fixBlockMarkup(text); + const normalizeGaps = (s) => s.replace(/\n+/g, '\n').trim(); + if (normalizeGaps(roundTrip.markup) !== normalizeGaps(text)) { + errors.push(`${rel}: invalid block markup (drifts from save() output) — run fix_block_markup`); + } + if (sub === 'templates' && file !== '404.html') { + const isContentful = /wp:post-content|wp:query/.test(text); + must(isContentful, `${rel}: template renders no content (needs post-content or a query loop)`); + } + } + } + + // templateParts <-> files reconcile + refs resolve + const partFiles = fs.existsSync(path.join(themeDir, 'parts')) + ? fs.readdirSync(path.join(themeDir, 'parts')).filter((f) => f.endsWith('.html')).map((f) => f.replace(/\.html$/, '')) : []; + for (const tp of themeJson?.templateParts || []) { + must(partFiles.includes(tp.name), `theme.json templatePart ${tp.name} has no parts/${tp.name}.html`); + } + for (const file of fs.existsSync(path.join(themeDir, 'templates')) ? fs.readdirSync(path.join(themeDir, 'templates')) : []) { + const text = readIfExists(path.join(themeDir, 'templates', file)); + // The grammar parser handles nested attr objects; a regex over the + // comment would truncate at the first "}" inside style attrs. + walkParsed(parseGrammar(text), (b) => { + if (b.blockName !== 'core/template-part') return; + const slugRef = b.attrs?.slug; + must(slugRef !== undefined && partFiles.includes(slugRef), `templates/${file}: unresolved template-part ref ${slugRef}`); + }); + } + + // fonts + no remote urls + for (const fam of themeJson?.settings?.typography?.fontFamilies || []) { + for (const face of fam.fontFace || []) { + for (const src of face.src || []) { + const rel = src.replace(/^file:\.\//, ''); + must(fs.existsSync(path.join(themeDir, rel)), `fontFace src missing on disk: ${src}`); + } + } + } + const themeTexts = [['style.css', styleCss], ['theme.json', JSON.stringify(themeJson)]]; + for (const [label, text] of themeTexts) { + // www.w3.org appears as the xmlns of inline data-URI SVGs — a namespace + // identifier, never fetched + const remotes = (text.match(/https?:\/\/[^"')\s]+/g) || []).filter((u) => !u.includes('schemas.wp.org') && !u.includes('gnu.org') && !u.includes('www.w3.org')); + for (const u of remotes) errors.push(`${label}: remote url ${u}`); + } + + // content plugin payload checks + const contentDir = path.join(workspaceRoot, 'theme-plugin', `${args.slug}-content`, 'content'); + if (fs.existsSync(contentDir)) { + const manifest = readJson(path.join(contentDir, 'manifest.json')); + for (const page of manifest.pages) { + const payloadPath = path.join(contentDir, `${page.slug}.html`); + must(fs.existsSync(payloadPath), `content payload missing for ${page.slug}`); + const payload = readIfExists(payloadPath); + if (payload) { + must(!/href="[^"]*\.html/.test(payload), `content/${page.slug}.html: internal .html link survived permalink rewrite`); + for (const u of remotePayloadAssetUrls(payload)) { + errors.push(`content/${page.slug}.html: remote asset url ${u} (use {{THEME_URI}})`); + } + } + } + const pluginPhp = readIfExists(path.join(workspaceRoot, 'theme-plugin', `${args.slug}-content`, `${args.slug}-content.php`)) || ''; + must(/Requires Plugins:\s*\S/.test(pluginPhp) || !fs.existsSync(path.join(workspaceRoot, 'theme-plugin', `${args.slug}-blocks`)), 'content plugin missing Requires Plugins header'); + } + + const report = { generatedAt: new Date().toISOString(), themeDir, errors, passed: errors.length === 0 }; + if (args.write !== false) writeJson(path.join(workspaceRoot, 'reports/theme-validation.json'), report); + return report; +} + +function walkParsed(blocks, fn) { + for (const b of blocks || []) { fn(b); walkParsed(b.innerBlocks, fn); } +} + +function remotePayloadAssetUrls(markup) { + const urls = []; + for (const match of markup.matchAll(/\b(?:src|poster)\s*=\s*["'](https?:\/\/[^"']+)/gi)) { + urls.push(match[1]); + } + for (const match of markup.matchAll(/\bsrcset\s*=\s*["']([^"']+)/gi)) { + for (const candidate of match[1].split(',')) { + const url = candidate.trim().split(/\s+/)[0]; + if (/^https?:\/\//i.test(url)) urls.push(url); + } + } + for (const match of markup.matchAll(/url\(\s*["']?(https?:\/\/[^"')\s]+)/gi)) { + urls.push(match[1]); + } + return urls; +} diff --git a/apps/cli/ai/skills/blocks-to-theme/SKILL.md b/apps/cli/ai/skills/blocks-to-theme/SKILL.md new file mode 100644 index 0000000000..199bc4ed32 --- /dev/null +++ b/apps/cli/ai/skills/blocks-to-theme/SKILL.md @@ -0,0 +1,130 @@ +--- +name: blocks-to-theme +description: Turn a completed html-to-blocks workspace (single or multi page) into an installable WordPress block theme, verify it in WordPress Playground, then install and activate it in a real Studio site. Stage 2 of the design-to-theme pipeline. Extracts theme.json from style evidence, infers template parts from cross-page repetition, bundles fonts and media, and gates on validation plus pixel-and-editor comparison. +user-invokable: true +--- + +# Blocks To Theme + +Run this skill on a COMPLETED `html-to-blocks` workspace — one whose comparison gates already passed for every page. The engine tools gather evidence and verify; you make the design decisions. The final deliverable is an installed, activated block theme in a real Studio site. + +Every engine tool here takes the same `workspaceRoot` the stage-1 run used. Pass it to every call. + +## Required Workflow + +1. Run `analyze_theme_evidence`; read `reports/theme-evidence.json`. +2. Run `infer_template_parts`; read `reports/template-parts.json`. +3. Write `plan/theme-plan.md` containing: the **token map** (value to preset slug), the **lift ledger** (every residual CSS rule with its reason category), the **parts decision** for every evidence group (unify / variant parts / leave in content, each with its cited group), the **template plan**, the **page manifest** (slugs, titles, front page), and the **media map** (source path to destination). +4. Run `fetch_theme_fonts` (see Fonts and Media). +5. Run `scaffold_block_theme` with the plan's decisions as data. +6. Run `validate_block_theme`; fix and re-scaffold until `errors` is empty. +7. Run `playground_render`; repair until every page passes both viewports AND every page's editor validation has zero failures (see Playground Gate). +8. Install into the Studio site and activate (see Install Into Studio). + +## Hard Gates + +### Evidence Gate + +No template part without a cited occurrence group from `reports/template-parts.json`. The standing template set is `index.html` plus the generic defaults `archive.html`, `single.html`, `404.html` — these need no evidence; the scaffold composes them from the inferred chrome plus plain core blocks (query loop, post title/date/content, not-found message) styled entirely by global styles. Any template beyond that set needs a cited difference in chrome variants or the front-page designation. **Single-page runs normally produce zero parts** — parts exist to share chrome across pages, and one page has no repetition evidence. + +### Lift-First Gate + +Every rule remaining in theme `style.css` or any `styles.blocks[...].css` carries a reason category in the lift ledger. A rule with no category MUST lift into theme.json. Do not solve fidelity by dumping the workspace stylesheet into the theme. + +### Completion Gate + +The run is complete only when `validate_block_theme` reports zero errors AND `reports/theme-comparison.json` shows every page within thresholds (`maxMismatchPercent <= 1`, `maxHeightDelta <= 8`) at both viewports AND every page's `editorValidation.failures` is zero AND the theme is installed and activated in the Studio site. Quote the validation `passed` flag and the comparison aggregates in the final response. Otherwise keep repairing or report the run blocked with the metrics and the blocking cause. + +## The Lifting Ladder + +For every recurring style fact in the evidence report, try each rung in order and stop at the first that fits. Lower rungs are more editable and inspectable; `style.css` is the last resort. + +1. **Presets** (`settings.color.palette`, `.typography.fontSizes`, `.spacing.spacingSizes`, `.custom`): recurring colors, font sizes, spacing values, custom properties become named tokens. +2. **Root styles** (`styles.color`/`.typography`/`.spacing`): the page's base background/text/font set on `body`/`:root`. +3. **Element styles** (`styles.elements.heading`/`.link`/`.button`/...): rules targeting plain elements site-wide. +4. **Block styles** (`styles.blocks["core/..."]`): rules consistently targeting one block type. +5. **Block style variations**: a named alternative look applied to some instances of a block type. +6. **Per-block CSS** (`styles.blocks["core/..."].css`): a scoped escape hatch for one block type with no structured property. +7. **Theme `style.css`**: only rules carrying a reason category below. + +## The Six Lift-Reason Categories + +A rule may remain in `style.css` (or any `styles.blocks[...].css`) only if the ledger tags it with at least one of: + +- `media-query` — the rule lives inside `@media` (theme.json has no responsive conditions). +- `pseudo` — `::before`/`::after` content and decoration. +- `position` — `position: fixed | absolute | sticky` layering. +- `blend` — `mix-blend-mode`, `filter`, `backdrop-filter`. +- `grid` — `display: grid` and `grid-*` (no block support models arbitrary grids). +- `interaction` — `:hover`/`:focus`/`:active`/`:checked`, transitions, animations, `@keyframes`. +- `selector` — the rule targets an arbitrary class composition (`.hero__copy`, `.tier li`) that theme.json structurally cannot address. This category is JUDGED, not tool-assigned: it never applies to rules on `body`, bare element selectors, or `.wp-block-*` roots — those always lift to root styles, `styles.elements`, or `styles.blocks`. + +The first six match the `cssRules[].buckets` `analyze_theme_evidence` assigns. A rule shown with an empty `buckets` array is either liftable (lift it) or a `selector` case (justify it in the ledger). + +## The Two Mechanical Rewrites + +You declare the token map; `scaffold_block_theme` performs these: + +1. **Preset refs in block trees.** Any tree attribute value that exactly matches a token-map entry is replaced by the preset ref. With `"#0b0b0b" -> "ink"`, `"style":{"color":{"background":"#0B0B0B"}}` becomes `"backgroundColor":"ink"`. Font sizes become `"fontSize":""`; spacing becomes `"var:preset|spacing|"`. +2. **Custom-property renames in CSS.** With `"--pad" -> "pad"`, every `var(--pad)` in residual CSS becomes `var(--wp--custom--pad)` and the `--pad` definition drops from `:root` (WordPress emits it from `settings.custom.pad`). + +Only EXACT value matches rewrite (trimmed, lowercased). Never map "close" values — `#0b0b0b` and `#0c0c0c` are two tokens or one stays raw. `clamp()` values go into presets verbatim; do not flatten fluid sizes. Name presets after the source custom properties (`--bone` to slug `bone`); a nameless value gets a descriptive slug, never `color-1`. WordPress camelCases `settings.custom` keys when emitting variables — prefer lowercase single words (`pad`, `ease`, `line`); for multi-word source props pick one convention and verify the emitted variable name matches what the rewritten CSS references. + +## Template Part Inference + +`infer_template_parts` groups every top-level subtree across all pages by an exact hash (block names + all attribute values + inner blocks) and a structural hash (block names, sorted className lists, sorted non-content attribute keys). A group is `exact` when all occurrences share one exact hash, `structural` when shapes match but values differ; structural groups carry a `variance` table. + +For every structural group, record one decision: + +1. **Unify** — emit one shared part from the cleanest occurrence. Right when variance is invisible (ordering artifacts, redundant attributes, anchors that normalize after permalink rewriting). +2. **Variant parts** — emit one part per page, named `-` (`nav-home`, `nav-judges`). Right when variance is visible per-page chrome state (an `is-current` marker, a per-page footer column). Each page's template references its own variant. +3. **Leave in content** — no part; the subtree ships inside the page's content payload. Right when differences are copy in otherwise-unique sections. + +Choose names and `area` from the occurrence evidence: `first: true` everywhere + `tagName: header`/nav block to a role name (`nav`, `site-nav`) with `area: "header"`; `last: true` everywhere + `tagName: footer` to `area: "footer"`; otherwise `area: "uncategorized"`. Read the variance tables before deciding — an attr path either appears or it does not; "looks the same to me" is not a decision input. + +## Template Planning + +The theme ships exactly the standing set by default: `index.html` (inferred chrome + `post-content`) plus the generic `archive.html`/`single.html`/`404.html`, which the scaffold builds automatically by wrapping plain core blocks with index's split top/bottom chrome. A `page-{slug}` template is allowed ONLY when that page needs a different chrome variant set than index, cited from `reports/template-parts.json`. `front-page.html` only when the front page's chrome differs from what would otherwise serve it; prefer the manifest's `front` flag and a page-assigned `template` first. + +A template is template-part references (chrome) plus `` (and the plain core blocks in the generic defaults). Page copy — heroes, sections, lists — lives in imported pages as editable content. Never bake a page's sections into its template. The generic defaults have no mockup, so `playground_render` does not screenshot them; `validate_block_theme` covers them. Do not spend repair cycles styling them beyond global styles. + +## Fonts and Media + +`fetch_theme_fonts` finds the Google Fonts `@import` in the mockup CSS, requests it with a modern Chrome UA so Google serves woff2, downloads each face into `assets/fonts/`, and returns `fontFamilies` entries with `src` values of `file:./assets/fonts/`. Pass them to `scaffold_block_theme` as `fontFamilies`; do not hand-write fontFace entries. If the fetch or any download fails, the run is BLOCKED — never ship the remote `@import` in theme CSS. Report blocked with the fetch error. + +**Zero remote URLs.** `validate_block_theme` fails on any `http(s)://` URL in `style.css`, `theme.json` (schema/license excepted), or any content payload, and on any internal `*.html` link that survived permalink rewriting. The fix is always to bundle the asset, never to allowlist the URL. + +**Media scheme:** inventory every image/media reference from the block trees and a CSS `url()` scan; the media map lists each source with its `assets/media/` destination. `scaffold_block_theme` copies them. Content payloads use `{{THEME_URI}}/assets/media/...` placeholders that the content plugin resolves to `get_stylesheet_directory_uri()` at import. Theme CSS uses relative paths (the placeholder does not resolve in a static stylesheet); templates and parts keep the placeholder, resolved by a `render_block` filter. + +## Editor-Runtime Attribute Constraints + +- **No raw `var(--…)` in style attribute values** (spacing, color, etc.). The browser save escapes `--` inside `style` attributes as `u002du002d` (kses), so such markup can never validate in the editor. Tokens in attribute values must become presets (`var:preset|spacing|`), which are exempt. Raw `var()` is fine in CSS files — only attribute values are affected. +- **`wp_slash` PHP-inserted block JSON.** Block attributes containing `&`, `<`, `>`, `"`, or `--` are escaped as `\uXXXX` in the serialized comment JSON. Anything inserting that markup through PHP must `wp_slash()` it first — `wp_insert_post` unslashes input and corrupts the escapes. The generated content plugin does this; any custom import path must too. + +## Playground Gate + +`playground_render` boots a real WordPress via `@wp-playground/cli`, mounting the theme and plugins, activating them, calling the content plugin's `_import_pages()` (the same path the admin "Import pages" button uses), then screenshotting each page logged-out at `/?pagename=` (and `/` for the front page) through the same capture path stage 1 used. The first run downloads the WordPress build — needs network and a generous timeout; on failure the error includes the Playground log tail, so read it before assuming a visual problem. + +Expect NEW drift on the first run that the workspace preview never had: + +- **block-library CSS** — core block defaults (paragraph/heading margins, button/navigation styling, image figure spacing). +- **global-styles preset CSS** — the CSS WordPress generates from your theme.json can cascade differently. +- **layout supports** — `is-layout-constrained`/`is-layout-flow` add content-width centering and block-gap margins. A section that filled the width on workspace CSS alone can render with gutters; `width: 100%` cannot beat `.is-layout-constrained > *:not(.alignwide):not(.alignfull)`. The block carries width through `align` (full/constrained for centered inner content, full/default on outer and inner for edge-to-edge). Confirm the first section's computed width equals the viewport before assuming a CSS fix. + +**Where to fix what:** token/element drift (wrong color/size/spacing from a preset, heading/link styles) to **theme.json**. Width or block-gap drift from layout rules to the block's own `align`/`layout`/spacing — prefer setting it in the stage-1 tree and re-serializing so the source of truth stays consistent; `width`/`margin` in style.css loses to the cascade. Structural shims (killing an unwanted core margin, overriding a layout rule for one class) to **theme `style.css`** with a ledger category. **Never edit the content payload to absorb WordPress's CSS** — that dodges the diff and the next editor-created page would look wrong. Setting a block's own `align`/`layout`/spacing is the legitimate width fix (real design intent); free-form payload edits are only valid when the payload itself is wrong (bad media path, missed permalink rewrite). + +After frontend captures, the gate logs into wp-admin and opens every imported page in the block editor, collecting `Block validation failed` console messages — the editor recomputes `save()` in the browser and catches drift no Node round trip can see (kses `--` escaping, content-filter mangling of empty inline elements, unslashed comment-JSON escapes). The run fails on ANY such message; counts land under `editorValidation` in `reports/theme-comparison.json`. + +Load the `visual-fidelity` skill for the repair loop: inspect `-diff-*.png` even on passing pages (numbers hide localized drift), fix the worst page first, re-run `validate_block_theme` after any re-scaffold, then re-run `playground_render`. Passing thresholds is the only successful end state. + +## Install Into Studio + +This is the closing step and is mandatory — the deliverable is a working theme in a real site, not a verified directory. + +1. Identify the target site with `site_list` (create one with `site_create` if none exists). Start it with `site_start` if it is not running. +2. Copy the verified theme directory from the workspace into the site's `wp-content/themes//`. If you are starting from a fresh site and want Studio to own the scaffolding, you may instead use `scaffold_theme` and lay the verified files into the scaffolded directory. +3. Activate it: `wp_cli theme activate `. +4. If the content plugin is part of the deliverable, activate the plugins and run the import the same way the gate did (activate `-blocks` and `-content`, then trigger the import) so the pages exist in the live site. Validate any block content with the `validate_blocks` Studio tool. +5. Verify the live result with `take_screenshot` (`viewport: "all"` for desktop and mobile), in both light and dark color schemes. + +Only after the theme is installed, activated, and visually verified in the Studio site — with the validation and comparison gates quoted — is the run complete. diff --git a/apps/cli/ai/skills/html-to-blocks/SKILL.md b/apps/cli/ai/skills/html-to-blocks/SKILL.md new file mode 100644 index 0000000000..ff9e554ab6 --- /dev/null +++ b/apps/cli/ai/skills/html-to-blocks/SKILL.md @@ -0,0 +1,168 @@ +--- +name: html-to-blocks +description: Transform designed or provided HTML/CSS/JS into editable WordPress block content. Stage 1 of the design-to-theme pipeline. Creates a workspace, plans core-first blocks, builds a data-only block tree, generates vanilla-JS custom blocks only when core cannot preserve fidelity and editability, serializes, and repairs until rendered and editor screenshots match the mockup. +user-invokable: true +--- + +# HTML To Blocks + +Use this skill when the user wants an HTML/CSS/JS design, or a directory of provided markup, transformed into editable WordPress blocks. You own the design judgment and code edits; the engine tools provide workspace setup, mockup analysis, custom-block scaffolding, preview wrapping, screenshot comparison, and DOM geometry measurement. This is stage 1; stage 2 is the `blocks-to-theme` skill, which turns a completed workspace into an installed theme. + +## The Workspace + +Every engine tool operates on a workspace directory, passed as `workspaceRoot`. Create it once under the active Studio site (e.g. `/tmp/h2b-/`) or a scratch path, and pass that same path to every engine tool for the whole run. + +- `create_workspace` scaffolds the directory. +- If the user has a Studio site already, find it with `site_list`; otherwise this stage does not need a running site (the install happens in stage 2). The workspace is just files. + +## Required Workflow + +1. Run `create_workspace`. +2. **Provided markup**: run `import_provided_markup` and treat the imported HTML/CSS as the source mockup. It copies the export into `mockup/`, bundles linked stylesheets into `mockup/style.css`, and preserves the HTML as the visual source of truth — do not redesign or simplify it before analysis. **No markup**: generate `mockup/index.html`, `mockup/style.css`, and optional `mockup/script.js` per the Design Stage below. Load the `visual-design` skill for art direction. +3. Run `analyze_mockup`; read `analysis/content-inventory.json`. +4. Write `plan/block-plan.md` and `plan/block-plan.json`. Complete the Core-First Gate (below) before writing any custom block. +5. Generate custom blocks only where core blocks cannot preserve both fidelity and editability. Scaffold each with `scaffold_custom_block`, then edit to match the mockup. Source lives in `wordpress/blocks//`. +6. Assemble editable content in `wordpress/block-tree.json` (data-only — see Assembly). Styling priority is strict: block support attributes first, custom-block scoped CSS second, small page CSS last. +7. Run `serialize_wordpress_blocks`. It registers core blocks via `@wordpress/block-library`, registers custom blocks from `wordpress/blocks/*/index.js`, serializes the tree, writes canonical markup to `wordpress/content.html`, frontend preview to `rendered/rendered-blocks.html`, a no-build editor preview to `editor/block-editor.html`, and `reports/style-audit.json`. Preview CSS comes from `wordpress/style.css` and custom-block `style.css` files; `mockup/style.css` is intentionally excluded from the rendered preview. +8. Use `create_block_editor_preview` to refresh or inspect an editor instance from a tree without reserializing everything. For multi-page runs, call it per page tree. +9. Use `screenshot_html` for inspection shots of mockup, rendered, editor, or arbitrary workspace HTML. +10. Run `compare_html`. +11. When a comparison fails, run `measure_layout` BEFORE staring at pixel diffs. It returns per-element top/height deltas between mockup and the rendered or editor page (`candidateKind: "editor"`), aligned by selector order. Drill from sections to children with narrower selectors until the drift names one element, then fix it. Pixel diffs localize; measurements identify. +12. Load the `visual-fidelity` skill and run its repair loop: inspect, write the repair-tasks file, fix each task, then repeat serialize/compare until both saved-frontend and editor-preview thresholds pass on every page. +13. Run `audit_standins` and read `reports/standins.json`. Confirm every data-driven region is a marked core-block stand-in (not a custom block), and that no stand-in references a postType/taxonomy the content model will not provide. Stand-ins stay static until `hydrate_standins` runs in stage 2. + +## Thresholds and Completion Gate + +Default thresholds: `maxMismatchPercent <= 1` and `maxHeightDelta <= 8`. + +A run is complete only when BOTH comparison aggregates pass for EVERY page: + +- `aggregates.rendered.maxMismatchPercent` and `aggregates.rendered.maxHeightDelta` within thresholds. +- `aggregates.editor.maxMismatchPercent` and `aggregates.editor.maxHeightDelta` within thresholds. + +Reports are per page: `reports/comparison.json` for the index, `reports/.comparison.json` for the rest. They never overwrite each other; a complete multi-page run leaves one passing report per page. Before the final response, read each report and state the rendered/editor metrics. Only say the run is done when every aggregate passes. Do not stop at "close", "structurally close", or "good enough". If concrete repairs cannot reduce the metrics, report the run blocked with the current metrics and the blocking cause — never present a failed comparison as a successful run. + +## Multi-Page Runs + +`import_provided_markup` detects sibling `.html` pages and returns a `pages` manifest (index/page/single/archive/404). Follow the manifest paths for every page, including the index, so the workspace stays symmetric: + +- Block tree: `wordpress/pages/.block-tree.json` +- Serialized markup: `wordpress/pages/.content.html` +- Frontend preview: `rendered/.html` +- Editor preview: `editor/.html` +- Comparison report: `reports/.comparison.json` + +Workflow: + +1. Import once; read the manifest. +2. Analyze and read EVERY page before planning. Identify shared chrome (header/nav/footer), shared components (cards, forms, teasers), and page-local sections. Plan custom blocks ONCE for the whole site; pages reuse them with different attributes. +3. Build and fully pass one page first — usually the index. It forces the shared blocks, design tokens, and editor parity into shape; later pages then converge in a few iterations each. Pass the index page first, then iterate the rest. +4. Page-specific CSS goes into clearly labelled sections of `wordpress/style.css` (or block CSS when component-scoped). Keep the shared token/base layer at the top. +5. Serialize and compare per page. The completion gate applies to every page; one failing page is an incomplete run. +6. Always compare both viewports per page. A missing margin can be invisible at desktop (a taller sibling column masks it) and surface only at mobile when columns stack. + +## Core-First Gate + +Before generating custom blocks or the block tree, write a core-first audit in `plan/block-plan.md`: + +- For every mockup section, list the candidate core block assembly first. +- For every chosen core block, list the native attributes/support props that will carry the visual styling before any CSS. +- Only then list any custom block, with the specific reason core blocks fail. +- A custom block that replaces a whole section is rejected unless the section is a real submission form (contact/newsletter/booking) or a genuinely bespoke interactive widget with a typed editing model that no core block expresses. Navigation, search, site identity, comments, pagination, post fields, and repeated content cards are NOT custom-block reasons — they are real core blocks or `core/query` stand-ins (see "The Serializer Is Not the Design"). +- Complex layout is not a sufficient reason for a custom block. Use `core/group`, `core/columns`/`core/column`, `core/heading`, `core/paragraph`, `core/buttons`/`core/button`, `core/list`/`core/list-item`, `core/details`, `core/image`, `core/cover`, `core/media-text`, `core/spacer`, `core/separator`, `core/quote`, `core/table`, and supports/classes first. +- The final tree should normally contain FAR more core blocks than custom blocks. If custom blocks approach the core-block count, treat the plan as failed unless the user explicitly asked for a mostly-custom site. +- If the editor preview drifts because WordPress wrappers, RichText sizing, placeholders, or chrome alter layout, fix the editor harness, custom-block `edit()`, or editor-scoped CSS. Do not replace core assemblies with custom section blocks to make editor comparison easier. + +## The Serializer Is Not the Design + +"It doesn't render in the static preview" is a HARNESS fact, never a design fact, and never a valid core-rejection reason. WordPress server-renders its dynamic blocks, and so does the pipeline (via `tools/lib/dynamic-render.mjs`), so they appear on both preview surfaces. Use the real core block and prepopulate it: + +- Navigation → `core/navigation` with `core/navigation-link`/`core/navigation-submenu` inner blocks. Never a custom "site-nav" block, never `core/buttons`. +- Search → `core/search` (label, placeholder, buttonText, buttonPosition). Never a custom "search-form". +- Site identity → `core/site-title`, `core/site-tagline`, `core/site-logo`. A text wordmark is `core/site-title` (+ tagline), not an image and not a custom block. +- Comments → `core/comments`; comment form → `core/post-comments-form`. +- Pagination → `core/query-pagination` (+ `-previous`/`-numbers`/`-next`); prev/next post → `core/post-navigation-link`. +- Images → `core/image`; missing media is a `data:` SVG placeholder at the right aspect ratio in `url`, never a URL hidden in InspectorControls. + +If a dynamic core block genuinely cannot preview after prepopulation, that is a renderer-shim gap to report (and fix in `dynamic-render.mjs`), not a licence to invent a custom block. Supply `wordpress/preview-context.json` (`{ "siteTitle", "siteLogoUrl", "homeUrl", "postDate", "postTerms" }`) so entity-backed blocks render with real-looking values on both surfaces. + +## Provided Markup Is Binding + +When the source export annotates intended blocks — ``, ``, a handoff table mapping components to blocks, a data file shaped like a CPT — those are requirements, not hints. Use the named block. Deviating needs an explicit design-level reason written in the plan, never "the preview is easier this way". + +## Stand-Ins for Data-Driven Regions + +Some regions are real queries/comments with no data yet (object/product grids, post indexes, comment threads). Build them as a static core-block composition seeded with representative content so the visual gate can style them, and MARK the region with `attrs.metadata.standin`: + +- repeating container → `{ "for": "core/query", "postType": "...", "taxonomy": "...", "query": { "perPage": N, "orderBy": "date", "order": "desc" } }`; the container's FIRST child is the item template. +- each per-item field in that template → `{ "for": "core/post-title" | "core/post-featured-image" | "core/post-terms" | "core/post-excerpt" | "core/post-date" }`. +- comment thread → `{ "for": "core/comments" }`. + +The card itself stays real core blocks (`core/group` + `core/image` + `core/heading` + `core/paragraph`) with stable classNames the CSS targets — never a custom "post-card" block, and never a `variant` enum encoding placement (featured/row/grid is layout, expressed by the container's class/CSS). Run `audit_standins` before completion; the content-modeling step in stage 2 runs `hydrate_standins` to swap these into `core/query`/`core/comments` against real seed content. + +## Core Block Selection + +Picking "a core block" is not enough; pick the one whose saved markup and editor behavior are closest to the source, or you create editor drift even when frontend CSS can be forced to match. + +- `core/cover` for media-backed sections, heroes, promos, overlay cards — text/buttons over an image/video with an overlay. Usually better editor parity than `core/group` + absolutely positioned `core/image`. But when the mockup "image" is a decorative CSS background (placeholder plates, tone classes), `core/group` carrying the mockup's own background classes beats `core/cover` on both surfaces — the media is not editable content. +- `core/media-text` for a two-part image/text split that should stack on mobile. Use its `mediaPosition`, `mediaWidth`, `isStackedOnMobile` before custom grid CSS. +- `core/columns`/`core/column` for ordinary responsive columns with native mobile collapse. But asymmetric grid tracks (e.g. `1.4fr 1fr 1fr`) are NOT column widths — use `core/group` with the mockup's grid CSS on a className. +- `core/group` for layout wrappers, section shells, stacked editorial bands, constrained containers, grid children. Not for media overlays, lists, forms, links, or arbitrary HTML. +- `core/buttons`/`core/button` for link/button groups, even when they need custom visual styling. +- `core/list`/`core/list-item` for real lists; never paragraphs with line breaks. +- `core/table` (cell arrays) and `core/quote` (inner paragraph) serialize cleanly and fit data tables and pull quotes. +- Dynamic core blocks (`core/navigation`, `core/search`, `core/site-title`/`-logo`, `core/comments`/`core/post-comments-form`, `core/query-pagination`, `core/post-navigation-link`, `core/post-date`/`-terms`) render in BOTH previews via the shim (`dynamic-render.mjs`) — use them directly and prepopulate them. Never avoid them because they "preview blank", and never substitute `core/buttons` or a custom block for navigation. + +Use native attributes before CSS: `core/group` `backgroundColor`/`textColor`/`gradient`/`style.*`/`layout`/`align`; `core/cover` `url`/`dimRatio`/`overlayColor`/`focalPoint`/`minHeight`/`contentPosition`; `core/image` `url`/`alt`/`aspectRatio`/`scale`; `core/button` `text`/`url`/`backgroundColor`/`textColor`/border/typography. + +**Preview-surface caveat:** the RENDERED preview loads only workspace CSS — no block-library CSS. Core blocks whose layout depends on library rules (`core/cover`'s absolute image, `core/columns` flex, `core/buttons` flex, `core/separator` defaults) render unstyled there unless `wordpress/style.css` shims those `wp-block-*` classes. The editor preview DOES load library CSS (in a low-priority cascade layer), so a library-dependent block can look right in the editor and broken in rendered. Either shim the classes or pick a block whose saved markup is self-sufficient. + +## Custom Blocks + +**Before writing one: is it actually custom?** Most custom-block instincts are a core block the static serializer happened to render blank — re-read the Core-First Gate and "The Serializer Is Not the Design". A custom block is justified only for a real submission form with no core equivalent, or a genuinely bespoke interactive widget. If you are about to build a navigation, search, comments, pagination, site-identity, card, or post-field block, stop and use the core block (or a marked `core/query` stand-in). + +Scope discipline for blocks that ARE custom: visible content (including media) lives in the canvas — an image is a `core/image` child or an in-canvas `MediaUpload`/`RichText` field, never a URL typed into InspectorControls. No placement-variant attribute (`variant: featured|row|grid` encodes where the block sits, which is the parent's layout/CSS job). An inline SVG icon never justifies a block — icons are CSS decoration (background or pseudo-element on a core composition). A custom block's attributes are a typed editing model, never a raw HTML blob. + +Use vanilla JavaScript with WordPress globals — no JSX, no build step. Scaffold with `scaffold_custom_block`, then edit. Use `wp.blocks.registerBlockType`, `wp.element.createElement`, `wp.blockEditor.useBlockProps`/`RichText`/`InspectorControls`/`BlockControls`, and `wp.components.*` only for settings. `block.json` uses `apiVersion: 3`; declare attributes with types/defaults and supports for spacing/color/typography/border/dimensions/align/anchor/className. + +- Visible copy belongs in the canvas via `RichText`, using the same `tagName`/className that `RichText.Content` uses in `save()`. Do not render `TextControl`/`TextareaControl` as primary in-canvas UI for visible content. +- Behavior and settings (URLs, method, required flags, speed, variants) go to `InspectorControls`/`BlockControls`. +- Forms, search boxes, subscriptions, booking, contact UI must save real semantic `
    ` markup, preserving labels, field names, input types, placeholders, required state, options, action, method, submit text. +- `save()` is the single source of frontend markup. `edit()` must be a visual twin: same root tag, class names, child order, repeated-item wrappers, geometry — with `RichText` or disabled controls replacing static text/inputs. Editor-only wrappers, helper labels, and disabled behavior must not change screenshot geometry. +- Root color/spacing/typography/border/min-height comes from supports-backed attributes in the tree, not hard-coded CSS. Use block `style.css` only for scoped internals supports cannot express. +- Do not use `dangerouslySetInnerHTML` or HTML-blob attributes. + +## Assembly + +The source of truth is `wordpress/block-tree.json`, not hand-written block comments or saved HTML. The tree is **data-only**: `blockName`, `attrs`, `innerBlocks`, block styles, support-like attributes, classes. `serialize_wordpress_blocks` turns that data into canonical markup via WordPress serialization and each block's `save()`. + +- Never put `htmlLines`, `innerHTML`, `innerContent`, `html`, `markup`, or `sourceHtml` in the tree. The serializer rejects those fields. +- The serializer also rejects unregistered core block names, attributes absent from block metadata, and non-layout `core/group` tag names (no `span`, `strong`, `time`, `dl`, `dt`, `dd`). If core cannot model a structure cleanly, generate a custom static block whose attributes represent the editable content model. +- Use only real registered core block names. Do not invent convenience blocks such as `core/link`. `core/group` is for block-level layout containers only. +- The tree must match the mockup, not merely contain the same text: preserve source order, links, labels, placeholders, repeated items, and button-group layout. Use stable class names that map cleanly to CSS. +- Do not hide structure inside rich-text attributes. Inline rich text is fine for emphasis/spans inside a heading; repeated items, forms, metrics, timelines, and data blocks are custom-block attributes saved by the block's `save()`. + +**Styling priority (strict):** + +1. Native block and block-support attributes in the tree: media URLs, overlay, focal points, min heights, `backgroundColor`, `textColor`, `gradient`, `style.background`/`.spacing`/`.color`/`.typography`/`.border`/`.dimensions`, `layout`, `align`, `className`, preset color/spacing/font attributes. +2. Custom-block attributes and style variations/classes for named editor-facing design choices. +3. `wordpress/blocks//style.css` for scoped internals supports cannot express: pseudo-elements, nested form controls, sticky behavior, horizontal rails, overlapping children, responsive grids, ornaments, interaction states. +4. `wordpress/style.css` only for design tokens, document defaults, shared responsive rules, and page glue that cannot attach to a block. + +Never solve parity by dumping `mockup/style.css` into `wordpress/style.css`. After serialization, read `reports/style-audit.json`: a good transform shows substantial `blocksWithSupportAttrs` usage and page CSS small enough to explain line by line. + +## CSS-Transfer Gotchas + +Check these BEFORE the first comparison; each has caused real repair iterations. + +- **Inline `margin-top` does not remove the default bottom margin.** A mockup `

    ` with `margin-top:22px` still has the stylesheet's `p { margin: 0 0 1.1em }`. Translating to `margin: 22px 0 0` silently deletes the rhythm. Write `margin: 22px 0 1.1em` or set only `margin-top`. +- **The `.is-layout-constrained > *` full-width cascade.** WordPress constrains children of constrained-layout containers to `contentSize` via `.is-layout-constrained > *:not(.alignwide):not(.alignfull)`. `width: 100%` in CSS cannot beat it. A full-width hero/banner/CTA carries its width through `align: full` (with `layout` type `constrained` for centered inner content, or `default` on both outer and inner groups for edge-to-edge content). Fix it on the block's `align`/`layout`, not in CSS. +- **`core/button` puts the className on the WRAPPER div.** Visual button rules must target the actual control (`.wp-block-button.btn .wp-block-button__link`), never the wrapper, or borders/padding double up. +- **`core/quote` wraps text in a `

    ` the mockup may not have.** That inner paragraph inherits all your `p` rules and can wrap to a different line count. Scope `blockquote p { margin: 0; font-size: inherit; line-height: inherit; text-wrap: wrap; max-width: none; }`. +- **RichText re-wraps overflowing text in the editor.** RichText applies `white-space: pre-wrap; min-width: 1px` as an inline style on every editable element. Text the mockup lets overflow on one line (ghost display words, oversized numerals, clipped labels) stays one line on the frontend but letter-wraps in the canvas — an editor-only, viewport-dependent drift whose fingerprint is a height delta that is an exact multiple of one element's line-height. Fix: pin `white-space: nowrap !important` on those elements. The `!important` is mandatory because it must beat an inline style. +- **Empty inline elements do not survive WordPress.** A decorative `` in rich text passes every preview gate, but `wp_insert_post` tag-balancing eats the closing tag, swallowing following text and failing editor validation in stage 2. Express decorative bars/ornaments as CSS pseudo-elements on the parent instead. +- **A bare inline anchor sits on the parent's line strut**, not its own smaller line-height. Keep the wrapping paragraph at the parent font-size and style only the inner anchor; do not pin a fixed pixel line-height on the paragraph, or baseline rounding produces 1px offsets that cost 2-3% mismatch on text-heavy pages. + +## Expected Output + +A workspace with: `mockup/index.html`, `mockup/style.css`, `plan/block-plan.md`, `plan/block-plan.json`, `wordpress/block-tree.json` (or `wordpress/pages/*.block-tree.json`), `wordpress/content.html`, `wordpress/style.css`, `wordpress/blocks/*`, `rendered/rendered-blocks.html` (or `rendered/*.html`), `editor/block-editor.html` (or `editor/*.html`), and a passing `reports/comparison.json` per page. Hand this completed workspace to the `blocks-to-theme` skill. diff --git a/apps/cli/ai/skills/visual-fidelity/SKILL.md b/apps/cli/ai/skills/visual-fidelity/SKILL.md new file mode 100644 index 0000000000..51dafb3214 --- /dev/null +++ b/apps/cli/ai/skills/visual-fidelity/SKILL.md @@ -0,0 +1,84 @@ +--- +name: visual-fidelity +description: The numeric visual repair loop shared by the html-to-blocks and blocks-to-theme skills. Run compare_html (or playground_render), measure layout geometry first to identify the drifted element, drill into it, repair in priority order, and iterate until rendered and editor surfaces both pass the thresholds. Invoked from within the other two skills during repair. +user-invokable: true +--- + +# Visual Fidelity + +Use this skill to run the visual repair loop that both the `html-to-blocks` and `blocks-to-theme` skills depend on. The comparison tools return metrics and images; you decide what to fix. This skill is normally invoked from within those two during their repair phase, not on its own. + +The thresholds are fixed: `maxMismatchPercent <= 1` and `maxHeightDelta <= 8`, on BOTH the rendered and the editor surface, for EVERY page. Passing them is the only success state. + +## The Loop + +1. Serialize/scaffold the current source (`serialize_wordpress_blocks` in stage 1, `scaffold_block_theme` + `validate_block_theme` in stage 2). Refresh the editor instance with `create_block_editor_preview` when a tree changed. +2. Run the comparison: `compare_html` in stage 1, `playground_render` in stage 2. +3. When it fails, run `measure_layout` FIRST, then inspect the screenshots and diff images to confirm. +4. Write the page's repair-tasks file with concrete, implementation-level tasks. +5. Fix the tasks as code changes — one targeted edit at a time. +6. Repeat until both rendered and editor thresholds pass on every page. + +## Measure First + +Pixel diffs LOCALIZE ("something is red near the footer"); DOM measurements IDENTIFY ("the footer h4 lost its 14px margin"). Always measure before staring at pixels. + +1. `measure_layout` mockup vs rendered (default selector = sections + footer) at both viewports. Find the first row where `deltaTop` starts accumulating — the divergence lives between that row and the previous one. +2. Re-run with a narrower selector (`".section-x > *"`, then `".component > div > *"`) until the drift names one element. Drill from sections to children with progressively narrower selectors. +3. If geometry matches but pixels still differ, probe computed styles on the suspect element (font-size, line-height, letter-spacing, white-space, margins) and compare line rects — a 1px baseline offset or a different line count at equal heights is invisible in geometry tables but obvious in computed styles. +4. Re-run against the editor (`candidateKind: "editor"`); it applies the same comparison CSS the screenshot uses, so what it measures is what the diff sees. + +A uniform `deltaTop` on every row below some point is ONE bug at that point, not many. Fix the first divergence and re-measure before touching anything below it. + +## Repair Order + +Repair from large to small. Do not spend a pass on minor spacing while obvious missing buttons, stacked button groups, broken layouts, wrong grid geometry, or fake forms remain. + +1. **Content / semantic** — missing, duplicated, escaped, or wrong content; fake forms, missing links, wrong buttons, lost labels. +2. **Macro layout** — section order, hero geometry, major grids, asymmetry. +3. **Responsive structure** — columns, button rows, wrapping, mobile order. +4. **Editor-surface drift** — `edit()` wrapper tree, RichText tag/class parity, disabled form geometry, editor-only helper markup, editor-specific CSS. +5. **Component scale** — marquees, cards, forms, buttons, media objects, wrappers, selectors. +6. **Spacing / color / type polish** — last, never first. + +## Task Format + +Tasks must be implementation-level, not "make it look right": + + - [ ] Priority: high + Area: header + Issue: Source has a visible "Book a booth" pill; rendered hides it on desktop after pass 1. + Cause: Repair CSS hides the third header button group at both breakpoints. + Fix: Remove the hiding rule, restore the .site-header three-zone layout, style the third button as the yellow booking pill. + Verify: Desktop rendered screenshot shows the pill; mobile matches source. + +Each task names the visible issue, the target file/selector/block, the cause, the exact fix, and a verification check. + +## Choosing the Surface to Fix + +- **Both rendered and editor fail in the same area** — fix the shared block tree, custom-block data model, shared CSS, or block structure first; both surfaces converge together. +- **Rendered passes but editor fails** — fix custom-block `edit()` output, editor-only classes, or block-owned editor CSS. Do not change `save()` or frontend CSS unless the shared structure is genuinely wrong. +- **Editor passes but rendered fails** — fix `save()` output, support attributes, or frontend scoped CSS without adding editor-only differences. +- Treat editor-surface diffs as first-class failures, not noise, when the frontend looks right but the editable canvas drifts. Do not convert a core section to a custom section block solely to improve editor parity. + +## Editor-Canvas Pitfalls + +The editor preview demotes WordPress editor CSS into a low-priority cascade layer, so unlayered workspace CSS wins at any specificity — the canvas inherits your document rhythm like the frontend, and no margin/line-height restatement is needed. What can still legitimately differ: + +- **Sticky elements engage their offset at scroll 0** inside the editor scroll context and render displaced. Add an editor-only override: `.your-bar.block-editor-block-list__block { position: static; }`. +- **Disabled inputs/textareas need `opacity: 1` and `-webkit-text-fill-color` set to the frontend text color** in the block CSS, or the browser greys them and the diff lights up. +- **RichText-editable `