assign-gingerly 0.0.14 → 0.0.16

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/README.md CHANGED
@@ -1400,14 +1400,48 @@ console.log(instance.count); // 42 (parsed from attribute)
1400
1400
  console.log(instance.theme); // 'dark' (parsed from attribute)
1401
1401
  ```
1402
1402
 
1403
+ **Example without enhKey:**
1404
+
1405
+ ```TypeScript
1406
+ // withAttrs works even without enhKey
1407
+ class SimpleEnhancement {
1408
+ element;
1409
+ ctx;
1410
+ value = null;
1411
+
1412
+ constructor(oElement, ctx, initVals) {
1413
+ this.element = oElement;
1414
+ this.ctx = ctx;
1415
+ if (initVals) {
1416
+ Object.assign(this, initVals);
1417
+ }
1418
+ }
1419
+ }
1420
+
1421
+ const element = document.createElement('div');
1422
+ element.setAttribute('data-value', 'test123');
1423
+
1424
+ const config = {
1425
+ spawn: SimpleEnhancement,
1426
+ // No enhKey - attributes still parsed!
1427
+ withAttrs: {
1428
+ base: 'data-',
1429
+ value: '${base}value'
1430
+ }
1431
+ };
1432
+
1433
+ const instance = element.enh.get(config);
1434
+ console.log(instance.value); // 'test123' (parsed from attribute)
1435
+ ```
1436
+
1403
1437
  **How it works:**
1404
1438
  1. When an enhancement is spawned via `enh.get()`, `enh.set`, or `assignGingerly()`
1405
1439
  2. If the registry item has a `withAttrs` property defined
1406
1440
  3. `parseWithAttrs(element, registryItem.withAttrs)` is automatically called
1407
- 4. The parsed attributes are merged into `initVals` (along with any existing values from `element.enh[enhKey]`)
1408
- 5. The merged `initVals` is passed to the enhancement constructor
1441
+ 4. The parsed attributes are passed to the enhancement constructor as `initVals`
1442
+ 5. If the registry item also has an `enhKey`, the parsed attributes are merged with any existing values from `element.enh[enhKey]` (existing values take precedence)
1409
1443
 
1410
- **Precedence**: If both parsed attributes and existing `element.enh[enhKey]` values exist, the existing values take precedence over parsed attributes.
1444
+ **Note**: `withAttrs` works with or without `enhKey`. When there's no `enhKey`, the parsed attributes are passed directly to the constructor. When there is an `enhKey`, they're merged with any pre-existing values on the enh container.
1411
1445
 
1412
1446
 
1413
1447
 
@@ -2146,6 +2180,187 @@ assignGingerly(element, attrs);
2146
2180
 
2147
2181
  </details>
2148
2182
 
2183
+ ## Building CSS Queries with `buildCSSQuery`
2184
+
2185
+ The `buildCSSQuery` function generates CSS selector strings that match elements with attributes defined in an enhancement configuration's `withAttrs`. This is particularly useful for libraries like mount-observer that need to find elements that should be enhanced.
2186
+
2187
+ ### Basic Usage
2188
+
2189
+ ```TypeScript
2190
+ import { buildCSSQuery } from 'assign-gingerly';
2191
+
2192
+ const config = {
2193
+ spawn: MyEnhancement,
2194
+ withAttrs: {
2195
+ base: 'my-component',
2196
+ theme: '${base}-theme'
2197
+ }
2198
+ };
2199
+
2200
+ const query = buildCSSQuery(config, 'div, span');
2201
+ console.log(query);
2202
+ // 'div[my-component], span[my-component], div[enh-my-component], span[enh-my-component],
2203
+ // div[my-component-theme], span[my-component-theme], div[enh-my-component-theme], span[enh-my-component-theme]'
2204
+
2205
+ // Use with querySelector
2206
+ const elements = document.querySelectorAll(query);
2207
+ ```
2208
+
2209
+ ### How It Works
2210
+
2211
+ `buildCSSQuery` creates a cross-product of:
2212
+ 1. **Selectors**: The CSS selectors you provide (e.g., `'div, span'`)
2213
+ 2. **Attributes**: All attribute names from `withAttrs` (resolving template variables)
2214
+ 3. **Prefixes**: Both unprefixed and `enh-` prefixed versions
2215
+
2216
+ This ensures you find all elements that might be enhanced, regardless of whether they use the `enh-` prefix or not.
2217
+
2218
+ ### Template Variable Resolution
2219
+
2220
+ Template variables in `withAttrs` are automatically resolved:
2221
+
2222
+ ```TypeScript
2223
+ const config = {
2224
+ spawn: BeABeacon,
2225
+ withAttrs: {
2226
+ base: 'be-a-beacon',
2227
+ theme: '${base}-theme',
2228
+ size: '${base}-size'
2229
+ }
2230
+ };
2231
+
2232
+ buildCSSQuery(config, 'template, script');
2233
+ // Returns selectors for: be-a-beacon, be-a-beacon-theme, be-a-beacon-size
2234
+ // Each with both prefixed and unprefixed versions
2235
+ ```
2236
+
2237
+ ### Complex Selectors
2238
+
2239
+ The function supports any valid CSS selector:
2240
+
2241
+ ```TypeScript
2242
+ const config = {
2243
+ spawn: MyEnhancement,
2244
+ withAttrs: {
2245
+ base: 'data-enhanced'
2246
+ }
2247
+ };
2248
+
2249
+ // Classes and IDs
2250
+ buildCSSQuery(config, 'div.highlight, span#special');
2251
+ // 'div.highlight[data-enhanced], span#special[data-enhanced], ...'
2252
+
2253
+ // Combinators
2254
+ buildCSSQuery(config, 'div > span, ul li');
2255
+ // 'div > span[data-enhanced], ul li[data-enhanced], ...'
2256
+
2257
+ // Pseudo-classes
2258
+ buildCSSQuery(config, 'div:hover, span:first-child');
2259
+ // 'div:hover[data-enhanced], span:first-child[data-enhanced], ...'
2260
+
2261
+ // Attribute selectors
2262
+ buildCSSQuery(config, 'div[existing-attr]');
2263
+ // 'div[existing-attr][data-enhanced], ...'
2264
+ ```
2265
+
2266
+ ### Underscore-Prefixed Keys Excluded
2267
+
2268
+ Configuration keys starting with `_` are excluded from the query:
2269
+
2270
+ ```TypeScript
2271
+ const config = {
2272
+ spawn: MyEnhancement,
2273
+ withAttrs: {
2274
+ base: 'my-attr',
2275
+ _base: {
2276
+ mapsTo: 'something' // Config only, not an attribute
2277
+ },
2278
+ theme: '${base}-theme',
2279
+ _theme: {
2280
+ instanceOf: 'String' // Config only
2281
+ }
2282
+ }
2283
+ };
2284
+
2285
+ buildCSSQuery(config, 'div');
2286
+ // Only includes: my-attr and my-attr-theme
2287
+ // Does NOT include: _base or _theme
2288
+ ```
2289
+
2290
+ ### Edge Cases
2291
+
2292
+ **Empty inputs return empty string:**
2293
+ ```TypeScript
2294
+ buildCSSQuery({ spawn: MyClass }, 'div'); // '' (no withAttrs)
2295
+ buildCSSQuery({ spawn: MyClass, withAttrs: {} }, 'div'); // '' (empty withAttrs)
2296
+ buildCSSQuery({ spawn: MyClass, withAttrs: { base: 'x' } }, ''); // '' (empty selectors)
2297
+ ```
2298
+
2299
+ **Deduplication:**
2300
+ ```TypeScript
2301
+ buildCSSQuery(config, 'div, div, div');
2302
+ // Duplicates are removed automatically
2303
+ ```
2304
+
2305
+ **Whitespace handling:**
2306
+ ```TypeScript
2307
+ buildCSSQuery(config, ' div , span , p ');
2308
+ // Whitespace is trimmed automatically
2309
+ ```
2310
+
2311
+ ### Use Cases
2312
+
2313
+ 1. **Mount Observer Integration**: Find elements that need enhancement
2314
+ ```TypeScript
2315
+ const query = buildCSSQuery(enhancementConfig, '*');
2316
+ const observer = new MutationObserver(() => {
2317
+ const elements = document.querySelectorAll(query);
2318
+ elements.forEach(el => enhance(el));
2319
+ });
2320
+ ```
2321
+
2322
+ 2. **Batch Enhancement**: Enhance all matching elements at once
2323
+ ```TypeScript
2324
+ const query = buildCSSQuery(config, 'template, script');
2325
+ document.querySelectorAll(query).forEach(el => {
2326
+ const instance = el.enh.get(config);
2327
+ });
2328
+ ```
2329
+
2330
+ 3. **Conditional Enhancement**: Find elements in specific contexts
2331
+ ```TypeScript
2332
+ const query = buildCSSQuery(config, '.container > div');
2333
+ const elements = document.querySelectorAll(query);
2334
+ ```
2335
+
2336
+ ### API Reference
2337
+
2338
+ ```TypeScript
2339
+ function buildCSSQuery(
2340
+ config: EnhancementConfig,
2341
+ selectors: string
2342
+ ): string
2343
+ ```
2344
+
2345
+ **Parameters:**
2346
+ - `config`: Enhancement configuration with `withAttrs` property
2347
+ - `selectors`: Comma-separated CSS selectors (e.g., `'div, span'`)
2348
+
2349
+ **Returns:**
2350
+ - CSS query string with cross-product of selectors and attributes
2351
+ - Empty string if `withAttrs` is missing or empty
2352
+
2353
+ **Throws:**
2354
+ - Error if template variables have circular references
2355
+ - Error if template variables reference undefined keys
2356
+
2357
+ ### Performance Notes
2358
+
2359
+ - The function is synchronous and fast
2360
+ - Resulting queries can be long with many attributes, but CSS engines handle this efficiently
2361
+ - Queries are deduplicated automatically
2362
+ - Consider caching the result if calling repeatedly with the same config
2363
+
2149
2364
  <!--
2150
2365
 
2151
2366
  ### Complete Example
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Resolves template variables in a string recursively
3
+ * @param template - Template string with ${var} placeholders
4
+ * @param patterns - The patterns object containing variable values
5
+ * @param resolvedCache - Cache of already resolved values
6
+ * @param visitedKeys - Set of keys being resolved (for cycle detection)
7
+ * @returns Resolved string
8
+ */
9
+ function resolveTemplate(template, patterns, resolvedCache, visitedKeys = new Set()) {
10
+ return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
11
+ // Check if already resolved
12
+ if (resolvedCache.has(varName)) {
13
+ return resolvedCache.get(varName);
14
+ }
15
+ // Check for circular reference
16
+ if (visitedKeys.has(varName)) {
17
+ throw new Error(`Circular reference detected in template variable: ${varName}`);
18
+ }
19
+ const value = patterns[varName];
20
+ if (value === undefined) {
21
+ throw new Error(`Undefined template variable: ${varName}`);
22
+ }
23
+ if (typeof value === 'string') {
24
+ // Recursively resolve
25
+ visitedKeys.add(varName);
26
+ const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
27
+ visitedKeys.delete(varName);
28
+ resolvedCache.set(varName, resolved);
29
+ return resolved;
30
+ }
31
+ // Non-string value, return as-is
32
+ return String(value);
33
+ });
34
+ }
35
+ /**
36
+ * Extracts attribute names from withAttrs configuration
37
+ * Resolves template variables and excludes underscore-prefixed config keys
38
+ * @param withAttrs - The attribute patterns configuration
39
+ * @returns Array of resolved attribute names
40
+ */
41
+ function extractAttributeNames(withAttrs) {
42
+ const names = [];
43
+ const resolvedCache = new Map();
44
+ // Add base if present
45
+ if ('base' in withAttrs && typeof withAttrs.base === 'string') {
46
+ names.push(withAttrs.base);
47
+ }
48
+ // Add other attributes (skip underscore-prefixed config keys)
49
+ for (const key in withAttrs) {
50
+ if (key === 'base' || key.startsWith('_')) {
51
+ continue;
52
+ }
53
+ const value = withAttrs[key];
54
+ if (typeof value === 'string') {
55
+ // Resolve template variables
56
+ const resolved = resolveTemplate(value, withAttrs, resolvedCache);
57
+ names.push(resolved);
58
+ }
59
+ }
60
+ return names;
61
+ }
62
+ /**
63
+ * Builds a CSS query selector that matches elements with attributes from withAttrs
64
+ * Creates a cross-product of selectors and attribute names (both prefixed and unprefixed)
65
+ *
66
+ * @param config - Enhancement configuration with withAttrs
67
+ * @param selectors - Comma-separated CSS selectors to match (e.g., 'template, script')
68
+ * @returns CSS query string with cross-product of selectors and attributes
69
+ *
70
+ * @example
71
+ * const config = {
72
+ * spawn: MyClass,
73
+ * withAttrs: {
74
+ * base: 'my-attr',
75
+ * theme: '${base}-theme'
76
+ * }
77
+ * };
78
+ *
79
+ * buildCSSQuery(config, 'div, span');
80
+ * // Returns: 'div[my-attr], span[my-attr], div[enh-my-attr], span[enh-my-attr],
81
+ * // div[my-attr-theme], span[my-attr-theme], div[enh-my-attr-theme], span[enh-my-attr-theme]'
82
+ */
83
+ export function buildCSSQuery(config, selectors) {
84
+ // Validate inputs
85
+ if (!config.withAttrs || !selectors) {
86
+ return '';
87
+ }
88
+ // Parse and normalize selectors
89
+ const selectorList = selectors
90
+ .split(',')
91
+ .map(s => s.trim())
92
+ .filter(s => s.length > 0);
93
+ if (selectorList.length === 0) {
94
+ return '';
95
+ }
96
+ // Extract and resolve attribute names
97
+ const attrNames = extractAttributeNames(config.withAttrs);
98
+ if (attrNames.length === 0) {
99
+ return '';
100
+ }
101
+ // Build cross-product of selectors × attributes × prefixes
102
+ const queries = [];
103
+ for (const selector of selectorList) {
104
+ for (const attrName of attrNames) {
105
+ // Unprefixed version
106
+ queries.push(`${selector}[${attrName}]`);
107
+ // enh- prefixed version
108
+ queries.push(`${selector}[enh-${attrName}]`);
109
+ }
110
+ }
111
+ // Deduplicate and join
112
+ const uniqueQueries = [...new Set(queries)];
113
+ return uniqueQueries.join(', ');
114
+ }
@@ -0,0 +1,142 @@
1
+ import { EnhancementConfig, AttrPatterns } from './types/assign-gingerly/types';
2
+
3
+ /**
4
+ * Resolves template variables in a string recursively
5
+ * @param template - Template string with ${var} placeholders
6
+ * @param patterns - The patterns object containing variable values
7
+ * @param resolvedCache - Cache of already resolved values
8
+ * @param visitedKeys - Set of keys being resolved (for cycle detection)
9
+ * @returns Resolved string
10
+ */
11
+ function resolveTemplate(
12
+ template: string,
13
+ patterns: Record<string, any>,
14
+ resolvedCache: Map<string, string>,
15
+ visitedKeys: Set<string> = new Set()
16
+ ): string {
17
+ return template.replace(/\$\{(\w+)\}/g, (match, varName) => {
18
+ // Check if already resolved
19
+ if (resolvedCache.has(varName)) {
20
+ return resolvedCache.get(varName)!;
21
+ }
22
+
23
+ // Check for circular reference
24
+ if (visitedKeys.has(varName)) {
25
+ throw new Error(`Circular reference detected in template variable: ${varName}`);
26
+ }
27
+
28
+ const value = patterns[varName];
29
+
30
+ if (value === undefined) {
31
+ throw new Error(`Undefined template variable: ${varName}`);
32
+ }
33
+
34
+ if (typeof value === 'string') {
35
+ // Recursively resolve
36
+ visitedKeys.add(varName);
37
+ const resolved = resolveTemplate(value, patterns, resolvedCache, visitedKeys);
38
+ visitedKeys.delete(varName);
39
+ resolvedCache.set(varName, resolved);
40
+ return resolved;
41
+ }
42
+
43
+ // Non-string value, return as-is
44
+ return String(value);
45
+ });
46
+ }
47
+
48
+ /**
49
+ * Extracts attribute names from withAttrs configuration
50
+ * Resolves template variables and excludes underscore-prefixed config keys
51
+ * @param withAttrs - The attribute patterns configuration
52
+ * @returns Array of resolved attribute names
53
+ */
54
+ function extractAttributeNames(withAttrs: AttrPatterns<any>): string[] {
55
+ const names: string[] = [];
56
+ const resolvedCache = new Map<string, string>();
57
+
58
+ // Add base if present
59
+ if ('base' in withAttrs && typeof withAttrs.base === 'string') {
60
+ names.push(withAttrs.base);
61
+ }
62
+
63
+ // Add other attributes (skip underscore-prefixed config keys)
64
+ for (const key in withAttrs) {
65
+ if (key === 'base' || key.startsWith('_')) {
66
+ continue;
67
+ }
68
+
69
+ const value = withAttrs[key];
70
+ if (typeof value === 'string') {
71
+ // Resolve template variables
72
+ const resolved = resolveTemplate(value, withAttrs, resolvedCache);
73
+ names.push(resolved);
74
+ }
75
+ }
76
+
77
+ return names;
78
+ }
79
+
80
+ /**
81
+ * Builds a CSS query selector that matches elements with attributes from withAttrs
82
+ * Creates a cross-product of selectors and attribute names (both prefixed and unprefixed)
83
+ *
84
+ * @param config - Enhancement configuration with withAttrs
85
+ * @param selectors - Comma-separated CSS selectors to match (e.g., 'template, script')
86
+ * @returns CSS query string with cross-product of selectors and attributes
87
+ *
88
+ * @example
89
+ * const config = {
90
+ * spawn: MyClass,
91
+ * withAttrs: {
92
+ * base: 'my-attr',
93
+ * theme: '${base}-theme'
94
+ * }
95
+ * };
96
+ *
97
+ * buildCSSQuery(config, 'div, span');
98
+ * // Returns: 'div[my-attr], span[my-attr], div[enh-my-attr], span[enh-my-attr],
99
+ * // div[my-attr-theme], span[my-attr-theme], div[enh-my-attr-theme], span[enh-my-attr-theme]'
100
+ */
101
+ export function buildCSSQuery(
102
+ config: EnhancementConfig,
103
+ selectors: string
104
+ ): string {
105
+ // Validate inputs
106
+ if (!config.withAttrs || !selectors) {
107
+ return '';
108
+ }
109
+
110
+ // Parse and normalize selectors
111
+ const selectorList = selectors
112
+ .split(',')
113
+ .map(s => s.trim())
114
+ .filter(s => s.length > 0);
115
+
116
+ if (selectorList.length === 0) {
117
+ return '';
118
+ }
119
+
120
+ // Extract and resolve attribute names
121
+ const attrNames = extractAttributeNames(config.withAttrs);
122
+
123
+ if (attrNames.length === 0) {
124
+ return '';
125
+ }
126
+
127
+ // Build cross-product of selectors × attributes × prefixes
128
+ const queries: string[] = [];
129
+
130
+ for (const selector of selectorList) {
131
+ for (const attrName of attrNames) {
132
+ // Unprefixed version
133
+ queries.push(`${selector}[${attrName}]`);
134
+ // enh- prefixed version
135
+ queries.push(`${selector}[enh-${attrName}]`);
136
+ }
137
+ }
138
+
139
+ // Deduplicate and join
140
+ const uniqueQueries = [...new Set(queries)];
141
+ return uniqueQueries.join(', ');
142
+ }
package/index.js CHANGED
@@ -4,4 +4,5 @@ export { BaseRegistry } from './assignGingerly.js';
4
4
  export { waitForEvent } from './waitForEvent.js';
5
5
  export { ParserRegistry, globalParserRegistry } from './parserRegistry.js';
6
6
  export { parseWithAttrs } from './parseWithAttrs.js';
7
+ export { buildCSSQuery } from './buildCSSQuery.js';
7
8
  import './object-extension.js';
package/index.ts CHANGED
@@ -4,4 +4,5 @@ export {BaseRegistry} from './assignGingerly.js';
4
4
  export {waitForEvent} from './waitForEvent.js';
5
5
  export {ParserRegistry, globalParserRegistry} from './parserRegistry.js';
6
6
  export {parseWithAttrs} from './parseWithAttrs.js';
7
+ export {buildCSSQuery} from './buildCSSQuery.js';
7
8
  import './object-extension.js';
@@ -82,21 +82,21 @@ class ElementEnhancementContainer {
82
82
  return undefined;
83
83
  }
84
84
  }
85
+ // Parse attributes if withAttrs is defined (regardless of enhKey)
86
+ let attrInitVals = undefined;
87
+ if (registryItem.withAttrs && element) {
88
+ try {
89
+ attrInitVals = parseWithAttrs(element, registryItem.withAttrs, registryItem.allowUnprefixed || false);
90
+ }
91
+ catch (e) {
92
+ console.error('Error parsing attributes:', e);
93
+ throw e;
94
+ }
95
+ }
85
96
  // Check if there's an enhKey
86
97
  if (registryItem.enhKey) {
87
98
  const ctx = { config: registryItem, mountCtx };
88
99
  const self = this;
89
- // Parse attributes if withAttrs is defined
90
- let attrInitVals = undefined;
91
- if (registryItem.withAttrs && element) {
92
- try {
93
- attrInitVals = parseWithAttrs(element, registryItem.withAttrs, registryItem.allowUnprefixed || false);
94
- }
95
- catch (e) {
96
- console.error('Error parsing attributes:', e);
97
- throw e;
98
- }
99
- }
100
100
  // Get existing initVals from enhKey
101
101
  const existingInitVals = self[registryItem.enhKey] &&
102
102
  !(self[registryItem.enhKey] instanceof SpawnClass)
@@ -111,9 +111,9 @@ class ElementEnhancementContainer {
111
111
  self[registryItem.enhKey] = instance;
112
112
  }
113
113
  else {
114
- // No enhKey, just spawn with element
114
+ // No enhKey, still pass attrInitVals
115
115
  const ctx = { config: registryItem, mountCtx };
116
- instance = new SpawnClass(element, ctx);
116
+ instance = new SpawnClass(element, ctx, attrInitVals);
117
117
  }
118
118
  // Store in global instance map
119
119
  instances.set(registryItem, instance);
@@ -156,26 +156,26 @@ class ElementEnhancementContainer {
156
156
  }
157
157
  }
158
158
 
159
+ // Parse attributes if withAttrs is defined (regardless of enhKey)
160
+ let attrInitVals: any = undefined;
161
+ if (registryItem.withAttrs && element) {
162
+ try {
163
+ attrInitVals = parseWithAttrs(
164
+ element,
165
+ registryItem.withAttrs,
166
+ registryItem.allowUnprefixed || false
167
+ );
168
+ } catch (e) {
169
+ console.error('Error parsing attributes:', e);
170
+ throw e;
171
+ }
172
+ }
173
+
159
174
  // Check if there's an enhKey
160
175
  if (registryItem.enhKey) {
161
176
  const ctx = { config: registryItem, mountCtx };
162
177
  const self = this as any;
163
178
 
164
- // Parse attributes if withAttrs is defined
165
- let attrInitVals: any = undefined;
166
- if (registryItem.withAttrs && element) {
167
- try {
168
- attrInitVals = parseWithAttrs(
169
- element,
170
- registryItem.withAttrs,
171
- registryItem.allowUnprefixed || false
172
- );
173
- } catch (e) {
174
- console.error('Error parsing attributes:', e);
175
- throw e;
176
- }
177
- }
178
-
179
179
  // Get existing initVals from enhKey
180
180
  const existingInitVals = self[registryItem.enhKey] &&
181
181
  !(self[registryItem.enhKey] instanceof SpawnClass)
@@ -192,9 +192,9 @@ class ElementEnhancementContainer {
192
192
  // Store on enh container
193
193
  self[registryItem.enhKey] = instance;
194
194
  } else {
195
- // No enhKey, just spawn with element
195
+ // No enhKey, still pass attrInitVals
196
196
  const ctx = { config: registryItem, mountCtx };
197
- instance = new SpawnClass(element, ctx);
197
+ instance = new SpawnClass(element, ctx, attrInitVals);
198
198
  }
199
199
 
200
200
  // Store in global instance map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "assign-gingerly",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "This package provides a utility function for carefully merging one object into another.",
5
5
  "homepage": "https://github.com/bahrus/assign-gingerly#readme",
6
6
  "bugs": {
@@ -43,6 +43,10 @@
43
43
  "./parseWithAttrs.js": {
44
44
  "default": "./parseWithAttrs.js",
45
45
  "types": "./parseWithAttrs.ts"
46
+ },
47
+ "./buildCSSQuery.js": {
48
+ "default": "./buildCSSQuery.js",
49
+ "types": "./buildCSSQuery.ts"
46
50
  }
47
51
  },
48
52
  "main": "index.js",
@@ -54,8 +58,8 @@
54
58
  },
55
59
  "devDependencies": {
56
60
  "@playwright/test": "^1.58.2",
57
- "spa-ssi": "0.0.26",
58
- "@types/node": "^25.2.3",
61
+ "spa-ssi": "0.0.27",
62
+ "@types/node": "^25.3.0",
59
63
  "typescript": "^5.9.3"
60
64
  }
61
65
  }