@animus-ui/extract 0.1.16 → 0.1.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/README.md +3 -1
  2. package/crates/extract-v2/animus-extract-v2.darwin-arm64.node +0 -0
  3. package/crates/extract-v2/animus-extract-v2.linux-arm64-gnu.node +0 -0
  4. package/crates/extract-v2/animus-extract-v2.linux-x64-gnu.node +0 -0
  5. package/crates/extract-v2/index.d.ts +20 -2
  6. package/dist/index.cjs +2 -1
  7. package/dist/index.mjs +2 -2
  8. package/dist/pipeline/analyze-project-args.d.ts +8 -1
  9. package/dist/pipeline/analyze-project-args.d.ts.map +1 -1
  10. package/dist/pipeline/core-options.d.ts +10 -2
  11. package/dist/pipeline/core-options.d.ts.map +1 -1
  12. package/dist/pipeline/dynamic-prop-config.d.ts +31 -4
  13. package/dist/pipeline/dynamic-prop-config.d.ts.map +1 -1
  14. package/dist/pipeline/engine-adapter.d.ts +1 -1
  15. package/dist/pipeline/engine-adapter.d.ts.map +1 -1
  16. package/dist/pipeline/index.d.ts +2 -2
  17. package/dist/pipeline/index.d.ts.map +1 -1
  18. package/dist/pipeline/manifest-diagnostics.d.ts +4 -2
  19. package/dist/pipeline/manifest-diagnostics.d.ts.map +1 -1
  20. package/dist/pipeline/manifest-schema.d.ts +7 -0
  21. package/dist/pipeline/manifest-schema.d.ts.map +1 -1
  22. package/dist/pipeline/prefix.d.ts +2 -0
  23. package/dist/pipeline/prefix.d.ts.map +1 -1
  24. package/dist/pipeline/run-analysis.d.ts.map +1 -1
  25. package/dist/pipeline/source-corpus.d.ts +11 -0
  26. package/dist/pipeline/source-corpus.d.ts.map +1 -1
  27. package/dist/pipeline/source-ingestion.d.ts +12 -0
  28. package/dist/pipeline/source-ingestion.d.ts.map +1 -1
  29. package/dist/pipeline/system-config.d.ts +9 -2
  30. package/dist/pipeline/system-config.d.ts.map +1 -1
  31. package/dist/pipeline/system-props-module.d.ts +3 -1
  32. package/dist/pipeline/system-props-module.d.ts.map +1 -1
  33. package/dist/pipeline/unit-fallback.d.ts.map +1 -1
  34. package/dist/{pipeline-DeEENy5C.mjs → pipeline-D7TdVERQ.mjs} +104 -16
  35. package/dist/{pipeline-x1yBZ2UP.cjs → pipeline-OMz1HGqU.cjs} +109 -15
  36. package/dist/session/extraction-session.d.ts +16 -4
  37. package/dist/session/extraction-session.d.ts.map +1 -1
  38. package/dist/session/index.d.ts +1 -1
  39. package/dist/session/index.d.ts.map +1 -1
  40. package/dist/session.cjs +69 -34
  41. package/dist/session.mjs +69 -35
  42. package/package.json +4 -4
@@ -64,7 +64,7 @@ function assembleStylesheet(options) {
64
64
  //#region pipeline/analyze-project-args.ts
65
65
  /** @internal */
66
66
  function buildAnalyzeProjectArgs(inputs) {
67
- return [
67
+ const args = [
68
68
  inputs.filesJson,
69
69
  inputs.scalesJson,
70
70
  inputs.variableMapJson,
@@ -82,8 +82,11 @@ function buildAnalyzeProjectArgs(inputs) {
82
82
  inputs.staticCssJson,
83
83
  inputs.conditionAliasesJson,
84
84
  inputs.externalDirsJson,
85
- inputs.transformSourcesJson
85
+ inputs.transformSourcesJson,
86
+ inputs.transformProvenanceJson ?? null
86
87
  ];
88
+ if (inputs.declarationScalesJson) args.push(inputs.declarationScalesJson);
89
+ return args;
87
90
  }
88
91
  //#endregion
89
92
  //#region pipeline/content-hash.ts
@@ -747,6 +750,7 @@ const NODE_NEXT_EXTENSION_MAP = {
747
750
  ".tsx",
748
751
  ".jsx"
749
752
  ],
753
+ ".jsx": [".tsx"],
750
754
  ".mjs": [".mts"],
751
755
  ".cjs": [".cts"]
752
756
  };
@@ -928,6 +932,7 @@ async function ingestSourceEntries(rawEntries, options) {
928
932
  }]);
929
933
  }
930
934
  const facts = collectFileFacts(analysisEntries, options);
