@csszyx/runtime 0.10.1 → 0.10.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1201,7 +1201,7 @@ function omit(classes, selector) {
1201
1201
  function classifySzKey(key) {
1202
1202
  return BOX_ROLE_BY_KEY.get(key);
1203
1203
  }
1204
- function isPlainObject(value) {
1204
+ function isPlainObject$1(value) {
1205
1205
  return typeof value === "object" && value !== null && !Array.isArray(value);
1206
1206
  }
1207
1207
  function matchesKey(key, entry, selector) {
@@ -1222,7 +1222,7 @@ function mergeSzInto(target, source, depth) {
1222
1222
  if (browser.isForbiddenSzKey(key)) continue;
1223
1223
  const sv = source[key];
1224
1224
  const tv = target[key];
1225
- target[key] = isPlainObject(sv) && isPlainObject(tv) ? mergeSzInto({ ...tv }, sv, depth + 1) : sv;
1225
+ target[key] = isPlainObject$1(sv) && isPlainObject$1(tv) ? mergeSzInto({ ...tv }, sv, depth + 1) : sv;
1226
1226
  }
1227
1227
  return target;
1228
1228
  }
@@ -1259,7 +1259,7 @@ function partitionSz(obj, options, outer, inner, depth) {
1259
1259
  outer[key] = value;
1260
1260
  } else if (entry) {
1261
1261
  (entry.role === "inner" ? inner : outer)[key] = value;
1262
- } else if (isPlainObject(value)) {
1262
+ } else if (isPlainObject$1(value)) {
1263
1263
  const subOuter = {};
1264
1264
  const subInner = {};
1265
1265
  partitionSz(value, options, subOuter, subInner, depth + 1);
@@ -1283,7 +1283,7 @@ function filterSz(obj, selector, keep, depth) {
1283
1283
  if (browser.isForbiddenSzKey(key)) continue;
1284
1284
  const value = obj[key];
1285
1285
  const entry = BOX_ROLE_BY_KEY.get(key);
1286
- if (entry || !isPlainObject(value)) {
1286
+ if (entry || !isPlainObject$1(value)) {
1287
1287
  if (matchesKey(key, entry, selector) === keep) result[key] = value;
1288
1288
  } else {
1289
1289
  const sub = filterSz(value, selector, keep, depth + 1);
@@ -1300,7 +1300,7 @@ function hasSz(sz, selector) {
1300
1300
  const value = obj[key];
1301
1301
  const entry = BOX_ROLE_BY_KEY.get(key);
1302
1302
  if (matchesKey(key, entry, selector)) return true;
1303
- if (!entry && isPlainObject(value) && scan(value, depth + 1)) return true;
1303
+ if (!entry && isPlainObject$1(value) && scan(value, depth + 1)) return true;
1304
1304
  }
1305
1305
  return false;
1306
1306
  };
@@ -1313,11 +1313,80 @@ function omitSz(sz, selector) {
1313
1313
  return filterSz(flattenSz(sz, 0), selector, false, 0);
1314
1314
  }
1315
1315
 
1316
- const warned = /* @__PURE__ */ new Set();
1316
+ const AMBIGUOUS_PREFIXES = /* @__PURE__ */ new Set([
1317
+ "flex",
1318
+ // flex-1 (flex shorthand) vs flex-row (flex-direction)
1319
+ "text",
1320
+ // text-sm (font-size) vs text-red-500 (color)
1321
+ "bg",
1322
+ // bg-red-500 (color) vs bg-cover (size) vs bg-center (position)
1323
+ "border",
1324
+ // border-2 (width) vs border-red-500 (color) vs border-solid (style)
1325
+ "divide",
1326
+ // divide-x (width) vs divide-red-500 (color)
1327
+ "ring",
1328
+ // ring-2 (width) vs ring-red-500 (color)
1329
+ "outline"
1330
+ // outline-2 (width) vs outline-red-500 (color)
1331
+ ]);
1332
+ function decodeToken(token) {
1333
+ const decode = globalThis.__csszyx?.decode;
1334
+ if (typeof decode === "function") {
1335
+ return decode(token) ?? token;
1336
+ }
1337
+ return token;
1338
+ }
1339
+ function conflictKey(token) {
1340
+ const base = stripVariant(token);
1341
+ const variant = token.slice(0, token.length - base.length);
1342
+ const norm = normalizeBase(base);
1343
+ if (!norm) {
1344
+ return null;
1345
+ }
1346
+ if (BOX_ROLE_TOKENS.has(norm)) {
1347
+ return null;
1348
+ }
1349
+ for (const [prefix] of BOX_ROLE_PREFIXES) {
1350
+ if (norm === prefix || norm.startsWith(`${prefix}-`)) {
1351
+ if (AMBIGUOUS_PREFIXES.has(prefix)) {
1352
+ return null;
1353
+ }
1354
+ return `${variant}\0${prefix}`;
1355
+ }
1356
+ }
1357
+ return null;
1358
+ }
1359
+ function szcn(...inputs) {
1360
+ const order = [];
1361
+ const byKey = /* @__PURE__ */ new Map();
1362
+ for (const input of inputs) {
1363
+ if (!input || typeof input !== "string") {
1364
+ continue;
1365
+ }
1366
+ for (const token of input.split(/\s+/)) {
1367
+ if (!token) {
1368
+ continue;
1369
+ }
1370
+ const original = decodeToken(token);
1371
+ const key = conflictKey(original) ?? `${original}`;
1372
+ if (byKey.has(key)) {
1373
+ const at = order.indexOf(key);
1374
+ if (at !== -1) {
1375
+ order.splice(at, 1);
1376
+ }
1377
+ }
1378
+ byKey.set(key, token);
1379
+ order.push(key);
1380
+ }
1381
+ }
1382
+ return order.map((key) => byKey.get(key)).join(" ");
1383
+ }
1384
+
1385
+ const warned$1 = /* @__PURE__ */ new Set();
1317
1386
  const RAW_SZ_WARNING = '[csszyx] A raw `sz` object reached the runtime and was dropped before it could leak to the DOM as sz="[object Object]".\nThis means the file was not compiled \u2014 its `sz` produces no CSS. If it lives in a workspace package, add that package to `compilePackages`; otherwise check that the bundler is not skipping the file.';
1318
1387
  function warnRawSz() {
1319
- if (warned.has(RAW_SZ_WARNING)) return;
1320
- warned.add(RAW_SZ_WARNING);
1388
+ if (warned$1.has(RAW_SZ_WARNING)) return;
1389
+ warned$1.add(RAW_SZ_WARNING);
1321
1390
  console.warn(RAW_SZ_WARNING);
1322
1391
  }
1323
1392
  function stripSzProps(props) {
@@ -1333,6 +1402,18 @@ function stripSzProps(props) {
1333
1402
  return rest;
1334
1403
  }
1335
1404
 
1405
+ const warned = /* @__PURE__ */ new Set();
1406
+ function devWarn(message) {
1407
+ if (process.env.NODE_ENV === "production") {
1408
+ return;
1409
+ }
1410
+ if (warned.has(message)) {
1411
+ return;
1412
+ }
1413
+ warned.add(message);
1414
+ console.warn(`[csszyx] ${message}`);
1415
+ }
1416
+
1336
1417
  function deepMerge(target, source, depth = 0) {
1337
1418
  if (depth >= browser.MAX_SZ_DEPTH) {
1338
1419
  throw new browser.SzDepthError();
@@ -1352,8 +1433,97 @@ function deepMerge(target, source, depth = 0) {
1352
1433
  }
1353
1434
  return result;
1354
1435
  }
1436
+ function isPlainObject(value) {
1437
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1438
+ }
1439
+ function validateSzvConfig(config) {
1440
+ if (process.env.NODE_ENV === "production") {
1441
+ return true;
1442
+ }
1443
+ if (!isPlainObject(config)) {
1444
+ devWarn(`szv(config): config must be an object, got ${describe(config)}. Ignoring.`);
1445
+ return false;
1446
+ }
1447
+ if (config.base !== void 0 && !isPlainObject(config.base)) {
1448
+ devWarn(`szv(config): base must be an sz object, got ${describe(config.base)}.`);
1449
+ }
1450
+ if (!isPlainObject(config.variants)) {
1451
+ devWarn(
1452
+ `szv(config): variants is required and must be an object, got ${describe(config.variants)}. Ignoring.`
1453
+ );
1454
+ return false;
1455
+ }
1456
+ for (const dim of Object.keys(config.variants)) {
1457
+ const values = config.variants[dim];
1458
+ if (!isPlainObject(values)) {
1459
+ devWarn(
1460
+ `szv(config): variants.${dim} must be an object of values, got ${describe(values)}.`
1461
+ );
1462
+ continue;
1463
+ }
1464
+ for (const token of Object.keys(values)) {
1465
+ const v = values[token];
1466
+ if (v !== null && v !== void 0 && !isPlainObject(v)) {
1467
+ devWarn(
1468
+ `szv(config): variants.${dim}.${token} must be an sz object, got ${describe(v)}. It will be skipped.`
1469
+ );
1470
+ } else if (isPlainObject(v)) {
1471
+ assertBoundedDepth(v, `variants.${dim}.${token}`);
1472
+ }
1473
+ }
1474
+ }
1475
+ if (config.defaultVariants !== void 0 && !isPlainObject(config.defaultVariants)) {
1476
+ devWarn(
1477
+ `szv(config): defaultVariants must be an object, got ${describe(config.defaultVariants)}.`
1478
+ );
1479
+ }
1480
+ return true;
1481
+ }
1482
+ function assertBoundedDepth(obj, where, depth = 0) {
1483
+ if (depth >= browser.MAX_SZ_DEPTH) {
1484
+ devWarn(
1485
+ `szv(config): ${where} nests deeper than ${browser.MAX_SZ_DEPTH} levels; it will be rejected at render.`
1486
+ );
1487
+ return;
1488
+ }
1489
+ for (const key of Object.keys(obj)) {
1490
+ if (browser.isForbiddenSzKey(key)) {
1491
+ devWarn(`szv(config): ${where} has a forbidden key "${key}"; it will be skipped.`);
1492
+ continue;
1493
+ }
1494
+ const v = obj[key];
1495
+ if (isPlainObject(v)) {
1496
+ assertBoundedDepth(v, `${where}.${key}`, depth + 1);
1497
+ }
1498
+ }
1499
+ }
1500
+ function describe(value) {
1501
+ if (value === null) return "null";
1502
+ if (Array.isArray(value)) return "an array";
1503
+ return typeof value;
1504
+ }
1355
1505
  function szv(config) {
1506
+ const configValid = validateSzvConfig(config);
1356
1507
  return function szVariantFn(selection) {
1508
+ if (!configValid) {
1509
+ return isPlainObject(config?.base) ? { ...config.base } : {};
1510
+ }
1511
+ if (process.env.NODE_ENV !== "production" && selection) {
1512
+ for (const key of Object.keys(selection)) {
1513
+ if (!(key in config.variants)) {
1514
+ devWarn(
1515
+ `szv()(selection): unknown variant "${key}" \u2014 not declared in config.variants.`
1516
+ );
1517
+ continue;
1518
+ }
1519
+ const val = selection[key];
1520
+ if (val !== null && val !== void 0 && !(String(val) in config.variants[key])) {
1521
+ devWarn(
1522
+ `szv()(selection): "${String(val)}" is not a value of variant "${key}" \u2014 it has no styles.`
1523
+ );
1524
+ }
1525
+ }
1526
+ }
1357
1527
  let result = config.base ? { ...config.base } : {};
1358
1528
  const resolved = { ...config.defaultVariants };
1359
1529
  if (selection) {
@@ -1367,13 +1537,13 @@ function szv(config) {
1367
1537
  }
1368
1538
  }
1369
1539
  }
1370
- for (const variantKey of Object.keys(config.variants)) {
1540
+ for (const variantKey of Object.keys(config.variants ?? {})) {
1371
1541
  const selectedValue = resolved[variantKey];
1372
1542
  if (selectedValue === null || selectedValue === void 0) {
1373
1543
  continue;
1374
1544
  }
1375
1545
  const variantObj = config.variants[variantKey][selectedValue];
1376
- if (variantObj) {
1546
+ if (isPlainObject(variantObj)) {
1377
1547
  result = deepMerge(result, variantObj);
1378
1548
  }
1379
1549
  }
@@ -1466,6 +1636,7 @@ exports.splitBox = splitBox;
1466
1636
  exports.splitBoxSz = splitBoxSz;
1467
1637
  exports.startHydration = startHydration;
1468
1638
  exports.stripSzProps = stripSzProps;
1639
+ exports.szcn = szcn;
1469
1640
  exports.szv = szv;
1470
1641
  exports.validateHydrationClass = validateHydrationClass;
1471
1642
  exports.verifyAllTokens = verifyAllTokens;
package/dist/index.d.cts CHANGED
@@ -598,6 +598,20 @@ declare function startHydration(): void;
598
598
  */
599
599
  declare function endHydration(): void;
600
600
 
601
+ /**
602
+ * Merge className strings with last-wins override per utility, mangle-aware.
603
+ *
604
+ * Intended for the single resolution point in a layered design-system component
605
+ * (typically at the leaf Box): combine the component's default classes with the
606
+ * forwarded override so the override wins on a same-utility collision, while
607
+ * keeping production mangling intact (unlike npm tailwind-merge).
608
+ *
609
+ * @param inputs - Class strings; falsy inputs (`false`/`null`/`undefined`/`''`) are skipped.
610
+ * @returns The merged className string.
611
+ * @example szcn('gap-2 p-4', 'gap-8') // → 'p-4 gap-8' (gap-8 overrides gap-2)
612
+ */
613
+ declare function szcn(...inputs: (string | false | null | undefined)[]): string;
614
+
601
615
  /** Which side of the CSS box-model border a property acts on. */
602
616
  type BoxRole = 'outer' | 'inner';
603
617
 
@@ -947,5 +961,5 @@ declare function isRuntimeInitialized(): boolean;
947
961
  */
948
962
  declare function resetRuntime(): void;
949
963
 
950
- export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
964
+ export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szcn, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
951
965
  export type { BoxRole, BoxSelector, Classification, HydrationError, HydrationErrorType, MangleMap, RecoveryManifest, RecoveryMode, RuntimeConfig, SSRContext, SplitBoxOptions, SplitBoxResult, SplitBoxSzOptions, SplitBoxSzResult, SzInput, TokenData, VerificationResult };
package/dist/index.d.mts CHANGED
@@ -598,6 +598,20 @@ declare function startHydration(): void;
598
598
  */
599
599
  declare function endHydration(): void;
600
600
 
601
+ /**
602
+ * Merge className strings with last-wins override per utility, mangle-aware.
603
+ *
604
+ * Intended for the single resolution point in a layered design-system component
605
+ * (typically at the leaf Box): combine the component's default classes with the
606
+ * forwarded override so the override wins on a same-utility collision, while
607
+ * keeping production mangling intact (unlike npm tailwind-merge).
608
+ *
609
+ * @param inputs - Class strings; falsy inputs (`false`/`null`/`undefined`/`''`) are skipped.
610
+ * @returns The merged className string.
611
+ * @example szcn('gap-2 p-4', 'gap-8') // → 'p-4 gap-8' (gap-8 overrides gap-2)
612
+ */
613
+ declare function szcn(...inputs: (string | false | null | undefined)[]): string;
614
+
601
615
  /** Which side of the CSS box-model border a property acts on. */
602
616
  type BoxRole = 'outer' | 'inner';
603
617
 
@@ -947,5 +961,5 @@ declare function isRuntimeInitialized(): boolean;
947
961
  */
948
962
  declare function resetRuntime(): void;
949
963
 
950
- export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
964
+ export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szcn, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
951
965
  export type { BoxRole, BoxSelector, Classification, HydrationError, HydrationErrorType, MangleMap, RecoveryManifest, RecoveryMode, RuntimeConfig, SSRContext, SplitBoxOptions, SplitBoxResult, SplitBoxSzOptions, SplitBoxSzResult, SzInput, TokenData, VerificationResult };
package/dist/index.mjs CHANGED
@@ -1199,7 +1199,7 @@ function omit(classes, selector) {
1199
1199
  function classifySzKey(key) {
1200
1200
  return BOX_ROLE_BY_KEY.get(key);
1201
1201
  }
1202
- function isPlainObject(value) {
1202
+ function isPlainObject$1(value) {
1203
1203
  return typeof value === "object" && value !== null && !Array.isArray(value);
1204
1204
  }
1205
1205
  function matchesKey(key, entry, selector) {
@@ -1220,7 +1220,7 @@ function mergeSzInto(target, source, depth) {
1220
1220
  if (isForbiddenSzKey(key)) continue;
1221
1221
  const sv = source[key];
1222
1222
  const tv = target[key];
1223
- target[key] = isPlainObject(sv) && isPlainObject(tv) ? mergeSzInto({ ...tv }, sv, depth + 1) : sv;
1223
+ target[key] = isPlainObject$1(sv) && isPlainObject$1(tv) ? mergeSzInto({ ...tv }, sv, depth + 1) : sv;
1224
1224
  }
1225
1225
  return target;
1226
1226
  }
@@ -1257,7 +1257,7 @@ function partitionSz(obj, options, outer, inner, depth) {
1257
1257
  outer[key] = value;
1258
1258
  } else if (entry) {
1259
1259
  (entry.role === "inner" ? inner : outer)[key] = value;
1260
- } else if (isPlainObject(value)) {
1260
+ } else if (isPlainObject$1(value)) {
1261
1261
  const subOuter = {};
1262
1262
  const subInner = {};
1263
1263
  partitionSz(value, options, subOuter, subInner, depth + 1);
@@ -1281,7 +1281,7 @@ function filterSz(obj, selector, keep, depth) {
1281
1281
  if (isForbiddenSzKey(key)) continue;
1282
1282
  const value = obj[key];
1283
1283
  const entry = BOX_ROLE_BY_KEY.get(key);
1284
- if (entry || !isPlainObject(value)) {
1284
+ if (entry || !isPlainObject$1(value)) {
1285
1285
  if (matchesKey(key, entry, selector) === keep) result[key] = value;
1286
1286
  } else {
1287
1287
  const sub = filterSz(value, selector, keep, depth + 1);
@@ -1298,7 +1298,7 @@ function hasSz(sz, selector) {
1298
1298
  const value = obj[key];
1299
1299
  const entry = BOX_ROLE_BY_KEY.get(key);
1300
1300
  if (matchesKey(key, entry, selector)) return true;
1301
- if (!entry && isPlainObject(value) && scan(value, depth + 1)) return true;
1301
+ if (!entry && isPlainObject$1(value) && scan(value, depth + 1)) return true;
1302
1302
  }
1303
1303
  return false;
1304
1304
  };
@@ -1311,11 +1311,80 @@ function omitSz(sz, selector) {
1311
1311
  return filterSz(flattenSz(sz, 0), selector, false, 0);
1312
1312
  }
1313
1313
 
1314
- const warned = /* @__PURE__ */ new Set();
1314
+ const AMBIGUOUS_PREFIXES = /* @__PURE__ */ new Set([
1315
+ "flex",
1316
+ // flex-1 (flex shorthand) vs flex-row (flex-direction)
1317
+ "text",
1318
+ // text-sm (font-size) vs text-red-500 (color)
1319
+ "bg",
1320
+ // bg-red-500 (color) vs bg-cover (size) vs bg-center (position)
1321
+ "border",
1322
+ // border-2 (width) vs border-red-500 (color) vs border-solid (style)
1323
+ "divide",
1324
+ // divide-x (width) vs divide-red-500 (color)
1325
+ "ring",
1326
+ // ring-2 (width) vs ring-red-500 (color)
1327
+ "outline"
1328
+ // outline-2 (width) vs outline-red-500 (color)
1329
+ ]);
1330
+ function decodeToken(token) {
1331
+ const decode = globalThis.__csszyx?.decode;
1332
+ if (typeof decode === "function") {
1333
+ return decode(token) ?? token;
1334
+ }
1335
+ return token;
1336
+ }
1337
+ function conflictKey(token) {
1338
+ const base = stripVariant(token);
1339
+ const variant = token.slice(0, token.length - base.length);
1340
+ const norm = normalizeBase(base);
1341
+ if (!norm) {
1342
+ return null;
1343
+ }
1344
+ if (BOX_ROLE_TOKENS.has(norm)) {
1345
+ return null;
1346
+ }
1347
+ for (const [prefix] of BOX_ROLE_PREFIXES) {
1348
+ if (norm === prefix || norm.startsWith(`${prefix}-`)) {
1349
+ if (AMBIGUOUS_PREFIXES.has(prefix)) {
1350
+ return null;
1351
+ }
1352
+ return `${variant}\0${prefix}`;
1353
+ }
1354
+ }
1355
+ return null;
1356
+ }
1357
+ function szcn(...inputs) {
1358
+ const order = [];
1359
+ const byKey = /* @__PURE__ */ new Map();
1360
+ for (const input of inputs) {
1361
+ if (!input || typeof input !== "string") {
1362
+ continue;
1363
+ }
1364
+ for (const token of input.split(/\s+/)) {
1365
+ if (!token) {
1366
+ continue;
1367
+ }
1368
+ const original = decodeToken(token);
1369
+ const key = conflictKey(original) ?? `${original}`;
1370
+ if (byKey.has(key)) {
1371
+ const at = order.indexOf(key);
1372
+ if (at !== -1) {
1373
+ order.splice(at, 1);
1374
+ }
1375
+ }
1376
+ byKey.set(key, token);
1377
+ order.push(key);
1378
+ }
1379
+ }
1380
+ return order.map((key) => byKey.get(key)).join(" ");
1381
+ }
1382
+
1383
+ const warned$1 = /* @__PURE__ */ new Set();
1315
1384
  const RAW_SZ_WARNING = '[csszyx] A raw `sz` object reached the runtime and was dropped before it could leak to the DOM as sz="[object Object]".\nThis means the file was not compiled \u2014 its `sz` produces no CSS. If it lives in a workspace package, add that package to `compilePackages`; otherwise check that the bundler is not skipping the file.';
1316
1385
  function warnRawSz() {
1317
- if (warned.has(RAW_SZ_WARNING)) return;
1318
- warned.add(RAW_SZ_WARNING);
1386
+ if (warned$1.has(RAW_SZ_WARNING)) return;
1387
+ warned$1.add(RAW_SZ_WARNING);
1319
1388
  console.warn(RAW_SZ_WARNING);
1320
1389
  }
1321
1390
  function stripSzProps(props) {
@@ -1331,6 +1400,18 @@ function stripSzProps(props) {
1331
1400
  return rest;
1332
1401
  }
1333
1402
 
1403
+ const warned = /* @__PURE__ */ new Set();
1404
+ function devWarn(message) {
1405
+ if (process.env.NODE_ENV === "production") {
1406
+ return;
1407
+ }
1408
+ if (warned.has(message)) {
1409
+ return;
1410
+ }
1411
+ warned.add(message);
1412
+ console.warn(`[csszyx] ${message}`);
1413
+ }
1414
+
1334
1415
  function deepMerge(target, source, depth = 0) {
1335
1416
  if (depth >= MAX_SZ_DEPTH) {
1336
1417
  throw new SzDepthError();
@@ -1350,8 +1431,97 @@ function deepMerge(target, source, depth = 0) {
1350
1431
  }
1351
1432
  return result;
1352
1433
  }
1434
+ function isPlainObject(value) {
1435
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1436
+ }
1437
+ function validateSzvConfig(config) {
1438
+ if (process.env.NODE_ENV === "production") {
1439
+ return true;
1440
+ }
1441
+ if (!isPlainObject(config)) {
1442
+ devWarn(`szv(config): config must be an object, got ${describe(config)}. Ignoring.`);
1443
+ return false;
1444
+ }
1445
+ if (config.base !== void 0 && !isPlainObject(config.base)) {
1446
+ devWarn(`szv(config): base must be an sz object, got ${describe(config.base)}.`);
1447
+ }
1448
+ if (!isPlainObject(config.variants)) {
1449
+ devWarn(
1450
+ `szv(config): variants is required and must be an object, got ${describe(config.variants)}. Ignoring.`
1451
+ );
1452
+ return false;
1453
+ }
1454
+ for (const dim of Object.keys(config.variants)) {
1455
+ const values = config.variants[dim];
1456
+ if (!isPlainObject(values)) {
1457
+ devWarn(
1458
+ `szv(config): variants.${dim} must be an object of values, got ${describe(values)}.`
1459
+ );
1460
+ continue;
1461
+ }
1462
+ for (const token of Object.keys(values)) {
1463
+ const v = values[token];
1464
+ if (v !== null && v !== void 0 && !isPlainObject(v)) {
1465
+ devWarn(
1466
+ `szv(config): variants.${dim}.${token} must be an sz object, got ${describe(v)}. It will be skipped.`
1467
+ );
1468
+ } else if (isPlainObject(v)) {
1469
+ assertBoundedDepth(v, `variants.${dim}.${token}`);
1470
+ }
1471
+ }
1472
+ }
1473
+ if (config.defaultVariants !== void 0 && !isPlainObject(config.defaultVariants)) {
1474
+ devWarn(
1475
+ `szv(config): defaultVariants must be an object, got ${describe(config.defaultVariants)}.`
1476
+ );
1477
+ }
1478
+ return true;
1479
+ }
1480
+ function assertBoundedDepth(obj, where, depth = 0) {
1481
+ if (depth >= MAX_SZ_DEPTH) {
1482
+ devWarn(
1483
+ `szv(config): ${where} nests deeper than ${MAX_SZ_DEPTH} levels; it will be rejected at render.`
1484
+ );
1485
+ return;
1486
+ }
1487
+ for (const key of Object.keys(obj)) {
1488
+ if (isForbiddenSzKey(key)) {
1489
+ devWarn(`szv(config): ${where} has a forbidden key "${key}"; it will be skipped.`);
1490
+ continue;
1491
+ }
1492
+ const v = obj[key];
1493
+ if (isPlainObject(v)) {
1494
+ assertBoundedDepth(v, `${where}.${key}`, depth + 1);
1495
+ }
1496
+ }
1497
+ }
1498
+ function describe(value) {
1499
+ if (value === null) return "null";
1500
+ if (Array.isArray(value)) return "an array";
1501
+ return typeof value;
1502
+ }
1353
1503
  function szv(config) {
1504
+ const configValid = validateSzvConfig(config);
1354
1505
  return function szVariantFn(selection) {
1506
+ if (!configValid) {
1507
+ return isPlainObject(config?.base) ? { ...config.base } : {};
1508
+ }
1509
+ if (process.env.NODE_ENV !== "production" && selection) {
1510
+ for (const key of Object.keys(selection)) {
1511
+ if (!(key in config.variants)) {
1512
+ devWarn(
1513
+ `szv()(selection): unknown variant "${key}" \u2014 not declared in config.variants.`
1514
+ );
1515
+ continue;
1516
+ }
1517
+ const val = selection[key];
1518
+ if (val !== null && val !== void 0 && !(String(val) in config.variants[key])) {
1519
+ devWarn(
1520
+ `szv()(selection): "${String(val)}" is not a value of variant "${key}" \u2014 it has no styles.`
1521
+ );
1522
+ }
1523
+ }
1524
+ }
1355
1525
  let result = config.base ? { ...config.base } : {};
1356
1526
  const resolved = { ...config.defaultVariants };
1357
1527
  if (selection) {
@@ -1365,13 +1535,13 @@ function szv(config) {
1365
1535
  }
1366
1536
  }
1367
1537
  }
1368
- for (const variantKey of Object.keys(config.variants)) {
1538
+ for (const variantKey of Object.keys(config.variants ?? {})) {
1369
1539
  const selectedValue = resolved[variantKey];
1370
1540
  if (selectedValue === null || selectedValue === void 0) {
1371
1541
  continue;
1372
1542
  }
1373
1543
  const variantObj = config.variants[variantKey][selectedValue];
1374
- if (variantObj) {
1544
+ if (isPlainObject(variantObj)) {
1375
1545
  result = deepMerge(result, variantObj);
1376
1546
  }
1377
1547
  }
@@ -1420,4 +1590,4 @@ function resetRuntime() {
1420
1590
  runtimeState.initialized = false;
1421
1591
  }
1422
1592
 
1423
- export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
1593
+ export { DEFAULT_RUNTIME_CONFIG, VERSION, _sz, _sz2, _sz3, _szMerge, abortHydration, attemptCSRRecovery, classify, classifySzKey, clearHydrationErrors, computeMangleChecksumAsync, disableCSRRecovery, enableCSRRecovery, endHydration, getAbortedSubtreeCount, getHydrationErrors, getRecoveryMode, getRuntimeConfig, getSSRContext, guardHydration, has, hasRecoveryToken, hasSz, initRuntime, isCSRRecoveryAllowed, isHydrating, isHydrationAborted, isRuntimeInitialized, isSSREnvironment, isValidMangleMap, isValidManifest, loadMangleMapFromDOM, loadManifestFromDOM, omit, omitSz, pick, pickSz, resetRuntime, splitBox, splitBoxSz, startHydration, stripSzProps, szcn, szv, validateHydrationClass, verifyAllTokens, verifyMangleChecksum, verifyMangleChecksumAsync, verifyMangleMapIntegrity, verifyRecoveryToken };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@csszyx/runtime",
3
- "version": "0.10.1",
3
+ "version": "0.10.3",
4
4
  "description": "Runtime helpers and hydration guards for csszyx",
5
5
  "keywords": [
6
6
  "csszyx",
@@ -49,8 +49,8 @@
49
49
  "dist"
50
50
  ],
51
51
  "dependencies": {
52
- "@csszyx/core": "0.10.1",
53
- "@csszyx/compiler": "0.10.1"
52
+ "@csszyx/compiler": "0.10.3",
53
+ "@csszyx/core": "0.10.3"
54
54
  },
55
55
  "devDependencies": {
56
56
  "@types/node": "^20.11.0",