935
+ const abortedParses = [];
931
936
  for (const [analysisPath, file] of Object.entries(facts.files)) {
932
937
  const originalPath = analysisOwner.get(analysisPath) ?? analysisPath;
933
938
  for (const message of file.parseDiagnostics) diagnostics.push({
@@ -936,6 +941,11 @@ async function ingestSourceEntries(rawEntries, options) {
936
941
  originalPath,
937
942
  analysisPath
938
943
  });
944
+ if (file.parsePanicked) abortedParses.push({
945
+ originalPath,
946
+ hash: ownership[originalPath].originalHash,
947
+ messages: file.parseDiagnostics
948
+ });
939
949
  }
940
950
  const resolvers = new ResolverExportIndex(facts, analysisEntries.map((entry) => entry.path));
941
951
  for (const { canonicalPath, paths } of resolvers.collisions) for (let index = 0; index < paths.length; index += 1) {
@@ -965,12 +975,14 @@ async function ingestSourceEntries(rawEntries, options) {
965
975
  }
966
976
  addAdaptedEntries(original.path, result.entries);
967
977
  }
968
- return {
978
+ const result = {
969
979
  originalEntries,
970
980
  analysisEntries,
971
981
  ownership,
972
982
  diagnostics
973
983
  };
984
+ if (abortedParses.length > 0) result.abortedParses = abortedParses;
985
+ return result;
974
986
  }
975
987
  /**
976
988
  * `parseCount` counts the parses performed by THIS call, so a fully-memoized
@@ -1096,7 +1108,7 @@ function createV2EngineApi(deps) {
1096
1108
  return {
1097
1109
  loadSystemModule: (...args) => native.loadSystemModule(...args),
1098
1110
  extractFacts: (filesJson) => native.extractFacts(filesJson),
1099
- analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson) => {
1111
+ analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson) => {
1100
1112
  const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
1101
1113
  const sent = /* @__PURE__ */ new Map();
1102
1114
  for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
@@ -1121,6 +1133,8 @@ function createV2EngineApi(deps) {
1121
1133
  staticCssJson: staticCssJson ?? void 0,
1122
1134
  externalDirsJson: externalDirsJson ?? void 0,
1123
1135
  transformSourcesJson: transformSourcesJson ?? void 0,
1136
+ transformProvenanceJson: transformProvenanceJson ?? void 0,
1137
+ declarationScalesJson,
1124
1138
  devMode
1125
1139
  };
1126
1140
  const engine = new native.ExtractEngine(config);
@@ -1180,6 +1194,12 @@ const REPLACEABLE_DEFAULT_EXCLUDE = [
1180
1194
  ".spec."
1181
1195
  ];
1182
1196
  const DEFAULT_EXCLUDE = [...STRUCTURAL_EXCLUDE, ...REPLACEABLE_DEFAULT_EXCLUDE];
1197
+ /** The effective log tier: the option or `ANIMUS_DEBUG`, whichever is higher. */
1198
+ function resolveVerbosity(option, debugEnv) {
1199
+ if (option === "trace" || debugEnv === "trace") return "trace";
1200
+ if (option === true || debugEnv === "1" || debugEnv === "true") return "verbose";
1201
+ return "quiet";
1202
+ }
1183
1203
  const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
1184
1204
  "system",
1185
1205
  "exclude",
@@ -1241,6 +1261,7 @@ function assertKnownOptionKeys(raw, ownKeys = [], rejectKeys = [], opts = {}) {
1241
1261
  */
1242
1262
  const isString = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object String]";
1243
1263
  const isBoolean = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object Boolean]";
1264
+ const isVerbose = (value) => isBoolean(value) || value === "trace";
1244
1265
  const isStringArray = (value) => Array.isArray(value) && value.every(isString);
1245
1266
  const isStringOrStringArray = (value) => isString(value) || isStringArray(value);
1246
1267
  const CORE_VALUE_GATES = [
@@ -1266,8 +1287,8 @@ const CORE_VALUE_GATES = [
1266
1287
  },
1267
1288
  {
1268
1289
  key: "verbose",
1269
- ok: isBoolean,
1270
- expected: "a boolean"
1290
+ ok: isVerbose,
1291
+ expected: "a boolean or 'trace'"
1271
1292
  },
1272
1293
  {
1273
1294
  key: "minify",
@@ -1949,6 +1970,16 @@ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
1949
1970
  const ingested = await ingestor.ingest(rawEntries);
1950
1971
  return withoutInvalidOriginals(ingested, ingestor.surfaceDiagnostics(ingested.diagnostics));
1951
1972
  },
1973
+ rejection(prepared) {
1974
+ const aborted = (prepared.abortedParses ?? []).filter(({ originalPath }) => prepared.ownership[originalPath] !== void 0);
1975
+ if (aborted.length === 0) return null;
1976
+ const files = aborted.map(({ originalPath, messages }) => `${originalPath} (${messages.join("; ")})`).join(", ");
1977
+ const owners = aborted.length === 1 ? "this file's" : "these files'";
1978
+ return {
1979
+ originals: new Map(aborted.map(({ originalPath, hash }) => [originalPath, hash])),
1980
+ message: `${host.prefix} analysis not published: the parser stopped before the end of ${files}; fix the syntax error to publish ${owners} changes`
1981
+ };
1982
+ },
1952
1983
  publish(accepted) {
1953
1984
  ingestor.markPublished(accepted);
1954
1985
  published = {
@@ -2356,9 +2387,14 @@ function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
2356
2387
  }
2357
2388
  //#endregion
2358
2389
  //#region pipeline/unit-fallback.ts
2390
+ /** A digit run after one of these continues a word (`#b1b1b7`, `ss01`,
2391
+ * `U+0025`), so it is not a bare number. The extractor predicts this pass
2392
+ * with `unit_fallback_rewrites` in `crates/extract-v2/src/css.rs`; keep both
2393
+ * in step. */
2394
+ const WORD_CHAR = /[A-Za-z0-9_#+-]/;
2359
2395
  function applyUnitFallback(css) {
2360
- return css.replace(/([a-z-]+)\s*:\s*([^;{}]+);/g, (match, prop, value) => {
2361
- if (isUnitlessProperty(prop)) return match;
2396
+ return css.replace(/(--[\w-]+|[a-z-]+)\s*:\s*([^;{}]+);/g, (match, prop, value) => {
2397
+ if (isUnitlessProperty(prop) || prop.startsWith("--")) return match;
2362
2398
  let depth = 0;
2363
2399
  let fixed = "";
2364
2400
  let i = 0;
@@ -2373,8 +2409,14 @@ function applyUnitFallback(css) {
2373
2409
  } else if (depth > 0) {
2374
2410
  fixed += value[i];
2375
2411
  i++;
2412
+ } else if (value[i] === "\"" || value[i] === "'") {
2413
+ let end = i + 1;
2414
+ while (end < value.length && value[end] !== value[i]) end += value[end] === "\\" ? 2 : 1;
2415
+ end = Math.min(end + 1, value.length);
2416
+ fixed += value.slice(i, end);
2417
+ i = end;
2376
2418
  } else {
2377
- const numMatch = value.slice(i).match(/^(-?\d+\.?\d*)/);
2419
+ const numMatch = i === 0 || !WORD_CHAR.test(value[i - 1]) ? value.slice(i).match(/^([+-]?\d+\.?\d*)/) : null;
2378
2420
  if (numMatch) {
2379
2421
  const num = numMatch[1];
2380
2422
  const after = value[i + num.length];
@@ -2399,7 +2441,7 @@ function buildAnalysisInputs(opts) {
2399
2441
  css_module_id: opts.emitter.cssModuleId
2400
2442
  };
2401
2443
  if (opts.emitter.systemPropsModuleId) emitterConfig.system_props_module_id = opts.emitter.systemPropsModuleId;
2402
- return {
2444
+ const inputs = {
2403
2445
  filesJson: JSON.stringify(opts.fileEntries),
2404
2446
  scalesJson: opts.system.scalesJson,
2405
2447
  variableMapJson: opts.system.variableMapJson,
@@ -2416,8 +2458,11 @@ function buildAnalysisInputs(opts) {
2416
2458
  staticCssJson: opts.staticCssJson ?? null,
2417
2459
  conditionAliasesJson: opts.system.conditionAliasesJson ?? null,
2418
2460
  transformSourcesJson: opts.system.transformSourcesJson ?? null,
2461
+ transformProvenanceJson: opts.system.transformProvenanceJson ?? null,
2419
2462
  externalDirsJson: opts.externalDirs?.length && hasSourceThemeManifests(opts.system) ? JSON.stringify(opts.externalDirs) : null
2420
2463
  };
2464
+ if (opts.system.declarationScalesJson) inputs.declarationScalesJson = opts.system.declarationScalesJson;
2465
+ return inputs;
2421
2466
  }
2422
2467
  function hasSourceThemeManifests(system) {
2423
2468
  const json = system.sourceThemeManifestsJson ?? "";
@@ -2499,6 +2544,11 @@ function serializeStaticCss(config) {
2499
2544
  }
2500
2545
  //#endregion
2501
2546
  //#region pipeline/prefix.ts
2547
+ /** Prefix variable references without changing their matching rules. */
2548
+ function prefixVariableReferences(prefix, value) {
2549
+ if (!prefix) return value;
2550
+ return value.replace(/var\(--([a-zA-Z][\w-]*)\)/g, `var(--${prefix}-$1)`);
2551
+ }
2502
2552
  function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextualVarsJson) {
2503
2553
  if (!prefix) return {
2504
2554
  variableMapJson,
@@ -2506,18 +2556,18 @@ function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextual
2506
2556
  themeJson,
2507
2557
  contextualVarsJson
2508
2558
  };
2509
- const varRefRe = /var\(--([a-zA-Z][\w-]*)\)/g;
2510
2559
  const map = JSON.parse(variableMapJson);
2511
2560
  const prefixed = {};
2512
2561
  for (const [key, varName] of Object.entries(map)) prefixed[key] = varName.startsWith("--") ? `--${prefix}-${varName.slice(2)}` : varName;
2513
2562
  let css = variableCss;
2514
2563
  css = css.replace(/--([a-zA-Z][\w-]*)\s*:/g, `--${prefix}-$1:`);
2515
- css = css.replace(varRefRe, `var(--${prefix}-$1)`);
2564
+ css = prefixVariableReferences(prefix, css);
2565
+ css = css.replace(/@property(\s+)--([a-zA-Z][\w-]*)/g, `@property$1--${prefix}-$2`);
2516
2566
  const result = {
2517
2567
  variableMapJson: JSON.stringify(prefixed),
2518
2568
  variableCss: css
2519
2569
  };
2520
- if (themeJson) result.themeJson = themeJson.replace(varRefRe, `var(--${prefix}-$1)`);
2570
+ if (themeJson) result.themeJson = prefixVariableReferences(prefix, themeJson);
2521
2571
  if (contextualVarsJson) {
2522
2572
  const ctxVars = JSON.parse(contextualVarsJson);
2523
2573
  const prefixedCtx = {};
@@ -2539,14 +2589,16 @@ function loadSystemConfig(engineApi, opts) {
2539
2589
  let variableMapJson = config.variableMapJson;
2540
2590
  let variableCss = config.variableCss;
2541
2591
  let contextualVarsJson = config.contextualVarsJson || null;
2592
+ let declarationScalesJson = config.declarationScalesJson || null;
2542
2593
  if (opts.prefix) {
2543
2594
  const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0);
2544
2595
  variableMapJson = prefixed.variableMapJson;
2545
2596
  variableCss = prefixed.variableCss;
2546
2597
  if (prefixed.themeJson) scalesJson = prefixed.themeJson;
2547
2598
  if (prefixed.contextualVarsJson) contextualVarsJson = prefixed.contextualVarsJson;
2599
+ if (declarationScalesJson) declarationScalesJson = prefixVariableReferences(opts.prefix, declarationScalesJson);
2548
2600
  }
2549
- return {
2601
+ const system = {
2550
2602
  propConfigJson: config.propConfig,
2551
2603
  groupRegistryJson: config.groupRegistry,
2552
2604
  scalesJson,
@@ -2556,12 +2608,15 @@ function loadSystemConfig(engineApi, opts) {
2556
2608
  selectorAliasesJson: config.selectorAliases || null,
2557
2609
  conditionAliasesJson: config.conditionAliases || null,
2558
2610
  transformSourcesJson: config.transformSources || null,
2611
+ transformProvenanceJson: config.transformProvenance || null,
2559
2612
  globalStyleBlocksJson: config.globalStyleBlocks || null,
2560
2613
  keyframesJson: config.keyframesBlocks || null,
2561
2614
  vocabularyWitnessesJson: config.vocabularyWitnesses || null,
2562
2615
  dependencies: config.dependencies ?? [],
2563
2616
  sourceThemeManifestsJson: config.sourceThemeManifests || null
2564
2617
  };
2618
+ if (declarationScalesJson) system.declarationScalesJson = declarationScalesJson;
2619
+ return system;
2565
2620
  }
2566
2621
  //#endregion
2567
2622
  //#region pipeline/structural-self-check.ts
@@ -2591,6 +2646,16 @@ function runStructuralSelfCheck(input) {
2591
2646
  function buildDynamicPropConfig(dynamicProps) {
2592
2647
  const configEntries = {};
2593
2648
  for (const [propName, meta] of Object.entries(dynamicProps)) {
2649
+ if (meta.kind === "declarations") {
2650
+ if (!meta.slotClass || !meta.memberVars || !meta.declarationScaleValues) throw new Error(`buildDynamicPropConfig: declaration prop '${propName}' carries no member metadata — expected slotClass, memberVars and declarationScaleValues.`);
2651
+ configEntries[propName] = {
2652
+ kind: meta.kind,
2653
+ slotClass: meta.slotClass,
2654
+ memberVars: meta.memberVars,
2655
+ declarationScaleValues: meta.declarationScaleValues
2656
+ };
2657
+ continue;
2658
+ }
2594
2659
  if (!meta.varName || !meta.slotClass) throw new Error(`buildDynamicPropConfig: dynamic prop '${propName}' carries no slot metadata — expected varName and slotClass, got keys [${Object.keys(meta).join(", ")}].`);
2595
2660
  const entry = {
2596
2661
  varName: meta.varName,
@@ -2599,16 +2664,39 @@ function buildDynamicPropConfig(dynamicProps) {
2599
2664
  if (meta.property) entry.property = meta.property;
2600
2665
  if (meta.properties && meta.properties.length > 0) entry.properties = meta.properties;
2601
2666
  if (meta.transformName) entry.transformName = meta.transformName;
2667
+ if (meta.transformId) entry.transformId = meta.transformId;
2602
2668
  if (meta.scaleValues && Object.keys(meta.scaleValues).length > 0) entry.scaleValues = meta.scaleValues;
2669
+ if (meta.negative) entry.negative = true;
2670
+ if (meta.strict) entry.strict = true;
2671
+ if (meta.keywords?.length) entry.keywords = meta.keywords;
2603
2672
  configEntries[propName] = entry;
2604
2673
  }
2605
2674
  return configEntries;
2606
2675
  }
2607
2676
  //#endregion
2608
2677
  //#region pipeline/system-props-module.ts
2678
+ /** Compiles the exact entry text in strict mode, the mode of the ES module it
2679
+ * lands in, without evaluating it: one malformed entry would otherwise fail
2680
+ * the whole module. */
2681
+ function parsesAsEntry(entry) {
2682
+ try {
2683
+ Function(`'use strict'; return {\n${entry}};`);
2684
+ return true;
2685
+ } catch {
2686
+ return false;
2687
+ }
2688
+ }
2689
+ /** Delivers the loaded system's configured transform sources that analysis
2690
+ * admitted, keyed by definition and wrapped as `({source})` like the
2691
+ * build-time evaluator registers them. Static evaluation uses the same
2692
+ * definitions, so a same-named project declaration reaches neither path. */
2693
+ function transformsSource(admittedTransforms) {
2694
+ const entries = Object.entries(admittedTransforms).sort(([a], [b]) => a < b ? -1 : 1).map(([name, source]) => ` ${JSON.stringify(name)}: (${source}),\n`).filter(parsesAsEntry);
2695
+ return entries.length > 0 ? `{\n${entries.join("")}}` : "{}";
2696
+ }
2609
2697
  function buildSystemPropsModule(opts) {
2610
2698
  const dynamicPropConfig = buildDynamicPropConfig(opts.dynamicProps);
2611
- return `export const systemPropMap = ${opts.systemPropMapJson};\nexport const systemPropGroups = ${opts.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${opts.transformsSource ?? "{}"};\n`;
2699
+ return `export const systemPropMap = ${opts.systemPropMapJson};\nexport const systemPropGroups = ${opts.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${transformsSource(opts.admittedTransforms)};\n` + (opts.typedSystemProps.length > 0 ? `export const typedSystemProps = ${JSON.stringify(opts.typedSystemProps)};\n` : "");
2612
2700
  }
2613
2701
  //#endregion
2614
2702
  //#region pipeline/timing-waterfall.ts
@@ -2902,4 +2990,4 @@ function realpathThroughNearestExistingAncestor(path) {
2902
2990
  }
2903
2991
  }
2904
2992
  //#endregion
2905
- export { DEFAULT_EXCLUDE as $, enforceExternalTokenContracts as A, firstOwners as B, surfaceManifestDiagnostics as C, validateLayerOrder as Ct, postProcessCss as D, vocabularyWitnessDiagnostics as E, createSourceCorpus as F, walkPackageSources as G, resolveAbsolutePathSpecifier as H, collectExternalPackageSources as I, sharesVolumeRoot as J, createSourceIdentity as K, excludeCollectedPackages as L, resolveThroughPathAliases as M, findAssetSpecifiers as N, resolveLightningTargets as O, substituteAssetPlaceholders as P, CORE_OPTION_KEYS as Q, extractSystemFilePackages as R, isUnresolvedParentDrop as S, stripLeadingLayerDeclaration as St, unresolvedParentName as T, staleDistIncludesMessage as U, isExcludedPackageRelativePath as V, unresolvableIncludesMessage as W, discoverFiles as X, compareDiscoveryOrder as Y, AnimusConfigError as Z, clearEngineCache as _, preprocessMdx as _t, assertNoErrorDiagnostics as a, resolveMode as at, VOCABULARY_COLLISION as b, ANIMUS_LAYERS as bt, buildSystemPropsModule as c, createV2EngineApi as ct, loadSystemConfig as d, parseFilesJson as dt, DRIVER_NAMESPACES as et, applyPrefix as f, projectExternalFileOwners as ft, buildAnalysisInputs as g, isEngineTransformExtension as gt, stableStringify as h, ENGINE_TRANSFORM_EXTENSIONS as ht, snapshotFilePlans as i, createExcludeMatcher as it, resolveAssetFile as j, buildPathAliasesJson as k, buildDynamicPropConfig as l, ingestSourceEntries as lt, camelToKebab as m, DEFAULT_EXTENSIONS as mt, diffFilePlans as n, STRUCTURAL_EXCLUDE as nt, readTsconfigAliasPairs as o, RETIRED_ENGINE_MESSAGE as ot, serializeStaticCss as p, adaptSvelteSource as pt, isPathWithinRoot as q, hashReplacementPlans as r, assertKnownOptionKeys as rt, formatRustTimingWaterfall as s, assertNoRetiredEngineSelection as st, toWatchKeys as t, REPLACEABLE_DEFAULT_EXCLUDE as tt, runStructuralSelfCheck as u, isAdvisorySourceDiagnostic as ut, runProjectAnalysis as v, contentHash as vt, unreadableSourceDiagnostic as w, __require as wt, VOCABULARY_LEGACY_VERB as x, assembleStylesheet as xt, applyUnitFallback as y, buildAnalyzeProjectArgs as yt, findPackageRoot as z };
2993
+ export { DEFAULT_EXCLUDE as $, enforceExternalTokenContracts as A, firstOwners as B, surfaceManifestDiagnostics as C, stripLeadingLayerDeclaration as Ct, postProcessCss as D, vocabularyWitnessDiagnostics as E, createSourceCorpus as F, walkPackageSources as G, resolveAbsolutePathSpecifier as H, collectExternalPackageSources as I, sharesVolumeRoot as J, createSourceIdentity as K, excludeCollectedPackages as L, resolveThroughPathAliases as M, findAssetSpecifiers as N, resolveLightningTargets as O, substituteAssetPlaceholders as P, CORE_OPTION_KEYS as Q, extractSystemFilePackages as R, isUnresolvedParentDrop as S, assembleStylesheet as St, unresolvedParentName as T, __require as Tt, staleDistIncludesMessage as U, isExcludedPackageRelativePath as V, unresolvableIncludesMessage as W, discoverFiles as X, compareDiscoveryOrder as Y, AnimusConfigError as Z, clearEngineCache as _, isEngineTransformExtension as _t, assertNoErrorDiagnostics as a, resolveMode as at, VOCABULARY_COLLISION as b, buildAnalyzeProjectArgs as bt, buildSystemPropsModule as c, assertNoRetiredEngineSelection as ct, loadSystemConfig as d, isAdvisorySourceDiagnostic as dt, DRIVER_NAMESPACES as et, applyPrefix as f, parseFilesJson as ft, buildAnalysisInputs as g, ENGINE_TRANSFORM_EXTENSIONS as gt, stableStringify as h, DEFAULT_EXTENSIONS as ht, snapshotFilePlans as i, createExcludeMatcher as it, resolveAssetFile as j, buildPathAliasesJson as k, buildDynamicPropConfig as l, createV2EngineApi as lt, camelToKebab as m, adaptSvelteSource as mt, diffFilePlans as n, STRUCTURAL_EXCLUDE as nt, readTsconfigAliasPairs as o, resolveVerbosity as ot, serializeStaticCss as p, projectExternalFileOwners as pt, isPathWithinRoot as q, hashReplacementPlans as r, assertKnownOptionKeys as rt, formatRustTimingWaterfall as s, RETIRED_ENGINE_MESSAGE as st, toWatchKeys as t, REPLACEABLE_DEFAULT_EXCLUDE as tt, runStructuralSelfCheck as u, ingestSourceEntries as ut, runProjectAnalysis as v, preprocessMdx as vt, unreadableSourceDiagnostic as w, validateLayerOrder as wt, VOCABULARY_LEGACY_VERB as x, ANIMUS_LAYERS as xt, applyUnitFallback as y, contentHash as yt, findPackageRoot as z };
@@ -84,7 +84,7 @@ function assembleStylesheet(options) {
84
84
  //#region pipeline/analyze-project-args.ts
85
85
  /** @internal */
86
86
  function buildAnalyzeProjectArgs(inputs) {
87
- return [
87
+ const args = [
88
88
  inputs.filesJson,
89
89
  inputs.scalesJson,
90
90
  inputs.variableMapJson,
@@ -102,8 +102,11 @@ function buildAnalyzeProjectArgs(inputs) {
102
102
  inputs.staticCssJson,
103
103
  inputs.conditionAliasesJson,
104
104
  inputs.externalDirsJson,
105
- inputs.transformSourcesJson
105
+ inputs.transformSourcesJson,
106
+ inputs.transformProvenanceJson ?? null
106
107
  ];
108
+ if (inputs.declarationScalesJson) args.push(inputs.declarationScalesJson);
109
+ return args;
107
110
  }
108
111
  //#endregion
109
112
  //#region pipeline/content-hash.ts
@@ -767,6 +770,7 @@ const NODE_NEXT_EXTENSION_MAP = {
767
770
  ".tsx",
768
771
  ".jsx"
769
772
  ],
773
+ ".jsx": [".tsx"],
770
774
  ".mjs": [".mts"],
771
775
  ".cjs": [".cts"]
772
776
  };
@@ -948,6 +952,7 @@ async function ingestSourceEntries(rawEntries, options) {
948
952
  }]);
949
953
  }
950
954
  const facts = collectFileFacts(analysisEntries, options);
955
+ const abortedParses = [];
951
956
  for (const [analysisPath, file] of Object.entries(facts.files)) {
952
957
  const originalPath = analysisOwner.get(analysisPath) ?? analysisPath;
953
958
  for (const message of file.parseDiagnostics) diagnostics.push({
@@ -956,6 +961,11 @@ async function ingestSourceEntries(rawEntries, options) {
956
961
  originalPath,
957
962
  analysisPath
958
963
  });
964
+ if (file.parsePanicked) abortedParses.push({
965
+ originalPath,
966
+ hash: ownership[originalPath].originalHash,
967
+ messages: file.parseDiagnostics
968
+ });
959
969
  }
960
970
  const resolvers = new ResolverExportIndex(facts, analysisEntries.map((entry) => entry.path));
961
971
  for (const { canonicalPath, paths } of resolvers.collisions) for (let index = 0; index < paths.length; index += 1) {
@@ -985,12 +995,14 @@ async function ingestSourceEntries(rawEntries, options) {
985
995
  }
986
996
  addAdaptedEntries(original.path, result.entries);
987
997
  }
988
- return {
998
+ const result = {
989
999
  originalEntries,
990
1000
  analysisEntries,
991
1001
  ownership,
992
1002
  diagnostics
993
1003
  };
1004
+ if (abortedParses.length > 0) result.abortedParses = abortedParses;
1005
+ return result;
994
1006
  }
995
1007
  /**
996
1008
  * `parseCount` counts the parses performed by THIS call, so a fully-memoized
@@ -1116,7 +1128,7 @@ function createV2EngineApi(deps) {
1116
1128
  return {
1117
1129
  loadSystemModule: (...args) => native.loadSystemModule(...args),
1118
1130
  extractFacts: (filesJson) => native.extractFacts(filesJson),
1119
- analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson) => {
1131
+ analyzeProject: (filesJsonRaw, scalesJson, variableMapJson, contextualVarsJson, propConfigJson, groupRegistryJson, packageResolutionJson, devMode, emitterConfigJson, selectorAliasesJson, _selectorOrderJson, globalStyleBlocksJson, pathAliasesJson, keyframesJson, staticCssJson, conditionAliasesJson, externalDirsJson, transformSourcesJson, transformProvenanceJson, declarationScalesJson) => {
1120
1132
  const filesJson = deps.rehydrateFilesJson ? deps.rehydrateFilesJson(filesJsonRaw) : filesJsonRaw;
1121
1133
  const sent = /* @__PURE__ */ new Map();
1122
1134
  for (const entry of parseFilesJson(filesJson, label)) sent.set(entry.path, entry.source);
@@ -1141,6 +1153,8 @@ function createV2EngineApi(deps) {
1141
1153
  staticCssJson: staticCssJson ?? void 0,
1142
1154
  externalDirsJson: externalDirsJson ?? void 0,
1143
1155
  transformSourcesJson: transformSourcesJson ?? void 0,
1156
+ transformProvenanceJson: transformProvenanceJson ?? void 0,
1157
+ declarationScalesJson,
1144
1158
  devMode
1145
1159
  };
1146
1160
  const engine = new native.ExtractEngine(config);
@@ -1200,6 +1214,12 @@ const REPLACEABLE_DEFAULT_EXCLUDE = [
1200
1214
  ".spec."
1201
1215
  ];
1202
1216
  const DEFAULT_EXCLUDE = [...STRUCTURAL_EXCLUDE, ...REPLACEABLE_DEFAULT_EXCLUDE];
1217
+ /** The effective log tier: the option or `ANIMUS_DEBUG`, whichever is higher. */
1218
+ function resolveVerbosity(option, debugEnv) {
1219
+ if (option === "trace" || debugEnv === "trace") return "trace";
1220
+ if (option === true || debugEnv === "1" || debugEnv === "true") return "verbose";
1221
+ return "quiet";
1222
+ }
1203
1223
  const CORE_OPTION_KEYS = /* @__PURE__ */ new Set([
1204
1224
  "system",
1205
1225
  "exclude",
@@ -1261,6 +1281,7 @@ function assertKnownOptionKeys(raw, ownKeys = [], rejectKeys = [], opts = {}) {
1261
1281
  */
1262
1282
  const isString = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object String]";
1263
1283
  const isBoolean = (value) => Object(value) !== value && Object.prototype.toString.call(value) === "[object Boolean]";
1284
+ const isVerbose = (value) => isBoolean(value) || value === "trace";
1264
1285
  const isStringArray = (value) => Array.isArray(value) && value.every(isString);
1265
1286
  const isStringOrStringArray = (value) => isString(value) || isStringArray(value);
1266
1287
  const CORE_VALUE_GATES = [
@@ -1286,8 +1307,8 @@ const CORE_VALUE_GATES = [
1286
1307
  },
1287
1308
  {
1288
1309
  key: "verbose",
1289
- ok: isBoolean,
1290
- expected: "a boolean"
1310
+ ok: isVerbose,
1311
+ expected: "a boolean or 'trace'"
1291
1312
  },
1292
1313
  {
1293
1314
  key: "minify",
@@ -1969,6 +1990,16 @@ function createSourceCorpus(host, ingestor = createSourceIngestor(host)) {
1969
1990
  const ingested = await ingestor.ingest(rawEntries);
1970
1991
  return withoutInvalidOriginals(ingested, ingestor.surfaceDiagnostics(ingested.diagnostics));
1971
1992
  },
1993
+ rejection(prepared) {
1994
+ const aborted = (prepared.abortedParses ?? []).filter(({ originalPath }) => prepared.ownership[originalPath] !== void 0);
1995
+ if (aborted.length === 0) return null;
1996
+ const files = aborted.map(({ originalPath, messages }) => `${originalPath} (${messages.join("; ")})`).join(", ");
1997
+ const owners = aborted.length === 1 ? "this file's" : "these files'";
1998
+ return {
1999
+ originals: new Map(aborted.map(({ originalPath, hash }) => [originalPath, hash])),
2000
+ message: `${host.prefix} analysis not published: the parser stopped before the end of ${files}; fix the syntax error to publish ${owners} changes`
2001
+ };
2002
+ },
1972
2003
  publish(accepted) {
1973
2004
  ingestor.markPublished(accepted);
1974
2005
  published = {
@@ -2376,9 +2407,14 @@ function surfaceManifestDiagnostics(manifest, warn, policy = {}) {
2376
2407
  }
2377
2408
  //#endregion
2378
2409
  //#region pipeline/unit-fallback.ts
2410
+ /** A digit run after one of these continues a word (`#b1b1b7`, `ss01`,
2411
+ * `U+0025`), so it is not a bare number. The extractor predicts this pass
2412
+ * with `unit_fallback_rewrites` in `crates/extract-v2/src/css.rs`; keep both
2413
+ * in step. */
2414
+ const WORD_CHAR = /[A-Za-z0-9_#+-]/;
2379
2415
  function applyUnitFallback(css) {
2380
- return css.replace(/([a-z-]+)\s*:\s*([^;{}]+);/g, (match, prop, value) => {
2381
- if ((0, _animus_ui_properties.isUnitlessProperty)(prop)) return match;
2416
+ return css.replace(/(--[\w-]+|[a-z-]+)\s*:\s*([^;{}]+);/g, (match, prop, value) => {
2417
+ if ((0, _animus_ui_properties.isUnitlessProperty)(prop) || prop.startsWith("--")) return match;
2382
2418
  let depth = 0;
2383
2419
  let fixed = "";
2384
2420
  let i = 0;
@@ -2393,8 +2429,14 @@ function applyUnitFallback(css) {
2393
2429
  } else if (depth > 0) {
2394
2430
  fixed += value[i];
2395
2431
  i++;
2432
+ } else if (value[i] === "\"" || value[i] === "'") {
2433
+ let end = i + 1;
2434
+ while (end < value.length && value[end] !== value[i]) end += value[end] === "\\" ? 2 : 1;
2435
+ end = Math.min(end + 1, value.length);
2436
+ fixed += value.slice(i, end);
2437
+ i = end;
2396
2438
  } else {
2397
- const numMatch = value.slice(i).match(/^(-?\d+\.?\d*)/);
2439
+ const numMatch = i === 0 || !WORD_CHAR.test(value[i - 1]) ? value.slice(i).match(/^([+-]?\d+\.?\d*)/) : null;
2398
2440
  if (numMatch) {
2399
2441
  const num = numMatch[1];
2400
2442
  const after = value[i + num.length];
@@ -2419,7 +2461,7 @@ function buildAnalysisInputs(opts) {
2419
2461
  css_module_id: opts.emitter.cssModuleId
2420
2462
  };
2421
2463
  if (opts.emitter.systemPropsModuleId) emitterConfig.system_props_module_id = opts.emitter.systemPropsModuleId;
2422
- return {
2464
+ const inputs = {
2423
2465
  filesJson: JSON.stringify(opts.fileEntries),
2424
2466
  scalesJson: opts.system.scalesJson,
2425
2467
  variableMapJson: opts.system.variableMapJson,
@@ -2436,8 +2478,11 @@ function buildAnalysisInputs(opts) {
2436
2478
  staticCssJson: opts.staticCssJson ?? null,
2437
2479
  conditionAliasesJson: opts.system.conditionAliasesJson ?? null,
2438
2480
  transformSourcesJson: opts.system.transformSourcesJson ?? null,
2481
+ transformProvenanceJson: opts.system.transformProvenanceJson ?? null,
2439
2482
  externalDirsJson: opts.externalDirs?.length && hasSourceThemeManifests(opts.system) ? JSON.stringify(opts.externalDirs) : null
2440
2483
  };
2484
+ if (opts.system.declarationScalesJson) inputs.declarationScalesJson = opts.system.declarationScalesJson;
2485
+ return inputs;
2441
2486
  }
2442
2487
  function hasSourceThemeManifests(system) {
2443
2488
  const json = system.sourceThemeManifestsJson ?? "";
@@ -2519,6 +2564,11 @@ function serializeStaticCss(config) {
2519
2564
  }
2520
2565
  //#endregion
2521
2566
  //#region pipeline/prefix.ts
2567
+ /** Prefix variable references without changing their matching rules. */
2568
+ function prefixVariableReferences(prefix, value) {
2569
+ if (!prefix) return value;
2570
+ return value.replace(/var\(--([a-zA-Z][\w-]*)\)/g, `var(--${prefix}-$1)`);
2571
+ }
2522
2572
  function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextualVarsJson) {
2523
2573
  if (!prefix) return {
2524
2574
  variableMapJson,
@@ -2526,18 +2576,18 @@ function applyPrefix(prefix, variableMapJson, variableCss, themeJson, contextual
2526
2576
  themeJson,
2527
2577
  contextualVarsJson
2528
2578
  };
2529
- const varRefRe = /var\(--([a-zA-Z][\w-]*)\)/g;
2530
2579
  const map = JSON.parse(variableMapJson);
2531
2580
  const prefixed = {};
2532
2581
  for (const [key, varName] of Object.entries(map)) prefixed[key] = varName.startsWith("--") ? `--${prefix}-${varName.slice(2)}` : varName;
2533
2582
  let css = variableCss;
2534
2583
  css = css.replace(/--([a-zA-Z][\w-]*)\s*:/g, `--${prefix}-$1:`);
2535
- css = css.replace(varRefRe, `var(--${prefix}-$1)`);
2584
+ css = prefixVariableReferences(prefix, css);
2585
+ css = css.replace(/@property(\s+)--([a-zA-Z][\w-]*)/g, `@property$1--${prefix}-$2`);
2536
2586
  const result = {
2537
2587
  variableMapJson: JSON.stringify(prefixed),
2538
2588
  variableCss: css
2539
2589
  };
2540
- if (themeJson) result.themeJson = themeJson.replace(varRefRe, `var(--${prefix}-$1)`);
2590
+ if (themeJson) result.themeJson = prefixVariableReferences(prefix, themeJson);
2541
2591
  if (contextualVarsJson) {
2542
2592
  const ctxVars = JSON.parse(contextualVarsJson);
2543
2593
  const prefixedCtx = {};
@@ -2559,14 +2609,16 @@ function loadSystemConfig(engineApi, opts) {
2559
2609
  let variableMapJson = config.variableMapJson;
2560
2610
  let variableCss = config.variableCss;
2561
2611
  let contextualVarsJson = config.contextualVarsJson || null;
2612
+ let declarationScalesJson = config.declarationScalesJson || null;
2562
2613
  if (opts.prefix) {
2563
2614
  const prefixed = applyPrefix(opts.prefix, variableMapJson, variableCss, scalesJson, contextualVarsJson || void 0);
2564
2615
  variableMapJson = prefixed.variableMapJson;
2565
2616
  variableCss = prefixed.variableCss;
2566
2617
  if (prefixed.themeJson) scalesJson = prefixed.themeJson;
2567
2618
  if (prefixed.contextualVarsJson) contextualVarsJson = prefixed.contextualVarsJson;
2619
+ if (declarationScalesJson) declarationScalesJson = prefixVariableReferences(opts.prefix, declarationScalesJson);
2568
2620
  }
2569
- return {
2621
+ const system = {
2570
2622
  propConfigJson: config.propConfig,
2571
2623
  groupRegistryJson: config.groupRegistry,
2572
2624
  scalesJson,
@@ -2576,12 +2628,15 @@ function loadSystemConfig(engineApi, opts) {
2576
2628
  selectorAliasesJson: config.selectorAliases || null,
2577
2629
  conditionAliasesJson: config.conditionAliases || null,
2578
2630
  transformSourcesJson: config.transformSources || null,
2631
+ transformProvenanceJson: config.transformProvenance || null,
2579
2632
  globalStyleBlocksJson: config.globalStyleBlocks || null,
2580
2633
  keyframesJson: config.keyframesBlocks || null,
2581
2634
  vocabularyWitnessesJson: config.vocabularyWitnesses || null,
2582
2635
  dependencies: config.dependencies ?? [],
2583
2636
  sourceThemeManifestsJson: config.sourceThemeManifests || null
2584
2637
  };
2638
+ if (declarationScalesJson) system.declarationScalesJson = declarationScalesJson;
2639
+ return system;
2585
2640
  }
2586
2641
  //#endregion
2587
2642
  //#region pipeline/structural-self-check.ts
@@ -2611,6 +2666,16 @@ function runStructuralSelfCheck(input) {
2611
2666
  function buildDynamicPropConfig(dynamicProps) {
2612
2667
  const configEntries = {};
2613
2668
  for (const [propName, meta] of Object.entries(dynamicProps)) {
2669
+ if (meta.kind === "declarations") {
2670
+ if (!meta.slotClass || !meta.memberVars || !meta.declarationScaleValues) throw new Error(`buildDynamicPropConfig: declaration prop '${propName}' carries no member metadata — expected slotClass, memberVars and declarationScaleValues.`);
2671
+ configEntries[propName] = {
2672
+ kind: meta.kind,
2673
+ slotClass: meta.slotClass,
2674
+ memberVars: meta.memberVars,
2675
+ declarationScaleValues: meta.declarationScaleValues
2676
+ };
2677
+ continue;
2678
+ }
2614
2679
  if (!meta.varName || !meta.slotClass) throw new Error(`buildDynamicPropConfig: dynamic prop '${propName}' carries no slot metadata — expected varName and slotClass, got keys [${Object.keys(meta).join(", ")}].`);
2615
2680
  const entry = {
2616
2681
  varName: meta.varName,
@@ -2619,16 +2684,39 @@ function buildDynamicPropConfig(dynamicProps) {
2619
2684
  if (meta.property) entry.property = meta.property;
2620
2685
  if (meta.properties && meta.properties.length > 0) entry.properties = meta.properties;
2621
2686
  if (meta.transformName) entry.transformName = meta.transformName;
2687
+ if (meta.transformId) entry.transformId = meta.transformId;
2622
2688
  if (meta.scaleValues && Object.keys(meta.scaleValues).length > 0) entry.scaleValues = meta.scaleValues;
2689
+ if (meta.negative) entry.negative = true;
2690
+ if (meta.strict) entry.strict = true;
2691
+ if (meta.keywords?.length) entry.keywords = meta.keywords;
2623
2692
  configEntries[propName] = entry;
2624
2693
  }
2625
2694
  return configEntries;
2626
2695
  }
2627
2696
  //#endregion
2628
2697
  //#region pipeline/system-props-module.ts
2698
+ /** Compiles the exact entry text in strict mode, the mode of the ES module it
2699
+ * lands in, without evaluating it: one malformed entry would otherwise fail
2700
+ * the whole module. */
2701
+ function parsesAsEntry(entry) {
2702
+ try {
2703
+ Function(`'use strict'; return {\n${entry}};`);
2704
+ return true;
2705
+ } catch {
2706
+ return false;
2707
+ }
2708
+ }
2709
+ /** Delivers the loaded system's configured transform sources that analysis
2710
+ * admitted, keyed by definition and wrapped as `({source})` like the
2711
+ * build-time evaluator registers them. Static evaluation uses the same
2712
+ * definitions, so a same-named project declaration reaches neither path. */
2713
+ function transformsSource(admittedTransforms) {
2714
+ const entries = Object.entries(admittedTransforms).sort(([a], [b]) => a < b ? -1 : 1).map(([name, source]) => ` ${JSON.stringify(name)}: (${source}),\n`).filter(parsesAsEntry);
2715
+ return entries.length > 0 ? `{\n${entries.join("")}}` : "{}";
2716
+ }
2629
2717
  function buildSystemPropsModule(opts) {
2630
2718
  const dynamicPropConfig = buildDynamicPropConfig(opts.dynamicProps);
2631
- return `export const systemPropMap = ${opts.systemPropMapJson};\nexport const systemPropGroups = ${opts.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${opts.transformsSource ?? "{}"};\n`;
2719
+ return `export const systemPropMap = ${opts.systemPropMapJson};\nexport const systemPropGroups = ${opts.groupRegistryJson};\nexport const dynamicPropConfig = ${JSON.stringify(dynamicPropConfig)};\nexport const transforms = ${transformsSource(opts.admittedTransforms)};\n` + (opts.typedSystemProps.length > 0 ? `export const typedSystemProps = ${JSON.stringify(opts.typedSystemProps)};\n` : "");
2632
2720
  }
2633
2721
  //#endregion
2634
2722
  //#region pipeline/timing-waterfall.ts
@@ -3282,6 +3370,12 @@ Object.defineProperty(exports, "resolveThroughPathAliases", {
3282
3370
  return resolveThroughPathAliases;
3283
3371
  }
3284
3372
  });
3373
+ Object.defineProperty(exports, "resolveVerbosity", {
3374
+ enumerable: true,
3375
+ get: function() {
3376
+ return resolveVerbosity;
3377
+ }
3378
+ });
3285
3379
  Object.defineProperty(exports, "runProjectAnalysis", {
3286
3380
  enumerable: true,
3287
3381
  get: function() {