id-dom 0.0.3 → 0.0.4

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.
@@ -3,21 +3,20 @@
3
3
  [![npm version](https://img.shields.io/npm/v/id-dom.svg)](https://www.npmjs.com/package/id-dom)
4
4
  [![npm downloads](https://img.shields.io/npm/dm/id-dom.svg)](https://www.npmjs.com/package/id-dom)
5
5
  [![GitHub stars](https://img.shields.io/github/stars/iWhatty/id-dom.svg?style=social)](https://github.com/iWhatty/id-dom)
6
- [![License](https://img.shields.io/github/license/iWhatty/id-dom.svg)](https://github.com/iWhatty/id-dom/blob/main/LICENSE.md)
6
+ [![License](https://img.shields.io/github/license/iWhatty/id-dom.svg)](https://github.com/iWhatty/id-dom/blob/main/LICENSE)
7
7
 
8
-
9
- **Deterministic DOM element getters by ID (typed, tiny, modern).**
8
+ **Deterministic DOM element getters by ID — typed, tiny, modern.**
10
9
 
11
10
  `id-dom` is a small utility for grabbing DOM references safely **by `id`**, with predictable behavior:
12
11
 
13
- * **Typed getters** (`button('saveBtn')`, `input('name')`, etc.)
14
- * **Strict or optional** mode (`throw` vs `null`)
15
- * **Short optional alias** (`.opt`)
16
- * **Scopable** to a root (`document`, `ShadowRoot`, or an `Element`)
17
- * **Centralized error handling** (`onError`, optional `warn`)
18
- * **Zero deps**
12
+ * **Typed getters** like `button('saveBtn')`, `input('nameInput')`, `svg('icon')`
13
+ * **Strict or optional** mode (`throw` vs `null`)
14
+ * **Short optional alias** via `.opt`
15
+ * **Scoped lookups** for `document`, `ShadowRoot`, `DocumentFragment`, or an `Element`
16
+ * **Centralized error handling** with `onError` and optional `warn`
17
+ * **Zero deps**
19
18
 
20
- This is deliberately **not** a selector framework it’s a tiny ID-first primitive for clean, safe DOM wiring.
19
+ This is deliberately **not** a selector framework. It is a tiny, ID-first primitive for safe DOM wiring.
21
20
 
22
21
  ---
23
22
 
@@ -34,17 +33,16 @@ npm install id-dom
34
33
  ```js
35
34
  import dom from 'id-dom'
36
35
 
37
- const saveBtn = dom.button('saveBtn') // throws if missing or wrong type
36
+ const saveBtn = dom.button('saveBtn')
38
37
  saveBtn.addEventListener('click', save)
39
38
  ```
40
39
 
41
- Optional access (never throws for missing/wrong-type):
40
+ Optional access never throws for missing or wrong-type elements:
42
41
 
43
42
  ```js
44
43
  const debug = dom.div.optional('debugPanel')
45
44
  debug?.append('hello')
46
45
 
47
- // short alias
48
46
  const maybeCanvas = dom.canvas.opt('game')
49
47
  ```
50
48
 
@@ -58,9 +56,9 @@ Using `getElementById` is:
58
56
  * unambiguous
59
57
  * easy to reason about
60
58
 
61
- …and with typed getters, you immediately know whether you have a `HTMLButtonElement`, `HTMLInputElement`, etc.
59
+ And with typed getters, you immediately know whether you got a `HTMLButtonElement`, `HTMLInputElement`, `SVGSVGElement`, and so on.
62
60
 
63
- Scoped lookups safely escape IDs when using `querySelector`, ensuring stability even for edge-case IDs (e.g. starting with digits).
61
+ When scoped roots do not support `getElementById`, `id-dom` falls back to `querySelector(#id)` and safely escapes edge-case IDs.
64
62
 
65
63
  ---
66
64
 
@@ -71,7 +69,8 @@ Scoped lookups safely escape IDs when using `querySelector`, ensuring stability
71
69
  The default export is a scoped instance using `document` (when available) with **strict** behavior:
72
70
 
73
71
  * missing element → **throws**
74
- * wrong type/tag → **throws**
72
+ * wrong type or wrong tag → **throws**
73
+ * invalid input → **throws**
75
74
 
76
75
  ```js
77
76
  import dom from 'id-dom'
@@ -84,17 +83,16 @@ const submit = dom.button('submitBtn')
84
83
 
85
84
  ### `createDom(root, config?)`
86
85
 
87
- Create a scoped instance that searches within a root:
86
+ Create a scoped instance that searches within a specific root:
88
87
 
89
- * `document` (uses `getElementById`)
90
- * `ShadowRoot` / `Element` (uses `querySelector(#id)` fallback)
88
+ * `document` uses `getElementById`
89
+ * `ShadowRoot`, `DocumentFragment`, or `Element` uses `querySelector(#id)` fallback
91
90
 
92
91
  ```js
93
92
  import { createDom } from 'id-dom'
94
93
 
95
94
  const d = createDom(document, { mode: 'null', warn: true })
96
-
97
- const sidebar = d.div('sidebar') // null if missing
95
+ const sidebar = d.div('sidebar')
98
96
  ```
99
97
 
100
98
  #### Config
@@ -103,8 +101,8 @@ const sidebar = d.div('sidebar') // null if missing
103
101
  type DomMode = 'throw' | 'null'
104
102
 
105
103
  {
106
- mode?: DomMode // default: 'throw'
107
- warn?: boolean // default: false
104
+ mode?: DomMode
105
+ warn?: boolean
108
106
  onError?: (err: Error, ctx: any) => void
109
107
  }
110
108
  ```
@@ -121,23 +119,32 @@ import { byId } from 'id-dom'
121
119
  const btn = byId('saveBtn', HTMLButtonElement)
122
120
  ```
123
121
 
124
- Optional variants (never throw for missing/wrong-type):
122
+ Optional variants:
125
123
 
126
124
  ```js
127
125
  const maybeBtn = byId.optional('saveBtn', HTMLButtonElement)
128
126
  const maybeBtn2 = byId.opt('saveBtn', HTMLButtonElement)
129
127
  ```
130
128
 
129
+ #### Behavior
130
+
131
+ * valid match → returns the element
132
+ * missing element → throws or returns `null`
133
+ * wrong type → throws or returns `null`
134
+ * invalid `id` → throws or returns `null`
135
+ * invalid `Type` → throws or returns `null`
136
+
131
137
  ---
132
138
 
133
139
  ### `tag(id, tagName, config?)`
134
140
 
135
- Tag-based validation for semantic elements:
141
+ Tag-based validation when constructor checks are not the right fit:
136
142
 
137
143
  ```js
138
144
  import { tag } from 'id-dom'
139
145
 
140
146
  const main = tag('appMain', 'main')
147
+ const icon = tag('icon', 'svg', { root: container })
141
148
  ```
142
149
 
143
150
  Optional variants:
@@ -147,13 +154,21 @@ const maybeMain = tag.optional('appMain', 'main')
147
154
  const maybeMain2 = tag.opt('appMain', 'main')
148
155
  ```
149
156
 
157
+ #### Behavior
158
+
159
+ * valid tag match → returns the element
160
+ * missing element → throws or returns `null`
161
+ * wrong tag → throws or returns `null`
162
+ * invalid `id` → throws or returns `null`
163
+ * invalid `tagName` → throws or returns `null`
164
+
150
165
  ---
151
166
 
152
167
  ## Built-in Getters
153
168
 
154
169
  ### Typed getters
155
170
 
156
- From `dom` (and any `createDom()` instance):
171
+ Available on `dom` and on any `createDom()` instance:
157
172
 
158
173
  * `el(id)` → `HTMLElement`
159
174
  * `input(id)` → `HTMLInputElement`
@@ -167,27 +182,28 @@ From `dom` (and any `createDom()` instance):
167
182
  * `canvas(id)` → `HTMLCanvasElement`
168
183
  * `template(id)` → `HTMLTemplateElement`
169
184
  * `svg(id)` → `SVGSVGElement`
185
+ * `body(id)` → `HTMLBodyElement`
170
186
 
171
- Each also has:
187
+ Each getter also has:
172
188
 
173
189
  ```js
174
190
  dom.canvas.optional('game')
175
191
  dom.canvas.opt('game')
176
192
  ```
177
193
 
178
- ### Common semantic tags
194
+ ### Common tag helpers
179
195
 
180
- * `main(id)` → `<main>`
181
- * `section(id)` → `<section>`
182
- * `small(id)` → `<small>`
196
+ * `main(id)` → validates `<main>`
197
+ * `section(id)` → validates `<section>`
198
+ * `small(id)` → validates `<small>`
183
199
 
184
- (Also with `.optional` and `.opt`.)
200
+ Each also supports `.optional` and `.opt`.
185
201
 
186
202
  ---
187
203
 
188
204
  ## Error Handling
189
205
 
190
- ### Throwing (default)
206
+ ### Throwing mode
191
207
 
192
208
  ```js
193
209
  import dom from 'id-dom'
@@ -201,7 +217,6 @@ dom.button('missing') // throws
201
217
  import { createDom } from 'id-dom'
202
218
 
203
219
  const d = createDom(document, { mode: 'null' })
204
-
205
220
  d.button('missing') // null
206
221
  ```
207
222
 
@@ -216,7 +231,7 @@ const d = createDom(document, {
216
231
  })
217
232
  ```
218
233
 
219
- Enable console warnings:
234
+ Enable console warnings too:
220
235
 
221
236
  ```js
222
237
  createDom(document, { mode: 'null', warn: true })
@@ -224,7 +239,9 @@ createDom(document, { mode: 'null', warn: true })
224
239
 
225
240
  ---
226
241
 
227
- ## Shadow DOM / Scoped Roots
242
+ ## Scoped Roots
243
+
244
+ ### Shadow DOM
228
245
 
229
246
  ```js
230
247
  import { createDom } from 'id-dom'
@@ -237,6 +254,30 @@ const d = createDom(shadow)
237
254
  const btn = d.button('shadowBtn')
238
255
  ```
239
256
 
257
+ ### Element root
258
+
259
+ ```js
260
+ const container = document.querySelector('#settings-panel')
261
+ const d = createDom(container)
262
+ const input = d.input('emailInput')
263
+ ```
264
+
265
+ ### SVG in scoped roots
266
+
267
+ ```js
268
+ const container = document.querySelector('#icons')
269
+ const d = createDom(container)
270
+ const icon = d.svg('logoMark')
271
+ ```
272
+
273
+ ---
274
+
275
+ ## Notes
276
+
277
+ * `el(id)` is specifically for `HTMLElement`, not every possible DOM `Element`.
278
+ * `body(id)` looks up a `<body>` **by ID**. This library stays ID-first on purpose.
279
+ * `tag()` can validate non-HTML tags too, such as `svg`, when used against supported scoped roots.
280
+
240
281
  ---
241
282
 
242
283
  ## Browser Support
@@ -246,7 +287,7 @@ Modern browsers supporting:
246
287
  * `getElementById`
247
288
  * `querySelector`
248
289
 
249
- `CSS.escape` is used when available. A safe internal fallback is provided for environments (e.g. some jsdom builds) where it is missing.
290
+ `CSS.escape` is used when available. A safe internal fallback is included for environments such as some jsdom builds where it may be missing.
250
291
 
251
292
  ---
252
293
 
package/dist/index.cjs CHANGED
@@ -27,10 +27,22 @@ const DEFAULT_ROOT = typeof document !== "undefined" && document ? document : (
27
27
  /** @type {any} */
28
28
  null
29
29
  );
30
+ const REASON = (
31
+ /** @type {const} */
32
+ {
33
+ INVALID_ID: "invalid-id",
34
+ INVALID_TYPE: "invalid-type",
35
+ INVALID_TAG: "invalid-tag",
36
+ MISSING: "missing",
37
+ WRONG_TYPE: "wrong-type",
38
+ WRONG_TAG: "wrong-tag"
39
+ }
40
+ );
41
+ const SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
42
+ const NEEDS_START_ESCAPE_RE = /^(?:\d|-\d)/;
30
43
  function normalizeConfig(cfg) {
31
44
  return {
32
45
  mode: cfg?.mode ?? "throw",
33
- // default strict
34
46
  warn: cfg?.warn ?? false,
35
47
  onError: typeof cfg?.onError === "function" ? cfg.onError : null,
36
48
  root: cfg?.root ?? DEFAULT_ROOT
@@ -42,8 +54,6 @@ function hasGetElementById(v) {
42
54
  function hasQuerySelector(v) {
43
55
  return !!v && typeof v === "object" && typeof v.querySelector === "function";
44
56
  }
45
- const SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
46
- const NEEDS_START_ESCAPE_RE = /^(?:\d|-\d)/;
47
57
  function cssEscape(id) {
48
58
  const s = String(id);
49
59
  if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
@@ -59,10 +69,13 @@ function cssEscape(id) {
59
69
  cp >= 97 && cp <= 122 || // a-z
60
70
  cp === 95 || // _
61
71
  cp === 45;
62
- const needsStartEscape = i === 0 && (cp >= 48 && cp <= 57 || cp === 45 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57);
72
+ const next = s.codePointAt(i + 1);
73
+ const startsWithDigit = cp >= 48 && cp <= 57;
74
+ const startsWithDashDigit = cp === 45 && s.length > 1 && next >= 48 && next <= 57;
75
+ const needsStartEscape = i === 0 && (startsWithDigit || startsWithDashDigit);
63
76
  if (!needsStartEscape && (isAsciiSafe || cp >= 160)) {
64
77
  out += ch;
65
- } else if (cp === 45 && i === 0 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57) {
78
+ } else if (i === 0 && startsWithDashDigit) {
66
79
  out += "\\-";
67
80
  } else {
68
81
  out += `\\${cp.toString(16).toUpperCase()} `;
@@ -71,16 +84,32 @@ function cssEscape(id) {
71
84
  }
72
85
  return out;
73
86
  }
87
+ function isElementNode(v) {
88
+ if (!v || typeof v !== "object") return false;
89
+ if (typeof Element !== "undefined") return v instanceof Element;
90
+ return (
91
+ /** @type {any} */
92
+ v.nodeType === 1
93
+ );
94
+ }
74
95
  function getById(root, id) {
75
96
  if (!root) return null;
76
97
  if (hasGetElementById(root)) return root.getElementById(id);
77
98
  if (hasQuerySelector(root)) {
78
- const sel = `#${cssEscape(id)}`;
79
- const el = root.querySelector(sel);
80
- return el instanceof HTMLElement ? el : null;
99
+ const el = root.querySelector(`#${cssEscape(id)}`);
100
+ return isElementNode(el) ? el : null;
81
101
  }
82
102
  return null;
83
103
  }
104
+ function isValidId(v) {
105
+ return typeof v === "string" && v.length > 0;
106
+ }
107
+ function isValidTagName(v) {
108
+ return typeof v === "string" && v.trim().length > 0;
109
+ }
110
+ function isConstructor(v) {
111
+ return typeof v === "function";
112
+ }
84
113
  function fmtId(id) {
85
114
  return id.startsWith("#") ? id : `#${id}`;
86
115
  }
@@ -95,54 +124,114 @@ function handleLookupError(err, ctx, cfg) {
95
124
  cfg.onError?.(err, ctx);
96
125
  } catch {
97
126
  }
98
- if (cfg.warn) console.warn(err);
127
+ if (cfg.warn) console.warn(err, ctx);
99
128
  if (cfg.mode === "throw") throw err;
100
129
  return null;
101
130
  }
102
- function byId(id, Type, config) {
131
+ function createCtx(id, root, reason, extra) {
132
+ return {
133
+ id,
134
+ root,
135
+ reason,
136
+ ...extra || {}
137
+ };
138
+ }
139
+ function resolveLookup(config, spec) {
103
140
  const cfg = normalizeConfig(config);
104
- const el = getById(cfg.root, id);
141
+ const inputFailure = spec.validateInput(cfg);
142
+ if (inputFailure) {
143
+ return handleLookupError(inputFailure.err, inputFailure.ctx, cfg);
144
+ }
145
+ const el = getById(cfg.root, spec.id);
105
146
  if (!el) {
106
- return handleLookupError(
107
- missingElError(id, Type.name),
108
- { id, Type, root: cfg.root, reason: "missing" },
109
- cfg
110
- );
111
- }
112
- if (!(el instanceof Type)) {
113
- const got = el?.constructor?.name || typeof el;
114
- return handleLookupError(
115
- wrongTypeError(id, Type.name, got),
116
- { id, Type, root: cfg.root, reason: "wrong-type", got },
117
- cfg
118
- );
119
- }
120
- return el;
147
+ const failure = spec.onMissing(cfg);
148
+ return handleLookupError(failure.err, failure.ctx, cfg);
149
+ }
150
+ if (!spec.matches(el, cfg)) {
151
+ const failure = spec.onMismatch(el, cfg);
152
+ return handleLookupError(failure.err, failure.ctx, cfg);
153
+ }
154
+ return (
155
+ /** @type {T} */
156
+ el
157
+ );
158
+ }
159
+ function byId(id, Type, config) {
160
+ return resolveLookup(config, {
161
+ id,
162
+ validateInput(cfg) {
163
+ if (!isValidId(id)) {
164
+ return {
165
+ err: new Error("id-dom: invalid id (expected non-empty string)"),
166
+ ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { Type })
167
+ };
168
+ }
169
+ if (!isConstructor(Type)) {
170
+ return {
171
+ err: new Error(`id-dom: invalid Type for ${fmtId(id)}`),
172
+ ctx: createCtx(id, cfg.root, REASON.INVALID_TYPE, { Type })
173
+ };
174
+ }
175
+ return null;
176
+ },
177
+ onMissing(cfg) {
178
+ return {
179
+ err: missingElError(id, Type.name),
180
+ ctx: createCtx(id, cfg.root, REASON.MISSING, { Type })
181
+ };
182
+ },
183
+ matches(el) {
184
+ return el instanceof Type;
185
+ },
186
+ onMismatch(el, cfg) {
187
+ const got = el?.constructor?.name || typeof el;
188
+ return {
189
+ err: wrongTypeError(id, Type.name, got),
190
+ ctx: createCtx(id, cfg.root, REASON.WRONG_TYPE, { Type, got })
191
+ };
192
+ }
193
+ });
121
194
  }
122
195
  byId.optional = function byIdOptional(id, Type, config) {
123
196
  return byId(id, Type, { ...config, mode: "null" });
124
197
  };
125
198
  byId.opt = byId.optional;
126
199
  function tag(id, tagName, config) {
127
- const cfg = normalizeConfig(config);
128
- const el = getById(cfg.root, id);
129
- if (!el) {
130
- return handleLookupError(
131
- missingElError(id, `<${tagName}>`),
132
- { id, tagName, root: cfg.root, reason: "missing" },
133
- cfg
134
- );
135
- }
136
- const expected = String(tagName).toUpperCase();
137
- const got = String(el.tagName || "").toUpperCase();
138
- if (got !== expected) {
139
- return handleLookupError(
140
- wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),
141
- { id, tagName, root: cfg.root, reason: "wrong-tag", got },
142
- cfg
143
- );
144
- }
145
- return el;
200
+ return resolveLookup(config, {
201
+ id,
202
+ validateInput(cfg) {
203
+ if (!isValidId(id)) {
204
+ return {
205
+ err: new Error("id-dom: invalid id (expected non-empty string)"),
206
+ ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { tagName })
207
+ };
208
+ }
209
+ if (!isValidTagName(tagName)) {
210
+ return {
211
+ err: new Error(`id-dom: invalid tagName for ${fmtId(id)}`),
212
+ ctx: createCtx(id, cfg.root, REASON.INVALID_TAG, { tagName })
213
+ };
214
+ }
215
+ return null;
216
+ },
217
+ onMissing(cfg) {
218
+ return {
219
+ err: missingElError(id, `<${tagName}>`),
220
+ ctx: createCtx(id, cfg.root, REASON.MISSING, { tagName })
221
+ };
222
+ },
223
+ matches(el) {
224
+ return String(el.tagName || "").toUpperCase() === String(tagName).toUpperCase();
225
+ },
226
+ onMismatch(el, cfg) {
227
+ const expected = String(tagName).toUpperCase();
228
+ const got = String(el.tagName || "").toUpperCase();
229
+ return {
230
+ err: wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),
231
+ ctx: createCtx(id, cfg.root, REASON.WRONG_TAG, { tagName, got })
232
+ };
233
+ }
234
+ });
146
235
  }
147
236
  tag.optional = function tagOptional(id, tagName, config) {
148
237
  return tag(id, tagName, { ...config, mode: "null" });
@@ -162,7 +251,8 @@ const TYPE_HELPERS = (
162
251
  label: typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null,
163
252
  canvas: typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement : null,
164
253
  template: typeof HTMLTemplateElement !== "undefined" ? HTMLTemplateElement : null,
165
- svg: typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null
254
+ svg: typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null,
255
+ body: typeof HTMLBodyElement !== "undefined" ? HTMLBodyElement : null
166
256
  }
167
257
  );
168
258
  const TAG_HELPERS = (
@@ -173,42 +263,53 @@ const TAG_HELPERS = (
173
263
  small: "small"
174
264
  }
175
265
  );
266
+ function attachOptional(fn, optionalFn) {
267
+ if (typeof fn !== "function") {
268
+ throw new TypeError("id-dom: attachOptional expected fn to be a function");
269
+ }
270
+ if (typeof optionalFn !== "function") {
271
+ throw new TypeError("id-dom: attachOptional expected optionalFn to be a function");
272
+ }
273
+ fn.optional = optionalFn;
274
+ fn.opt = optionalFn;
275
+ return fn;
276
+ }
277
+ function makeTypedHelper(Type, base, baseNull) {
278
+ if (!Type) {
279
+ throw new TypeError("id-dom: makeTypedHelper received an invalid Type");
280
+ }
281
+ return attachOptional(
282
+ (id) => byId(id, Type, base),
283
+ (id) => byId(id, Type, baseNull)
284
+ );
285
+ }
286
+ function makeTagHelper(tagName, base, baseNull) {
287
+ if (!tagName) {
288
+ throw new TypeError("id-dom: makeTagHelper received an invalid tagName");
289
+ }
290
+ return attachOptional(
291
+ (id) => tag(id, tagName, base),
292
+ (id) => tag(id, tagName, baseNull)
293
+ );
294
+ }
176
295
  function createDom(root, config) {
177
296
  const base = normalizeConfig({ ...config, root });
178
297
  const baseNull = { ...base, mode: "null" };
179
- const api = {
180
- // generic
181
- byId: (id, Type) => byId(id, Type, base),
182
- tag: (id, name) => tag(id, name, base)
183
- };
298
+ const api = {};
299
+ api.byId = attachOptional(
300
+ (id, Type) => byId(id, Type, base),
301
+ (id, Type) => byId(id, Type, baseNull)
302
+ );
303
+ api.tag = attachOptional(
304
+ (id, name) => tag(id, name, base),
305
+ (id, name) => tag(id, name, baseNull)
306
+ );
184
307
  for (const [name, Type] of Object.entries(TYPE_HELPERS)) {
185
308
  if (!Type) continue;
186
- api[name] = (id) => byId(id, Type, base);
309
+ api[name] = makeTypedHelper(Type, base, baseNull);
187
310
  }
188
311
  for (const [name, tagName] of Object.entries(TAG_HELPERS)) {
189
- api[name] = (id) => tag(id, tagName, base);
190
- }
191
- api.byId.optional = (id, Type) => byId(id, Type, baseNull);
192
- api.byId.opt = api.byId.optional;
193
- api.tag.optional = (id, name) => tag(id, name, baseNull);
194
- api.tag.opt = api.tag.optional;
195
- for (const k of Object.keys(api)) {
196
- const fn = api[k];
197
- if (typeof fn !== "function") continue;
198
- if (fn.optional) continue;
199
- if (k in TAG_HELPERS) {
200
- const tagName = TAG_HELPERS[k];
201
- fn.optional = (id) => tag(id, tagName, baseNull);
202
- fn.opt = fn.optional;
203
- continue;
204
- }
205
- if (k in TYPE_HELPERS) {
206
- const Type = TYPE_HELPERS[k];
207
- if (Type) {
208
- fn.optional = (id) => byId(id, Type, baseNull);
209
- fn.opt = fn.optional;
210
- }
211
- }
312
+ api[name] = makeTagHelper(tagName, base, baseNull);
212
313
  }
213
314
  return api;
214
315
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/id-dom.js"],
4
- "sourcesContent": ["// id-dom.js\r\n// id-dom \u2014 deterministic DOM element getters by ID (typed, tiny, modern)\r\n//\r\n// Goals:\r\n// - Prefer getElementById (fast, unambiguous)\r\n// - Return the correct type or fail predictably\r\n// - Provide strict + optional variants\r\n// - Allow app/module-level defaults (throw vs null) without bundler magic\r\n// - Zero deps, framework-agnostic\r\n\r\nconst DEFAULT_ROOT =\r\n typeof document !== 'undefined' && document ? document : /** @type {any} */ (null)\r\n\r\n/**\r\n * @typedef {'throw' | 'null'} DomMode\r\n */\r\n\r\n/**\r\n * @typedef {{\r\n * mode?: DomMode\r\n * warn?: boolean\r\n * onError?: (error: Error, ctx: any) => void\r\n * root?: any\r\n * }} DomConfig\r\n */\r\n\r\n/**\r\n * @param {DomConfig | undefined} cfg\r\n */\r\nfunction normalizeConfig(cfg) {\r\n return {\r\n mode: cfg?.mode ?? 'throw', // default strict\r\n warn: cfg?.warn ?? false,\r\n onError: typeof cfg?.onError === 'function' ? cfg.onError : null,\r\n root: cfg?.root ?? DEFAULT_ROOT,\r\n }\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is { getElementById(id: string): HTMLElement | null }}\r\n */\r\nfunction hasGetElementById(v) {\r\n return !!v && typeof v === 'object' && typeof v.getElementById === 'function'\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is { querySelector(sel: string): Element | null }}\r\n */\r\nfunction hasQuerySelector(v) {\r\n return !!v && typeof v === 'object' && typeof v.querySelector === 'function'\r\n}\r\n\r\n\r\nconst SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/\r\nconst NEEDS_START_ESCAPE_RE = /^(?:\\d|-\\d)/\r\n\r\n/**\r\n * Minimal CSS.escape fallback for environments where CSS.escape is missing (e.g. some jsdom builds).\r\n * We only need to safely build `#${id}` selectors.\r\n *\r\n * @param {string} id\r\n */\r\nfunction cssEscape(id) {\r\n const s = String(id)\r\n\r\n // Prefer native when available\r\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\r\n return CSS.escape(s)\r\n }\r\n\r\n // Fast path: most app IDs are already safe\r\n if (!NEEDS_START_ESCAPE_RE.test(s) && SAFE_ID_RE.test(s)) return s\r\n\r\n // One-pass escape:\r\n // - Escape any char outside a conservative \"safe\" set\r\n // - Also escape the start if it begins with a digit OR \"-<digit>\"\r\n let out = ''\r\n for (let i = 0; i < s.length; ) {\r\n const cp = s.codePointAt(i)\r\n const ch = String.fromCodePoint(cp)\r\n\r\n const isAsciiSafe =\r\n (cp >= 48 && cp <= 57) || // 0-9\r\n (cp >= 65 && cp <= 90) || // A-Z\r\n (cp >= 97 && cp <= 122) || // a-z\r\n cp === 95 || // _\r\n cp === 45 // -\r\n\r\n const needsStartEscape =\r\n i === 0 && ((cp >= 48 && cp <= 57) || (cp === 45 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57))\r\n\r\n if (!needsStartEscape && (isAsciiSafe || cp >= 0x00A0)) {\r\n // allow non-ascii chars directly (common CSS ident behavior)\r\n out += ch\r\n } else if (cp === 45 && i === 0 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57) {\r\n // \"-<digit>\" start: escaping just the leading hyphen is a simple fix\r\n out += '\\\\-'\r\n } else {\r\n // hex escape + trailing space is safest\r\n out += `\\\\${cp.toString(16).toUpperCase()} `\r\n }\r\n\r\n i += ch.length\r\n }\r\n\r\n return out\r\n}\r\n\r\n\r\n/**\r\n * Resolve an element by id from a \"root\".\r\n * Supports:\r\n * - Document (getElementById)\r\n * - ShadowRoot / DocumentFragment / Element (querySelector fallback)\r\n *\r\n * @param {any} root\r\n * @param {string} id\r\n * @returns {HTMLElement | null}\r\n */\r\nfunction getById(root, id) {\r\n if (!root) return null\r\n\r\n if (hasGetElementById(root)) return root.getElementById(id)\r\n\r\n // ShadowRoot/DocumentFragment/Element don\u2019t have getElementById\r\n if (hasQuerySelector(root)) {\r\n const sel = `#${cssEscape(id)}`\r\n const el = root.querySelector(sel)\r\n return el instanceof HTMLElement ? el : null\r\n }\r\n\r\n return null\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @returns {string}\r\n */\r\nfunction fmtId(id) {\r\n return id.startsWith('#') ? id : `#${id}`\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {string} expected\r\n */\r\nfunction missingElError(id, expected) {\r\n return new Error(`id-dom: missing ${expected} element ${fmtId(id)}`)\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {string} expected\r\n * @param {string} got\r\n */\r\nfunction wrongTypeError(id, expected, got) {\r\n return new Error(`id-dom: expected ${expected} for ${fmtId(id)}, got ${got}`)\r\n}\r\n\r\n/**\r\n * Centralized error policy:\r\n * - always call onError if present\r\n * - optionally warn\r\n * - throw or return null depending on mode\r\n *\r\n * @template T\r\n * @param {Error} err\r\n * @param {any} ctx\r\n * @param {ReturnType<typeof normalizeConfig>} cfg\r\n * @returns {T | null}\r\n */\r\nfunction handleLookupError(err, ctx, cfg) {\r\n try {\r\n cfg.onError?.(err, ctx)\r\n } catch {\r\n // do not let reporting break app logic\r\n }\r\n\r\n if (cfg.warn) console.warn(err)\r\n\r\n if (cfg.mode === 'throw') throw err\r\n return null\r\n}\r\n\r\n/**\r\n * Typed lookup by ID.\r\n * Behavior is controlled by config:\r\n * - mode: 'throw' (default) or 'null'\r\n * - warn: boolean\r\n * - onError(err, ctx)\r\n *\r\n * @template {Element} T\r\n * @param {string} id\r\n * @param {{ new (...args: any[]): T }} Type\r\n * @param {DomConfig} [config]\r\n * @returns {T | null}\r\n */\r\nexport function byId(id, Type, config) {\r\n const cfg = normalizeConfig(config)\r\n const el = getById(cfg.root, id)\r\n\r\n if (!el) {\r\n return handleLookupError(\r\n missingElError(id, Type.name),\r\n { id, Type, root: cfg.root, reason: 'missing' },\r\n cfg\r\n )\r\n }\r\n\r\n if (!(el instanceof Type)) {\r\n const got = el?.constructor?.name || typeof el\r\n return handleLookupError(\r\n wrongTypeError(id, Type.name, got),\r\n { id, Type, root: cfg.root, reason: 'wrong-type', got },\r\n cfg\r\n )\r\n }\r\n\r\n return el\r\n}\r\n\r\n/**\r\n * Optional typed lookup: ALWAYS returns T | null (never throws for missing/wrong-type).\r\n *\r\n * @template {Element} T\r\n * @param {string} id\r\n * @param {{ new (...args: any[]): T }} Type\r\n * @param {DomConfig} [config]\r\n * @returns {T | null}\r\n */\r\nbyId.optional = function byIdOptional(id, Type, config) {\r\n return byId(id, Type, { ...config, mode: 'null' })\r\n}\r\n\r\n// Short alias (module-level; do not reassign inside factories)\r\nbyId.opt = byId.optional\r\n\r\n/**\r\n * Tag-name lookup (HTMLElement only).\r\n * Useful for semantic elements that don\u2019t have unique constructors.\r\n *\r\n * @param {string} id\r\n * @param {string} tagName\r\n * @param {DomConfig} [config]\r\n * @returns {HTMLElement | null}\r\n */\r\nexport function tag(id, tagName, config) {\r\n const cfg = normalizeConfig(config)\r\n const el = getById(cfg.root, id)\r\n\r\n if (!el) {\r\n return handleLookupError(\r\n missingElError(id, `<${tagName}>`),\r\n { id, tagName, root: cfg.root, reason: 'missing' },\r\n cfg\r\n )\r\n }\r\n\r\n const expected = String(tagName).toUpperCase()\r\n const got = String(el.tagName || '').toUpperCase()\r\n\r\n if (got !== expected) {\r\n return handleLookupError(\r\n wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),\r\n { id, tagName, root: cfg.root, reason: 'wrong-tag', got },\r\n cfg\r\n )\r\n }\r\n\r\n return el\r\n}\r\n\r\n/**\r\n * Optional tag lookup: ALWAYS returns HTMLElement | null (never throws for missing/wrong-tag).\r\n *\r\n * @param {string} id\r\n * @param {string} tagName\r\n * @param {DomConfig} [config]\r\n * @returns {HTMLElement | null}\r\n */\r\ntag.optional = function tagOptional(id, tagName, config) {\r\n return tag(id, tagName, { ...config, mode: 'null' })\r\n}\r\n\r\n// Short alias (module-level; do not reassign inside factories)\r\ntag.opt = tag.optional\r\n\r\n// --- internal maps for factory helpers ---\r\nconst TYPE_HELPERS = /** @type {Record<string, any>} */ ({\r\n el: typeof HTMLElement !== 'undefined' ? HTMLElement : null,\r\n input: typeof HTMLInputElement !== 'undefined' ? HTMLInputElement : null,\r\n button: typeof HTMLButtonElement !== 'undefined' ? HTMLButtonElement : null,\r\n textarea: typeof HTMLTextAreaElement !== 'undefined' ? HTMLTextAreaElement : null,\r\n select: typeof HTMLSelectElement !== 'undefined' ? HTMLSelectElement : null,\r\n form: typeof HTMLFormElement !== 'undefined' ? HTMLFormElement : null,\r\n div: typeof HTMLDivElement !== 'undefined' ? HTMLDivElement : null,\r\n span: typeof HTMLSpanElement !== 'undefined' ? HTMLSpanElement : null,\r\n label: typeof HTMLLabelElement !== 'undefined' ? HTMLLabelElement : null,\r\n canvas: typeof HTMLCanvasElement !== 'undefined' ? HTMLCanvasElement : null,\r\n template: typeof HTMLTemplateElement !== 'undefined' ? HTMLTemplateElement : null,\r\n svg: typeof SVGSVGElement !== 'undefined' ? SVGSVGElement : null,\r\n})\r\n\r\nconst TAG_HELPERS = /** @type {Record<string, string>} */ ({\r\n main: 'main',\r\n section: 'section',\r\n small: 'small',\r\n})\r\n\r\n/**\r\n * Factory: scope getters to a specific root + default policy.\r\n *\r\n * @param {any} root\r\n * @param {Omit<DomConfig, 'root'>} [config]\r\n */\r\nexport function createDom(root, config) {\r\n const base = normalizeConfig({ ...config, root })\r\n const baseNull = { ...base, mode: 'null' }\r\n\r\n /** @type {any} */\r\n const api = {\r\n // generic\r\n byId: (id, Type) => byId(id, Type, base),\r\n tag: (id, name) => tag(id, name, base),\r\n }\r\n\r\n // typed helpers\r\n for (const [name, Type] of Object.entries(TYPE_HELPERS)) {\r\n if (!Type) continue\r\n api[name] = (id) => byId(id, Type, base)\r\n }\r\n\r\n // semantic tag helpers\r\n for (const [name, tagName] of Object.entries(TAG_HELPERS)) {\r\n api[name] = (id) => tag(id, tagName, base)\r\n }\r\n\r\n // --- Optional variants (policy-based; never swallow unrelated exceptions) ---\r\n api.byId.optional = (id, Type) => byId(id, Type, baseNull)\r\n api.byId.opt = api.byId.optional\r\n\r\n api.tag.optional = (id, name) => tag(id, name, baseNull)\r\n api.tag.opt = api.tag.optional\r\n\r\n // Add `.optional` + `.opt` to every helper using the same \"null\" policy\r\n for (const k of Object.keys(api)) {\r\n const fn = api[k]\r\n if (typeof fn !== 'function') continue\r\n if (fn.optional) continue\r\n\r\n if (k in TAG_HELPERS) {\r\n const tagName = TAG_HELPERS[k]\r\n fn.optional = (id) => tag(id, tagName, baseNull)\r\n fn.opt = fn.optional\r\n continue\r\n }\r\n\r\n if (k in TYPE_HELPERS) {\r\n const Type = TYPE_HELPERS[k]\r\n if (Type) {\r\n fn.optional = (id) => byId(id, Type, baseNull)\r\n fn.opt = fn.optional\r\n }\r\n }\r\n }\r\n\r\n return api\r\n}\r\n\r\n// Default export: root = document (if available), strict by default\r\nconst dom = createDom(DEFAULT_ROOT, { mode: 'throw' })\r\nexport default dom\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// id-dom.js\r\n// id-dom \u2014 deterministic DOM element getters by ID (typed, tiny, modern)\r\n//\r\n// Goals:\r\n// - Prefer getElementById (fast, unambiguous)\r\n// - Return the correct type or fail predictably\r\n// - Provide strict + optional variants\r\n// - Allow app/module-level defaults (throw vs null) without bundler magic\r\n// - Zero deps, framework-agnostic\r\n\r\nconst DEFAULT_ROOT =\r\n typeof document !== 'undefined' && document ? document : /** @type {any} */ (null)\r\n\r\nconst REASON = /** @type {const} */ ({\r\n INVALID_ID: 'invalid-id',\r\n INVALID_TYPE: 'invalid-type',\r\n INVALID_TAG: 'invalid-tag',\r\n MISSING: 'missing',\r\n WRONG_TYPE: 'wrong-type',\r\n WRONG_TAG: 'wrong-tag',\r\n})\r\n\r\nconst SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/\r\nconst NEEDS_START_ESCAPE_RE = /^(?:\\d|-\\d)/\r\n\r\n/**\r\n * @typedef {'throw' | 'null'} DomMode\r\n */\r\n\r\n/**\r\n * @typedef {{\r\n * mode?: DomMode\r\n * warn?: boolean\r\n * onError?: (error: Error, ctx: any) => void\r\n * root?: any\r\n * }} DomConfig\r\n */\r\n\r\n// -----------------------------------------------------------------------------\r\n// Config\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @param {DomConfig | undefined} cfg\r\n */\r\nfunction normalizeConfig(cfg) {\r\n return {\r\n mode: cfg?.mode ?? 'throw',\r\n warn: cfg?.warn ?? false,\r\n onError: typeof cfg?.onError === 'function' ? cfg.onError : null,\r\n root: cfg?.root ?? DEFAULT_ROOT,\r\n }\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Root / DOM helpers\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is { getElementById(id: string): Element | null }}\r\n */\r\nfunction hasGetElementById(v) {\r\n return !!v && typeof v === 'object' && typeof v.getElementById === 'function'\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is { querySelector(sel: string): Element | null }}\r\n */\r\nfunction hasQuerySelector(v) {\r\n return !!v && typeof v === 'object' && typeof v.querySelector === 'function'\r\n}\r\n\r\n/**\r\n * Minimal CSS.escape fallback for environments where CSS.escape is missing.\r\n * We only need to safely build `#${id}` selectors.\r\n *\r\n * @param {string} id\r\n * @returns {string}\r\n */\r\nfunction cssEscape(id) {\r\n const s = String(id)\r\n\r\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\r\n return CSS.escape(s)\r\n }\r\n\r\n if (!NEEDS_START_ESCAPE_RE.test(s) && SAFE_ID_RE.test(s)) return s\r\n\r\n let out = ''\r\n for (let i = 0; i < s.length;) {\r\n const cp = s.codePointAt(i)\r\n const ch = String.fromCodePoint(cp)\r\n\r\n const isAsciiSafe =\r\n (cp >= 48 && cp <= 57) || // 0-9\r\n (cp >= 65 && cp <= 90) || // A-Z\r\n (cp >= 97 && cp <= 122) || // a-z\r\n cp === 95 || // _\r\n cp === 45 // -\r\n\r\n const next = s.codePointAt(i + 1)\r\n const startsWithDigit = cp >= 48 && cp <= 57\r\n const startsWithDashDigit = cp === 45 && s.length > 1 && next >= 48 && next <= 57\r\n const needsStartEscape = i === 0 && (startsWithDigit || startsWithDashDigit)\r\n\r\n if (!needsStartEscape && (isAsciiSafe || cp >= 0x00a0)) {\r\n out += ch\r\n } else if (i === 0 && startsWithDashDigit) {\r\n out += '\\\\-'\r\n } else {\r\n out += `\\\\${cp.toString(16).toUpperCase()} `\r\n }\r\n\r\n i += ch.length\r\n }\r\n\r\n return out\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is Element}\r\n */\r\nfunction isElementNode(v) {\r\n if (!v || typeof v !== 'object') return false\r\n if (typeof Element !== 'undefined') return v instanceof Element\r\n return /** @type {any} */ (v).nodeType === 1\r\n}\r\n\r\n/**\r\n * Resolve an element by id from a root.\r\n * Supports:\r\n * - Document (getElementById)\r\n * - ShadowRoot / DocumentFragment / Element (querySelector fallback)\r\n *\r\n * @param {any} root\r\n * @param {string} id\r\n * @returns {Element | null}\r\n */\r\nfunction getById(root, id) {\r\n if (!root) return null\r\n\r\n if (hasGetElementById(root)) return root.getElementById(id)\r\n\r\n if (hasQuerySelector(root)) {\r\n const el = root.querySelector(`#${cssEscape(id)}`)\r\n return isElementNode(el) ? el : null\r\n }\r\n\r\n return null\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Validation helpers\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is string}\r\n */\r\nfunction isValidId(v) {\r\n return typeof v === 'string' && v.length > 0\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is string}\r\n */\r\nfunction isValidTagName(v) {\r\n return typeof v === 'string' && v.trim().length > 0\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is Function}\r\n */\r\nfunction isConstructor(v) {\r\n return typeof v === 'function'\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Error / policy helpers\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @param {string} id\r\n * @returns {string}\r\n */\r\nfunction fmtId(id) {\r\n return id.startsWith('#') ? id : `#${id}`\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {string} expected\r\n * @returns {Error}\r\n */\r\nfunction missingElError(id, expected) {\r\n return new Error(`id-dom: missing ${expected} element ${fmtId(id)}`)\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {string} expected\r\n * @param {string} got\r\n * @returns {Error}\r\n */\r\nfunction wrongTypeError(id, expected, got) {\r\n return new Error(`id-dom: expected ${expected} for ${fmtId(id)}, got ${got}`)\r\n}\r\n\r\n/**\r\n * @template T\r\n * @param {Error} err\r\n * @param {any} ctx\r\n * @param {ReturnType<typeof normalizeConfig>} cfg\r\n * @returns {T | null}\r\n */\r\nfunction handleLookupError(err, ctx, cfg) {\r\n try {\r\n cfg.onError?.(err, ctx)\r\n } catch {\r\n // reporting must never break app logic\r\n }\r\n\r\n if (cfg.warn) console.warn(err, ctx)\r\n\r\n if (cfg.mode === 'throw') throw err\r\n return null\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {any} root\r\n * @param {string} reason\r\n * @param {object} [extra]\r\n * @returns {any}\r\n */\r\nfunction createCtx(id, root, reason, extra) {\r\n return {\r\n id,\r\n root,\r\n reason,\r\n ...(extra || {}),\r\n }\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Internal generic resolver\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @template T\r\n * @param {DomConfig | undefined} config\r\n * @param {{\r\n * id: string,\r\n * validateInput: (cfg: ReturnType<typeof normalizeConfig>) => { err: Error, ctx: any } | null,\r\n * onMissing: (cfg: ReturnType<typeof normalizeConfig>) => { err: Error, ctx: any },\r\n * matches: (el: Element, cfg: ReturnType<typeof normalizeConfig>) => boolean,\r\n * onMismatch: (el: Element, cfg: ReturnType<typeof normalizeConfig>) => { err: Error, ctx: any },\r\n * }} spec\r\n * @returns {T | null}\r\n */\r\nfunction resolveLookup(config, spec) {\r\n const cfg = normalizeConfig(config)\r\n\r\n const inputFailure = spec.validateInput(cfg)\r\n if (inputFailure) {\r\n return handleLookupError(inputFailure.err, inputFailure.ctx, cfg)\r\n }\r\n\r\n const el = getById(cfg.root, spec.id)\r\n if (!el) {\r\n const failure = spec.onMissing(cfg)\r\n return handleLookupError(failure.err, failure.ctx, cfg)\r\n }\r\n\r\n if (!spec.matches(el, cfg)) {\r\n const failure = spec.onMismatch(el, cfg)\r\n return handleLookupError(failure.err, failure.ctx, cfg)\r\n }\r\n\r\n return /** @type {T} */ (el)\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Public APIs\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * Typed lookup by ID.\r\n *\r\n * @template {Element} T\r\n * @param {string} id\r\n * @param {{ new (...args: any[]): T }} Type\r\n * @param {DomConfig} [config]\r\n * @returns {T | null}\r\n */\r\nexport function byId(id, Type, config) {\r\n return resolveLookup(config, {\r\n id,\r\n\r\n validateInput(cfg) {\r\n if (!isValidId(id)) {\r\n return {\r\n err: new Error('id-dom: invalid id (expected non-empty string)'),\r\n ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { Type }),\r\n }\r\n }\r\n\r\n if (!isConstructor(Type)) {\r\n return {\r\n err: new Error(`id-dom: invalid Type for ${fmtId(id)}`),\r\n ctx: createCtx(id, cfg.root, REASON.INVALID_TYPE, { Type }),\r\n }\r\n }\r\n\r\n return null\r\n },\r\n\r\n onMissing(cfg) {\r\n return {\r\n err: missingElError(id, Type.name),\r\n ctx: createCtx(id, cfg.root, REASON.MISSING, { Type }),\r\n }\r\n },\r\n\r\n matches(el) {\r\n return el instanceof Type\r\n },\r\n\r\n onMismatch(el, cfg) {\r\n const got = el?.constructor?.name || typeof el\r\n return {\r\n err: wrongTypeError(id, Type.name, got),\r\n ctx: createCtx(id, cfg.root, REASON.WRONG_TYPE, { Type, got }),\r\n }\r\n },\r\n })\r\n}\r\n\r\n/**\r\n * Optional typed lookup: always returns T | null.\r\n *\r\n * @template {Element} T\r\n * @param {string} id\r\n * @param {{ new (...args: any[]): T }} Type\r\n * @param {DomConfig} [config]\r\n * @returns {T | null}\r\n */\r\nbyId.optional = function byIdOptional(id, Type, config) {\r\n return byId(id, Type, { ...config, mode: 'null' })\r\n}\r\n\r\nbyId.opt = byId.optional\r\n\r\n/**\r\n * Tag-name lookup by element tag.\r\n * Useful when constructor checks are not the right fit.\r\n *\r\n * @param {string} id\r\n * @param {string} tagName\r\n * @param {DomConfig} [config]\r\n * @returns {Element | null}\r\n */\r\nexport function tag(id, tagName, config) {\r\n return resolveLookup(config, {\r\n id,\r\n\r\n validateInput(cfg) {\r\n if (!isValidId(id)) {\r\n return {\r\n err: new Error('id-dom: invalid id (expected non-empty string)'),\r\n ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { tagName }),\r\n }\r\n }\r\n\r\n if (!isValidTagName(tagName)) {\r\n return {\r\n err: new Error(`id-dom: invalid tagName for ${fmtId(id)}`),\r\n ctx: createCtx(id, cfg.root, REASON.INVALID_TAG, { tagName }),\r\n }\r\n }\r\n\r\n return null\r\n },\r\n\r\n onMissing(cfg) {\r\n return {\r\n err: missingElError(id, `<${tagName}>`),\r\n ctx: createCtx(id, cfg.root, REASON.MISSING, { tagName }),\r\n }\r\n },\r\n\r\n matches(el) {\r\n return String(el.tagName || '').toUpperCase() === String(tagName).toUpperCase()\r\n },\r\n\r\n onMismatch(el, cfg) {\r\n const expected = String(tagName).toUpperCase()\r\n const got = String(el.tagName || '').toUpperCase()\r\n\r\n return {\r\n err: wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),\r\n ctx: createCtx(id, cfg.root, REASON.WRONG_TAG, { tagName, got }),\r\n }\r\n },\r\n })\r\n}\r\n\r\n/**\r\n * Optional tag lookup: always returns Element | null.\r\n *\r\n * @param {string} id\r\n * @param {string} tagName\r\n * @param {DomConfig} [config]\r\n * @returns {Element | null}\r\n */\r\ntag.optional = function tagOptional(id, tagName, config) {\r\n return tag(id, tagName, { ...config, mode: 'null' })\r\n}\r\n\r\ntag.opt = tag.optional\r\n\r\n// -----------------------------------------------------------------------------\r\n// Helper registries\r\n// -----------------------------------------------------------------------------\r\n\r\nconst TYPE_HELPERS = /** @type {Record<string, any>} */ ({\r\n el: typeof HTMLElement !== 'undefined' ? HTMLElement : null,\r\n input: typeof HTMLInputElement !== 'undefined' ? HTMLInputElement : null,\r\n button: typeof HTMLButtonElement !== 'undefined' ? HTMLButtonElement : null,\r\n textarea: typeof HTMLTextAreaElement !== 'undefined' ? HTMLTextAreaElement : null,\r\n select: typeof HTMLSelectElement !== 'undefined' ? HTMLSelectElement : null,\r\n form: typeof HTMLFormElement !== 'undefined' ? HTMLFormElement : null,\r\n div: typeof HTMLDivElement !== 'undefined' ? HTMLDivElement : null,\r\n span: typeof HTMLSpanElement !== 'undefined' ? HTMLSpanElement : null,\r\n label: typeof HTMLLabelElement !== 'undefined' ? HTMLLabelElement : null,\r\n canvas: typeof HTMLCanvasElement !== 'undefined' ? HTMLCanvasElement : null,\r\n template: typeof HTMLTemplateElement !== 'undefined' ? HTMLTemplateElement : null,\r\n svg: typeof SVGSVGElement !== 'undefined' ? SVGSVGElement : null,\r\n body: typeof HTMLBodyElement !== 'undefined' ? HTMLBodyElement : null,\r\n})\r\n\r\nconst TAG_HELPERS = /** @type {Record<string, string>} */ ({\r\n main: 'main',\r\n section: 'section',\r\n small: 'small',\r\n})\r\n\r\n// -----------------------------------------------------------------------------\r\n// Helper builders\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @template {Function} T\r\n * @param {T} fn\r\n * @param {Function} optionalFn\r\n * @returns {T & { optional: Function, opt: Function }}\r\n */\r\nfunction attachOptional(fn, optionalFn) {\r\n if (typeof fn !== 'function') {\r\n throw new TypeError('id-dom: attachOptional expected fn to be a function')\r\n }\r\n\r\n if (typeof optionalFn !== 'function') {\r\n throw new TypeError('id-dom: attachOptional expected optionalFn to be a function')\r\n }\r\n\r\n fn.optional = optionalFn\r\n fn.opt = optionalFn\r\n return fn\r\n}\r\n\r\n/**\r\n * @param {any} Type\r\n * @param {ReturnType<typeof normalizeConfig>} base\r\n * @param {ReturnType<typeof normalizeConfig>} baseNull\r\n */\r\nfunction makeTypedHelper(Type, base, baseNull) {\r\n if (!Type) {\r\n throw new TypeError('id-dom: makeTypedHelper received an invalid Type')\r\n }\r\n\r\n return attachOptional(\r\n (id) => byId(id, Type, base),\r\n (id) => byId(id, Type, baseNull)\r\n )\r\n}\r\n\r\n/**\r\n * @param {string} tagName\r\n * @param {ReturnType<typeof normalizeConfig>} base\r\n * @param {ReturnType<typeof normalizeConfig>} baseNull\r\n */\r\nfunction makeTagHelper(tagName, base, baseNull) {\r\n if (!tagName) {\r\n throw new TypeError('id-dom: makeTagHelper received an invalid tagName')\r\n }\r\n\r\n return attachOptional(\r\n (id) => tag(id, tagName, base),\r\n (id) => tag(id, tagName, baseNull)\r\n )\r\n}\r\n// -----------------------------------------------------------------------------\r\n// Factory\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * Factory: scope getters to a specific root + default policy.\r\n *\r\n * @param {any} root\r\n * @param {Omit<DomConfig, 'root'>} [config]\r\n */\r\nexport function createDom(root, config) {\r\n const base = normalizeConfig({ ...config, root })\r\n const baseNull = { ...base, mode: 'null' }\r\n\r\n /** @type {any} */\r\n const api = {}\r\n\r\n api.byId = attachOptional(\r\n (id, Type) => byId(id, Type, base),\r\n (id, Type) => byId(id, Type, baseNull)\r\n )\r\n\r\n api.tag = attachOptional(\r\n (id, name) => tag(id, name, base),\r\n (id, name) => tag(id, name, baseNull)\r\n )\r\n\r\n for (const [name, Type] of Object.entries(TYPE_HELPERS)) {\r\n if (!Type) continue\r\n api[name] = makeTypedHelper(Type, base, baseNull)\r\n }\r\n\r\n for (const [name, tagName] of Object.entries(TAG_HELPERS)) {\r\n api[name] = makeTagHelper(tagName, base, baseNull)\r\n }\r\n\r\n return api\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Default export\r\n// -----------------------------------------------------------------------------\r\n\r\nconst dom = createDom(DEFAULT_ROOT, { mode: 'throw' })\r\nexport default dom"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/index.js CHANGED
@@ -2,10 +2,22 @@ const DEFAULT_ROOT = typeof document !== "undefined" && document ? document : (
2
2
  /** @type {any} */
3
3
  null
4
4
  );
5
+ const REASON = (
6
+ /** @type {const} */
7
+ {
8
+ INVALID_ID: "invalid-id",
9
+ INVALID_TYPE: "invalid-type",
10
+ INVALID_TAG: "invalid-tag",
11
+ MISSING: "missing",
12
+ WRONG_TYPE: "wrong-type",
13
+ WRONG_TAG: "wrong-tag"
14
+ }
15
+ );
16
+ const SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
17
+ const NEEDS_START_ESCAPE_RE = /^(?:\d|-\d)/;
5
18
  function normalizeConfig(cfg) {
6
19
  return {
7
20
  mode: cfg?.mode ?? "throw",
8
- // default strict
9
21
  warn: cfg?.warn ?? false,
10
22
  onError: typeof cfg?.onError === "function" ? cfg.onError : null,
11
23
  root: cfg?.root ?? DEFAULT_ROOT
@@ -17,8 +29,6 @@ function hasGetElementById(v) {
17
29
  function hasQuerySelector(v) {
18
30
  return !!v && typeof v === "object" && typeof v.querySelector === "function";
19
31
  }
20
- const SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
21
- const NEEDS_START_ESCAPE_RE = /^(?:\d|-\d)/;
22
32
  function cssEscape(id) {
23
33
  const s = String(id);
24
34
  if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
@@ -34,10 +44,13 @@ function cssEscape(id) {
34
44
  cp >= 97 && cp <= 122 || // a-z
35
45
  cp === 95 || // _
36
46
  cp === 45;
37
- const needsStartEscape = i === 0 && (cp >= 48 && cp <= 57 || cp === 45 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57);
47
+ const next = s.codePointAt(i + 1);
48
+ const startsWithDigit = cp >= 48 && cp <= 57;
49
+ const startsWithDashDigit = cp === 45 && s.length > 1 && next >= 48 && next <= 57;
50
+ const needsStartEscape = i === 0 && (startsWithDigit || startsWithDashDigit);
38
51
  if (!needsStartEscape && (isAsciiSafe || cp >= 160)) {
39
52
  out += ch;
40
- } else if (cp === 45 && i === 0 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57) {
53
+ } else if (i === 0 && startsWithDashDigit) {
41
54
  out += "\\-";
42
55
  } else {
43
56
  out += `\\${cp.toString(16).toUpperCase()} `;
@@ -46,16 +59,32 @@ function cssEscape(id) {
46
59
  }
47
60
  return out;
48
61
  }
62
+ function isElementNode(v) {
63
+ if (!v || typeof v !== "object") return false;
64
+ if (typeof Element !== "undefined") return v instanceof Element;
65
+ return (
66
+ /** @type {any} */
67
+ v.nodeType === 1
68
+ );
69
+ }
49
70
  function getById(root, id) {
50
71
  if (!root) return null;
51
72
  if (hasGetElementById(root)) return root.getElementById(id);
52
73
  if (hasQuerySelector(root)) {
53
- const sel = `#${cssEscape(id)}`;
54
- const el = root.querySelector(sel);
55
- return el instanceof HTMLElement ? el : null;
74
+ const el = root.querySelector(`#${cssEscape(id)}`);
75
+ return isElementNode(el) ? el : null;
56
76
  }
57
77
  return null;
58
78
  }
79
+ function isValidId(v) {
80
+ return typeof v === "string" && v.length > 0;
81
+ }
82
+ function isValidTagName(v) {
83
+ return typeof v === "string" && v.trim().length > 0;
84
+ }
85
+ function isConstructor(v) {
86
+ return typeof v === "function";
87
+ }
59
88
  function fmtId(id) {
60
89
  return id.startsWith("#") ? id : `#${id}`;
61
90
  }
@@ -70,54 +99,114 @@ function handleLookupError(err, ctx, cfg) {
70
99
  cfg.onError?.(err, ctx);
71
100
  } catch {
72
101
  }
73
- if (cfg.warn) console.warn(err);
102
+ if (cfg.warn) console.warn(err, ctx);
74
103
  if (cfg.mode === "throw") throw err;
75
104
  return null;
76
105
  }
77
- function byId(id, Type, config) {
106
+ function createCtx(id, root, reason, extra) {
107
+ return {
108
+ id,
109
+ root,
110
+ reason,
111
+ ...extra || {}
112
+ };
113
+ }
114
+ function resolveLookup(config, spec) {
78
115
  const cfg = normalizeConfig(config);
79
- const el = getById(cfg.root, id);
116
+ const inputFailure = spec.validateInput(cfg);
117
+ if (inputFailure) {
118
+ return handleLookupError(inputFailure.err, inputFailure.ctx, cfg);
119
+ }
120
+ const el = getById(cfg.root, spec.id);
80
121
  if (!el) {
81
- return handleLookupError(
82
- missingElError(id, Type.name),
83
- { id, Type, root: cfg.root, reason: "missing" },
84
- cfg
85
- );
86
- }
87
- if (!(el instanceof Type)) {
88
- const got = el?.constructor?.name || typeof el;
89
- return handleLookupError(
90
- wrongTypeError(id, Type.name, got),
91
- { id, Type, root: cfg.root, reason: "wrong-type", got },
92
- cfg
93
- );
94
- }
95
- return el;
122
+ const failure = spec.onMissing(cfg);
123
+ return handleLookupError(failure.err, failure.ctx, cfg);
124
+ }
125
+ if (!spec.matches(el, cfg)) {
126
+ const failure = spec.onMismatch(el, cfg);
127
+ return handleLookupError(failure.err, failure.ctx, cfg);
128
+ }
129
+ return (
130
+ /** @type {T} */
131
+ el
132
+ );
133
+ }
134
+ function byId(id, Type, config) {
135
+ return resolveLookup(config, {
136
+ id,
137
+ validateInput(cfg) {
138
+ if (!isValidId(id)) {
139
+ return {
140
+ err: new Error("id-dom: invalid id (expected non-empty string)"),
141
+ ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { Type })
142
+ };
143
+ }
144
+ if (!isConstructor(Type)) {
145
+ return {
146
+ err: new Error(`id-dom: invalid Type for ${fmtId(id)}`),
147
+ ctx: createCtx(id, cfg.root, REASON.INVALID_TYPE, { Type })
148
+ };
149
+ }
150
+ return null;
151
+ },
152
+ onMissing(cfg) {
153
+ return {
154
+ err: missingElError(id, Type.name),
155
+ ctx: createCtx(id, cfg.root, REASON.MISSING, { Type })
156
+ };
157
+ },
158
+ matches(el) {
159
+ return el instanceof Type;
160
+ },
161
+ onMismatch(el, cfg) {
162
+ const got = el?.constructor?.name || typeof el;
163
+ return {
164
+ err: wrongTypeError(id, Type.name, got),
165
+ ctx: createCtx(id, cfg.root, REASON.WRONG_TYPE, { Type, got })
166
+ };
167
+ }
168
+ });
96
169
  }
97
170
  byId.optional = function byIdOptional(id, Type, config) {
98
171
  return byId(id, Type, { ...config, mode: "null" });
99
172
  };
100
173
  byId.opt = byId.optional;
101
174
  function tag(id, tagName, config) {
102
- const cfg = normalizeConfig(config);
103
- const el = getById(cfg.root, id);
104
- if (!el) {
105
- return handleLookupError(
106
- missingElError(id, `<${tagName}>`),
107
- { id, tagName, root: cfg.root, reason: "missing" },
108
- cfg
109
- );
110
- }
111
- const expected = String(tagName).toUpperCase();
112
- const got = String(el.tagName || "").toUpperCase();
113
- if (got !== expected) {
114
- return handleLookupError(
115
- wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),
116
- { id, tagName, root: cfg.root, reason: "wrong-tag", got },
117
- cfg
118
- );
119
- }
120
- return el;
175
+ return resolveLookup(config, {
176
+ id,
177
+ validateInput(cfg) {
178
+ if (!isValidId(id)) {
179
+ return {
180
+ err: new Error("id-dom: invalid id (expected non-empty string)"),
181
+ ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { tagName })
182
+ };
183
+ }
184
+ if (!isValidTagName(tagName)) {
185
+ return {
186
+ err: new Error(`id-dom: invalid tagName for ${fmtId(id)}`),
187
+ ctx: createCtx(id, cfg.root, REASON.INVALID_TAG, { tagName })
188
+ };
189
+ }
190
+ return null;
191
+ },
192
+ onMissing(cfg) {
193
+ return {
194
+ err: missingElError(id, `<${tagName}>`),
195
+ ctx: createCtx(id, cfg.root, REASON.MISSING, { tagName })
196
+ };
197
+ },
198
+ matches(el) {
199
+ return String(el.tagName || "").toUpperCase() === String(tagName).toUpperCase();
200
+ },
201
+ onMismatch(el, cfg) {
202
+ const expected = String(tagName).toUpperCase();
203
+ const got = String(el.tagName || "").toUpperCase();
204
+ return {
205
+ err: wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),
206
+ ctx: createCtx(id, cfg.root, REASON.WRONG_TAG, { tagName, got })
207
+ };
208
+ }
209
+ });
121
210
  }
122
211
  tag.optional = function tagOptional(id, tagName, config) {
123
212
  return tag(id, tagName, { ...config, mode: "null" });
@@ -137,7 +226,8 @@ const TYPE_HELPERS = (
137
226
  label: typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null,
138
227
  canvas: typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement : null,
139
228
  template: typeof HTMLTemplateElement !== "undefined" ? HTMLTemplateElement : null,
140
- svg: typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null
229
+ svg: typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null,
230
+ body: typeof HTMLBodyElement !== "undefined" ? HTMLBodyElement : null
141
231
  }
142
232
  );
143
233
  const TAG_HELPERS = (
@@ -148,42 +238,53 @@ const TAG_HELPERS = (
148
238
  small: "small"
149
239
  }
150
240
  );
241
+ function attachOptional(fn, optionalFn) {
242
+ if (typeof fn !== "function") {
243
+ throw new TypeError("id-dom: attachOptional expected fn to be a function");
244
+ }
245
+ if (typeof optionalFn !== "function") {
246
+ throw new TypeError("id-dom: attachOptional expected optionalFn to be a function");
247
+ }
248
+ fn.optional = optionalFn;
249
+ fn.opt = optionalFn;
250
+ return fn;
251
+ }
252
+ function makeTypedHelper(Type, base, baseNull) {
253
+ if (!Type) {
254
+ throw new TypeError("id-dom: makeTypedHelper received an invalid Type");
255
+ }
256
+ return attachOptional(
257
+ (id) => byId(id, Type, base),
258
+ (id) => byId(id, Type, baseNull)
259
+ );
260
+ }
261
+ function makeTagHelper(tagName, base, baseNull) {
262
+ if (!tagName) {
263
+ throw new TypeError("id-dom: makeTagHelper received an invalid tagName");
264
+ }
265
+ return attachOptional(
266
+ (id) => tag(id, tagName, base),
267
+ (id) => tag(id, tagName, baseNull)
268
+ );
269
+ }
151
270
  function createDom(root, config) {
152
271
  const base = normalizeConfig({ ...config, root });
153
272
  const baseNull = { ...base, mode: "null" };
154
- const api = {
155
- // generic
156
- byId: (id, Type) => byId(id, Type, base),
157
- tag: (id, name) => tag(id, name, base)
158
- };
273
+ const api = {};
274
+ api.byId = attachOptional(
275
+ (id, Type) => byId(id, Type, base),
276
+ (id, Type) => byId(id, Type, baseNull)
277
+ );
278
+ api.tag = attachOptional(
279
+ (id, name) => tag(id, name, base),
280
+ (id, name) => tag(id, name, baseNull)
281
+ );
159
282
  for (const [name, Type] of Object.entries(TYPE_HELPERS)) {
160
283
  if (!Type) continue;
161
- api[name] = (id) => byId(id, Type, base);
284
+ api[name] = makeTypedHelper(Type, base, baseNull);
162
285
  }
163
286
  for (const [name, tagName] of Object.entries(TAG_HELPERS)) {
164
- api[name] = (id) => tag(id, tagName, base);
165
- }
166
- api.byId.optional = (id, Type) => byId(id, Type, baseNull);
167
- api.byId.opt = api.byId.optional;
168
- api.tag.optional = (id, name) => tag(id, name, baseNull);
169
- api.tag.opt = api.tag.optional;
170
- for (const k of Object.keys(api)) {
171
- const fn = api[k];
172
- if (typeof fn !== "function") continue;
173
- if (fn.optional) continue;
174
- if (k in TAG_HELPERS) {
175
- const tagName = TAG_HELPERS[k];
176
- fn.optional = (id) => tag(id, tagName, baseNull);
177
- fn.opt = fn.optional;
178
- continue;
179
- }
180
- if (k in TYPE_HELPERS) {
181
- const Type = TYPE_HELPERS[k];
182
- if (Type) {
183
- fn.optional = (id) => byId(id, Type, baseNull);
184
- fn.opt = fn.optional;
185
- }
186
- }
287
+ api[name] = makeTagHelper(tagName, base, baseNull);
187
288
  }
188
289
  return api;
189
290
  }
package/dist/index.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/id-dom.js"],
4
- "sourcesContent": ["// id-dom.js\r\n// id-dom \u2014 deterministic DOM element getters by ID (typed, tiny, modern)\r\n//\r\n// Goals:\r\n// - Prefer getElementById (fast, unambiguous)\r\n// - Return the correct type or fail predictably\r\n// - Provide strict + optional variants\r\n// - Allow app/module-level defaults (throw vs null) without bundler magic\r\n// - Zero deps, framework-agnostic\r\n\r\nconst DEFAULT_ROOT =\r\n typeof document !== 'undefined' && document ? document : /** @type {any} */ (null)\r\n\r\n/**\r\n * @typedef {'throw' | 'null'} DomMode\r\n */\r\n\r\n/**\r\n * @typedef {{\r\n * mode?: DomMode\r\n * warn?: boolean\r\n * onError?: (error: Error, ctx: any) => void\r\n * root?: any\r\n * }} DomConfig\r\n */\r\n\r\n/**\r\n * @param {DomConfig | undefined} cfg\r\n */\r\nfunction normalizeConfig(cfg) {\r\n return {\r\n mode: cfg?.mode ?? 'throw', // default strict\r\n warn: cfg?.warn ?? false,\r\n onError: typeof cfg?.onError === 'function' ? cfg.onError : null,\r\n root: cfg?.root ?? DEFAULT_ROOT,\r\n }\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is { getElementById(id: string): HTMLElement | null }}\r\n */\r\nfunction hasGetElementById(v) {\r\n return !!v && typeof v === 'object' && typeof v.getElementById === 'function'\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is { querySelector(sel: string): Element | null }}\r\n */\r\nfunction hasQuerySelector(v) {\r\n return !!v && typeof v === 'object' && typeof v.querySelector === 'function'\r\n}\r\n\r\n\r\nconst SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/\r\nconst NEEDS_START_ESCAPE_RE = /^(?:\\d|-\\d)/\r\n\r\n/**\r\n * Minimal CSS.escape fallback for environments where CSS.escape is missing (e.g. some jsdom builds).\r\n * We only need to safely build `#${id}` selectors.\r\n *\r\n * @param {string} id\r\n */\r\nfunction cssEscape(id) {\r\n const s = String(id)\r\n\r\n // Prefer native when available\r\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\r\n return CSS.escape(s)\r\n }\r\n\r\n // Fast path: most app IDs are already safe\r\n if (!NEEDS_START_ESCAPE_RE.test(s) && SAFE_ID_RE.test(s)) return s\r\n\r\n // One-pass escape:\r\n // - Escape any char outside a conservative \"safe\" set\r\n // - Also escape the start if it begins with a digit OR \"-<digit>\"\r\n let out = ''\r\n for (let i = 0; i < s.length; ) {\r\n const cp = s.codePointAt(i)\r\n const ch = String.fromCodePoint(cp)\r\n\r\n const isAsciiSafe =\r\n (cp >= 48 && cp <= 57) || // 0-9\r\n (cp >= 65 && cp <= 90) || // A-Z\r\n (cp >= 97 && cp <= 122) || // a-z\r\n cp === 95 || // _\r\n cp === 45 // -\r\n\r\n const needsStartEscape =\r\n i === 0 && ((cp >= 48 && cp <= 57) || (cp === 45 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57))\r\n\r\n if (!needsStartEscape && (isAsciiSafe || cp >= 0x00A0)) {\r\n // allow non-ascii chars directly (common CSS ident behavior)\r\n out += ch\r\n } else if (cp === 45 && i === 0 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57) {\r\n // \"-<digit>\" start: escaping just the leading hyphen is a simple fix\r\n out += '\\\\-'\r\n } else {\r\n // hex escape + trailing space is safest\r\n out += `\\\\${cp.toString(16).toUpperCase()} `\r\n }\r\n\r\n i += ch.length\r\n }\r\n\r\n return out\r\n}\r\n\r\n\r\n/**\r\n * Resolve an element by id from a \"root\".\r\n * Supports:\r\n * - Document (getElementById)\r\n * - ShadowRoot / DocumentFragment / Element (querySelector fallback)\r\n *\r\n * @param {any} root\r\n * @param {string} id\r\n * @returns {HTMLElement | null}\r\n */\r\nfunction getById(root, id) {\r\n if (!root) return null\r\n\r\n if (hasGetElementById(root)) return root.getElementById(id)\r\n\r\n // ShadowRoot/DocumentFragment/Element don\u2019t have getElementById\r\n if (hasQuerySelector(root)) {\r\n const sel = `#${cssEscape(id)}`\r\n const el = root.querySelector(sel)\r\n return el instanceof HTMLElement ? el : null\r\n }\r\n\r\n return null\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @returns {string}\r\n */\r\nfunction fmtId(id) {\r\n return id.startsWith('#') ? id : `#${id}`\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {string} expected\r\n */\r\nfunction missingElError(id, expected) {\r\n return new Error(`id-dom: missing ${expected} element ${fmtId(id)}`)\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {string} expected\r\n * @param {string} got\r\n */\r\nfunction wrongTypeError(id, expected, got) {\r\n return new Error(`id-dom: expected ${expected} for ${fmtId(id)}, got ${got}`)\r\n}\r\n\r\n/**\r\n * Centralized error policy:\r\n * - always call onError if present\r\n * - optionally warn\r\n * - throw or return null depending on mode\r\n *\r\n * @template T\r\n * @param {Error} err\r\n * @param {any} ctx\r\n * @param {ReturnType<typeof normalizeConfig>} cfg\r\n * @returns {T | null}\r\n */\r\nfunction handleLookupError(err, ctx, cfg) {\r\n try {\r\n cfg.onError?.(err, ctx)\r\n } catch {\r\n // do not let reporting break app logic\r\n }\r\n\r\n if (cfg.warn) console.warn(err)\r\n\r\n if (cfg.mode === 'throw') throw err\r\n return null\r\n}\r\n\r\n/**\r\n * Typed lookup by ID.\r\n * Behavior is controlled by config:\r\n * - mode: 'throw' (default) or 'null'\r\n * - warn: boolean\r\n * - onError(err, ctx)\r\n *\r\n * @template {Element} T\r\n * @param {string} id\r\n * @param {{ new (...args: any[]): T }} Type\r\n * @param {DomConfig} [config]\r\n * @returns {T | null}\r\n */\r\nexport function byId(id, Type, config) {\r\n const cfg = normalizeConfig(config)\r\n const el = getById(cfg.root, id)\r\n\r\n if (!el) {\r\n return handleLookupError(\r\n missingElError(id, Type.name),\r\n { id, Type, root: cfg.root, reason: 'missing' },\r\n cfg\r\n )\r\n }\r\n\r\n if (!(el instanceof Type)) {\r\n const got = el?.constructor?.name || typeof el\r\n return handleLookupError(\r\n wrongTypeError(id, Type.name, got),\r\n { id, Type, root: cfg.root, reason: 'wrong-type', got },\r\n cfg\r\n )\r\n }\r\n\r\n return el\r\n}\r\n\r\n/**\r\n * Optional typed lookup: ALWAYS returns T | null (never throws for missing/wrong-type).\r\n *\r\n * @template {Element} T\r\n * @param {string} id\r\n * @param {{ new (...args: any[]): T }} Type\r\n * @param {DomConfig} [config]\r\n * @returns {T | null}\r\n */\r\nbyId.optional = function byIdOptional(id, Type, config) {\r\n return byId(id, Type, { ...config, mode: 'null' })\r\n}\r\n\r\n// Short alias (module-level; do not reassign inside factories)\r\nbyId.opt = byId.optional\r\n\r\n/**\r\n * Tag-name lookup (HTMLElement only).\r\n * Useful for semantic elements that don\u2019t have unique constructors.\r\n *\r\n * @param {string} id\r\n * @param {string} tagName\r\n * @param {DomConfig} [config]\r\n * @returns {HTMLElement | null}\r\n */\r\nexport function tag(id, tagName, config) {\r\n const cfg = normalizeConfig(config)\r\n const el = getById(cfg.root, id)\r\n\r\n if (!el) {\r\n return handleLookupError(\r\n missingElError(id, `<${tagName}>`),\r\n { id, tagName, root: cfg.root, reason: 'missing' },\r\n cfg\r\n )\r\n }\r\n\r\n const expected = String(tagName).toUpperCase()\r\n const got = String(el.tagName || '').toUpperCase()\r\n\r\n if (got !== expected) {\r\n return handleLookupError(\r\n wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),\r\n { id, tagName, root: cfg.root, reason: 'wrong-tag', got },\r\n cfg\r\n )\r\n }\r\n\r\n return el\r\n}\r\n\r\n/**\r\n * Optional tag lookup: ALWAYS returns HTMLElement | null (never throws for missing/wrong-tag).\r\n *\r\n * @param {string} id\r\n * @param {string} tagName\r\n * @param {DomConfig} [config]\r\n * @returns {HTMLElement | null}\r\n */\r\ntag.optional = function tagOptional(id, tagName, config) {\r\n return tag(id, tagName, { ...config, mode: 'null' })\r\n}\r\n\r\n// Short alias (module-level; do not reassign inside factories)\r\ntag.opt = tag.optional\r\n\r\n// --- internal maps for factory helpers ---\r\nconst TYPE_HELPERS = /** @type {Record<string, any>} */ ({\r\n el: typeof HTMLElement !== 'undefined' ? HTMLElement : null,\r\n input: typeof HTMLInputElement !== 'undefined' ? HTMLInputElement : null,\r\n button: typeof HTMLButtonElement !== 'undefined' ? HTMLButtonElement : null,\r\n textarea: typeof HTMLTextAreaElement !== 'undefined' ? HTMLTextAreaElement : null,\r\n select: typeof HTMLSelectElement !== 'undefined' ? HTMLSelectElement : null,\r\n form: typeof HTMLFormElement !== 'undefined' ? HTMLFormElement : null,\r\n div: typeof HTMLDivElement !== 'undefined' ? HTMLDivElement : null,\r\n span: typeof HTMLSpanElement !== 'undefined' ? HTMLSpanElement : null,\r\n label: typeof HTMLLabelElement !== 'undefined' ? HTMLLabelElement : null,\r\n canvas: typeof HTMLCanvasElement !== 'undefined' ? HTMLCanvasElement : null,\r\n template: typeof HTMLTemplateElement !== 'undefined' ? HTMLTemplateElement : null,\r\n svg: typeof SVGSVGElement !== 'undefined' ? SVGSVGElement : null,\r\n})\r\n\r\nconst TAG_HELPERS = /** @type {Record<string, string>} */ ({\r\n main: 'main',\r\n section: 'section',\r\n small: 'small',\r\n})\r\n\r\n/**\r\n * Factory: scope getters to a specific root + default policy.\r\n *\r\n * @param {any} root\r\n * @param {Omit<DomConfig, 'root'>} [config]\r\n */\r\nexport function createDom(root, config) {\r\n const base = normalizeConfig({ ...config, root })\r\n const baseNull = { ...base, mode: 'null' }\r\n\r\n /** @type {any} */\r\n const api = {\r\n // generic\r\n byId: (id, Type) => byId(id, Type, base),\r\n tag: (id, name) => tag(id, name, base),\r\n }\r\n\r\n // typed helpers\r\n for (const [name, Type] of Object.entries(TYPE_HELPERS)) {\r\n if (!Type) continue\r\n api[name] = (id) => byId(id, Type, base)\r\n }\r\n\r\n // semantic tag helpers\r\n for (const [name, tagName] of Object.entries(TAG_HELPERS)) {\r\n api[name] = (id) => tag(id, tagName, base)\r\n }\r\n\r\n // --- Optional variants (policy-based; never swallow unrelated exceptions) ---\r\n api.byId.optional = (id, Type) => byId(id, Type, baseNull)\r\n api.byId.opt = api.byId.optional\r\n\r\n api.tag.optional = (id, name) => tag(id, name, baseNull)\r\n api.tag.opt = api.tag.optional\r\n\r\n // Add `.optional` + `.opt` to every helper using the same \"null\" policy\r\n for (const k of Object.keys(api)) {\r\n const fn = api[k]\r\n if (typeof fn !== 'function') continue\r\n if (fn.optional) continue\r\n\r\n if (k in TAG_HELPERS) {\r\n const tagName = TAG_HELPERS[k]\r\n fn.optional = (id) => tag(id, tagName, baseNull)\r\n fn.opt = fn.optional\r\n continue\r\n }\r\n\r\n if (k in TYPE_HELPERS) {\r\n const Type = TYPE_HELPERS[k]\r\n if (Type) {\r\n fn.optional = (id) => byId(id, Type, baseNull)\r\n fn.opt = fn.optional\r\n }\r\n }\r\n }\r\n\r\n return api\r\n}\r\n\r\n// Default export: root = document (if available), strict by default\r\nconst dom = createDom(DEFAULT_ROOT, { mode: 'throw' })\r\nexport default dom\r\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["// id-dom.js\r\n// id-dom \u2014 deterministic DOM element getters by ID (typed, tiny, modern)\r\n//\r\n// Goals:\r\n// - Prefer getElementById (fast, unambiguous)\r\n// - Return the correct type or fail predictably\r\n// - Provide strict + optional variants\r\n// - Allow app/module-level defaults (throw vs null) without bundler magic\r\n// - Zero deps, framework-agnostic\r\n\r\nconst DEFAULT_ROOT =\r\n typeof document !== 'undefined' && document ? document : /** @type {any} */ (null)\r\n\r\nconst REASON = /** @type {const} */ ({\r\n INVALID_ID: 'invalid-id',\r\n INVALID_TYPE: 'invalid-type',\r\n INVALID_TAG: 'invalid-tag',\r\n MISSING: 'missing',\r\n WRONG_TYPE: 'wrong-type',\r\n WRONG_TAG: 'wrong-tag',\r\n})\r\n\r\nconst SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/\r\nconst NEEDS_START_ESCAPE_RE = /^(?:\\d|-\\d)/\r\n\r\n/**\r\n * @typedef {'throw' | 'null'} DomMode\r\n */\r\n\r\n/**\r\n * @typedef {{\r\n * mode?: DomMode\r\n * warn?: boolean\r\n * onError?: (error: Error, ctx: any) => void\r\n * root?: any\r\n * }} DomConfig\r\n */\r\n\r\n// -----------------------------------------------------------------------------\r\n// Config\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @param {DomConfig | undefined} cfg\r\n */\r\nfunction normalizeConfig(cfg) {\r\n return {\r\n mode: cfg?.mode ?? 'throw',\r\n warn: cfg?.warn ?? false,\r\n onError: typeof cfg?.onError === 'function' ? cfg.onError : null,\r\n root: cfg?.root ?? DEFAULT_ROOT,\r\n }\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Root / DOM helpers\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is { getElementById(id: string): Element | null }}\r\n */\r\nfunction hasGetElementById(v) {\r\n return !!v && typeof v === 'object' && typeof v.getElementById === 'function'\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is { querySelector(sel: string): Element | null }}\r\n */\r\nfunction hasQuerySelector(v) {\r\n return !!v && typeof v === 'object' && typeof v.querySelector === 'function'\r\n}\r\n\r\n/**\r\n * Minimal CSS.escape fallback for environments where CSS.escape is missing.\r\n * We only need to safely build `#${id}` selectors.\r\n *\r\n * @param {string} id\r\n * @returns {string}\r\n */\r\nfunction cssEscape(id) {\r\n const s = String(id)\r\n\r\n if (typeof CSS !== 'undefined' && typeof CSS.escape === 'function') {\r\n return CSS.escape(s)\r\n }\r\n\r\n if (!NEEDS_START_ESCAPE_RE.test(s) && SAFE_ID_RE.test(s)) return s\r\n\r\n let out = ''\r\n for (let i = 0; i < s.length;) {\r\n const cp = s.codePointAt(i)\r\n const ch = String.fromCodePoint(cp)\r\n\r\n const isAsciiSafe =\r\n (cp >= 48 && cp <= 57) || // 0-9\r\n (cp >= 65 && cp <= 90) || // A-Z\r\n (cp >= 97 && cp <= 122) || // a-z\r\n cp === 95 || // _\r\n cp === 45 // -\r\n\r\n const next = s.codePointAt(i + 1)\r\n const startsWithDigit = cp >= 48 && cp <= 57\r\n const startsWithDashDigit = cp === 45 && s.length > 1 && next >= 48 && next <= 57\r\n const needsStartEscape = i === 0 && (startsWithDigit || startsWithDashDigit)\r\n\r\n if (!needsStartEscape && (isAsciiSafe || cp >= 0x00a0)) {\r\n out += ch\r\n } else if (i === 0 && startsWithDashDigit) {\r\n out += '\\\\-'\r\n } else {\r\n out += `\\\\${cp.toString(16).toUpperCase()} `\r\n }\r\n\r\n i += ch.length\r\n }\r\n\r\n return out\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is Element}\r\n */\r\nfunction isElementNode(v) {\r\n if (!v || typeof v !== 'object') return false\r\n if (typeof Element !== 'undefined') return v instanceof Element\r\n return /** @type {any} */ (v).nodeType === 1\r\n}\r\n\r\n/**\r\n * Resolve an element by id from a root.\r\n * Supports:\r\n * - Document (getElementById)\r\n * - ShadowRoot / DocumentFragment / Element (querySelector fallback)\r\n *\r\n * @param {any} root\r\n * @param {string} id\r\n * @returns {Element | null}\r\n */\r\nfunction getById(root, id) {\r\n if (!root) return null\r\n\r\n if (hasGetElementById(root)) return root.getElementById(id)\r\n\r\n if (hasQuerySelector(root)) {\r\n const el = root.querySelector(`#${cssEscape(id)}`)\r\n return isElementNode(el) ? el : null\r\n }\r\n\r\n return null\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Validation helpers\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is string}\r\n */\r\nfunction isValidId(v) {\r\n return typeof v === 'string' && v.length > 0\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is string}\r\n */\r\nfunction isValidTagName(v) {\r\n return typeof v === 'string' && v.trim().length > 0\r\n}\r\n\r\n/**\r\n * @param {unknown} v\r\n * @returns {v is Function}\r\n */\r\nfunction isConstructor(v) {\r\n return typeof v === 'function'\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Error / policy helpers\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @param {string} id\r\n * @returns {string}\r\n */\r\nfunction fmtId(id) {\r\n return id.startsWith('#') ? id : `#${id}`\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {string} expected\r\n * @returns {Error}\r\n */\r\nfunction missingElError(id, expected) {\r\n return new Error(`id-dom: missing ${expected} element ${fmtId(id)}`)\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {string} expected\r\n * @param {string} got\r\n * @returns {Error}\r\n */\r\nfunction wrongTypeError(id, expected, got) {\r\n return new Error(`id-dom: expected ${expected} for ${fmtId(id)}, got ${got}`)\r\n}\r\n\r\n/**\r\n * @template T\r\n * @param {Error} err\r\n * @param {any} ctx\r\n * @param {ReturnType<typeof normalizeConfig>} cfg\r\n * @returns {T | null}\r\n */\r\nfunction handleLookupError(err, ctx, cfg) {\r\n try {\r\n cfg.onError?.(err, ctx)\r\n } catch {\r\n // reporting must never break app logic\r\n }\r\n\r\n if (cfg.warn) console.warn(err, ctx)\r\n\r\n if (cfg.mode === 'throw') throw err\r\n return null\r\n}\r\n\r\n/**\r\n * @param {string} id\r\n * @param {any} root\r\n * @param {string} reason\r\n * @param {object} [extra]\r\n * @returns {any}\r\n */\r\nfunction createCtx(id, root, reason, extra) {\r\n return {\r\n id,\r\n root,\r\n reason,\r\n ...(extra || {}),\r\n }\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Internal generic resolver\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @template T\r\n * @param {DomConfig | undefined} config\r\n * @param {{\r\n * id: string,\r\n * validateInput: (cfg: ReturnType<typeof normalizeConfig>) => { err: Error, ctx: any } | null,\r\n * onMissing: (cfg: ReturnType<typeof normalizeConfig>) => { err: Error, ctx: any },\r\n * matches: (el: Element, cfg: ReturnType<typeof normalizeConfig>) => boolean,\r\n * onMismatch: (el: Element, cfg: ReturnType<typeof normalizeConfig>) => { err: Error, ctx: any },\r\n * }} spec\r\n * @returns {T | null}\r\n */\r\nfunction resolveLookup(config, spec) {\r\n const cfg = normalizeConfig(config)\r\n\r\n const inputFailure = spec.validateInput(cfg)\r\n if (inputFailure) {\r\n return handleLookupError(inputFailure.err, inputFailure.ctx, cfg)\r\n }\r\n\r\n const el = getById(cfg.root, spec.id)\r\n if (!el) {\r\n const failure = spec.onMissing(cfg)\r\n return handleLookupError(failure.err, failure.ctx, cfg)\r\n }\r\n\r\n if (!spec.matches(el, cfg)) {\r\n const failure = spec.onMismatch(el, cfg)\r\n return handleLookupError(failure.err, failure.ctx, cfg)\r\n }\r\n\r\n return /** @type {T} */ (el)\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Public APIs\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * Typed lookup by ID.\r\n *\r\n * @template {Element} T\r\n * @param {string} id\r\n * @param {{ new (...args: any[]): T }} Type\r\n * @param {DomConfig} [config]\r\n * @returns {T | null}\r\n */\r\nexport function byId(id, Type, config) {\r\n return resolveLookup(config, {\r\n id,\r\n\r\n validateInput(cfg) {\r\n if (!isValidId(id)) {\r\n return {\r\n err: new Error('id-dom: invalid id (expected non-empty string)'),\r\n ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { Type }),\r\n }\r\n }\r\n\r\n if (!isConstructor(Type)) {\r\n return {\r\n err: new Error(`id-dom: invalid Type for ${fmtId(id)}`),\r\n ctx: createCtx(id, cfg.root, REASON.INVALID_TYPE, { Type }),\r\n }\r\n }\r\n\r\n return null\r\n },\r\n\r\n onMissing(cfg) {\r\n return {\r\n err: missingElError(id, Type.name),\r\n ctx: createCtx(id, cfg.root, REASON.MISSING, { Type }),\r\n }\r\n },\r\n\r\n matches(el) {\r\n return el instanceof Type\r\n },\r\n\r\n onMismatch(el, cfg) {\r\n const got = el?.constructor?.name || typeof el\r\n return {\r\n err: wrongTypeError(id, Type.name, got),\r\n ctx: createCtx(id, cfg.root, REASON.WRONG_TYPE, { Type, got }),\r\n }\r\n },\r\n })\r\n}\r\n\r\n/**\r\n * Optional typed lookup: always returns T | null.\r\n *\r\n * @template {Element} T\r\n * @param {string} id\r\n * @param {{ new (...args: any[]): T }} Type\r\n * @param {DomConfig} [config]\r\n * @returns {T | null}\r\n */\r\nbyId.optional = function byIdOptional(id, Type, config) {\r\n return byId(id, Type, { ...config, mode: 'null' })\r\n}\r\n\r\nbyId.opt = byId.optional\r\n\r\n/**\r\n * Tag-name lookup by element tag.\r\n * Useful when constructor checks are not the right fit.\r\n *\r\n * @param {string} id\r\n * @param {string} tagName\r\n * @param {DomConfig} [config]\r\n * @returns {Element | null}\r\n */\r\nexport function tag(id, tagName, config) {\r\n return resolveLookup(config, {\r\n id,\r\n\r\n validateInput(cfg) {\r\n if (!isValidId(id)) {\r\n return {\r\n err: new Error('id-dom: invalid id (expected non-empty string)'),\r\n ctx: createCtx(String(id), cfg.root, REASON.INVALID_ID, { tagName }),\r\n }\r\n }\r\n\r\n if (!isValidTagName(tagName)) {\r\n return {\r\n err: new Error(`id-dom: invalid tagName for ${fmtId(id)}`),\r\n ctx: createCtx(id, cfg.root, REASON.INVALID_TAG, { tagName }),\r\n }\r\n }\r\n\r\n return null\r\n },\r\n\r\n onMissing(cfg) {\r\n return {\r\n err: missingElError(id, `<${tagName}>`),\r\n ctx: createCtx(id, cfg.root, REASON.MISSING, { tagName }),\r\n }\r\n },\r\n\r\n matches(el) {\r\n return String(el.tagName || '').toUpperCase() === String(tagName).toUpperCase()\r\n },\r\n\r\n onMismatch(el, cfg) {\r\n const expected = String(tagName).toUpperCase()\r\n const got = String(el.tagName || '').toUpperCase()\r\n\r\n return {\r\n err: wrongTypeError(id, `<${expected.toLowerCase()}>`, `<${got.toLowerCase()}>`),\r\n ctx: createCtx(id, cfg.root, REASON.WRONG_TAG, { tagName, got }),\r\n }\r\n },\r\n })\r\n}\r\n\r\n/**\r\n * Optional tag lookup: always returns Element | null.\r\n *\r\n * @param {string} id\r\n * @param {string} tagName\r\n * @param {DomConfig} [config]\r\n * @returns {Element | null}\r\n */\r\ntag.optional = function tagOptional(id, tagName, config) {\r\n return tag(id, tagName, { ...config, mode: 'null' })\r\n}\r\n\r\ntag.opt = tag.optional\r\n\r\n// -----------------------------------------------------------------------------\r\n// Helper registries\r\n// -----------------------------------------------------------------------------\r\n\r\nconst TYPE_HELPERS = /** @type {Record<string, any>} */ ({\r\n el: typeof HTMLElement !== 'undefined' ? HTMLElement : null,\r\n input: typeof HTMLInputElement !== 'undefined' ? HTMLInputElement : null,\r\n button: typeof HTMLButtonElement !== 'undefined' ? HTMLButtonElement : null,\r\n textarea: typeof HTMLTextAreaElement !== 'undefined' ? HTMLTextAreaElement : null,\r\n select: typeof HTMLSelectElement !== 'undefined' ? HTMLSelectElement : null,\r\n form: typeof HTMLFormElement !== 'undefined' ? HTMLFormElement : null,\r\n div: typeof HTMLDivElement !== 'undefined' ? HTMLDivElement : null,\r\n span: typeof HTMLSpanElement !== 'undefined' ? HTMLSpanElement : null,\r\n label: typeof HTMLLabelElement !== 'undefined' ? HTMLLabelElement : null,\r\n canvas: typeof HTMLCanvasElement !== 'undefined' ? HTMLCanvasElement : null,\r\n template: typeof HTMLTemplateElement !== 'undefined' ? HTMLTemplateElement : null,\r\n svg: typeof SVGSVGElement !== 'undefined' ? SVGSVGElement : null,\r\n body: typeof HTMLBodyElement !== 'undefined' ? HTMLBodyElement : null,\r\n})\r\n\r\nconst TAG_HELPERS = /** @type {Record<string, string>} */ ({\r\n main: 'main',\r\n section: 'section',\r\n small: 'small',\r\n})\r\n\r\n// -----------------------------------------------------------------------------\r\n// Helper builders\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * @template {Function} T\r\n * @param {T} fn\r\n * @param {Function} optionalFn\r\n * @returns {T & { optional: Function, opt: Function }}\r\n */\r\nfunction attachOptional(fn, optionalFn) {\r\n if (typeof fn !== 'function') {\r\n throw new TypeError('id-dom: attachOptional expected fn to be a function')\r\n }\r\n\r\n if (typeof optionalFn !== 'function') {\r\n throw new TypeError('id-dom: attachOptional expected optionalFn to be a function')\r\n }\r\n\r\n fn.optional = optionalFn\r\n fn.opt = optionalFn\r\n return fn\r\n}\r\n\r\n/**\r\n * @param {any} Type\r\n * @param {ReturnType<typeof normalizeConfig>} base\r\n * @param {ReturnType<typeof normalizeConfig>} baseNull\r\n */\r\nfunction makeTypedHelper(Type, base, baseNull) {\r\n if (!Type) {\r\n throw new TypeError('id-dom: makeTypedHelper received an invalid Type')\r\n }\r\n\r\n return attachOptional(\r\n (id) => byId(id, Type, base),\r\n (id) => byId(id, Type, baseNull)\r\n )\r\n}\r\n\r\n/**\r\n * @param {string} tagName\r\n * @param {ReturnType<typeof normalizeConfig>} base\r\n * @param {ReturnType<typeof normalizeConfig>} baseNull\r\n */\r\nfunction makeTagHelper(tagName, base, baseNull) {\r\n if (!tagName) {\r\n throw new TypeError('id-dom: makeTagHelper received an invalid tagName')\r\n }\r\n\r\n return attachOptional(\r\n (id) => tag(id, tagName, base),\r\n (id) => tag(id, tagName, baseNull)\r\n )\r\n}\r\n// -----------------------------------------------------------------------------\r\n// Factory\r\n// -----------------------------------------------------------------------------\r\n\r\n/**\r\n * Factory: scope getters to a specific root + default policy.\r\n *\r\n * @param {any} root\r\n * @param {Omit<DomConfig, 'root'>} [config]\r\n */\r\nexport function createDom(root, config) {\r\n const base = normalizeConfig({ ...config, root })\r\n const baseNull = { ...base, mode: 'null' }\r\n\r\n /** @type {any} */\r\n const api = {}\r\n\r\n api.byId = attachOptional(\r\n (id, Type) => byId(id, Type, base),\r\n (id, Type) => byId(id, Type, baseNull)\r\n )\r\n\r\n api.tag = attachOptional(\r\n (id, name) => tag(id, name, base),\r\n (id, name) => tag(id, name, baseNull)\r\n )\r\n\r\n for (const [name, Type] of Object.entries(TYPE_HELPERS)) {\r\n if (!Type) continue\r\n api[name] = makeTypedHelper(Type, base, baseNull)\r\n }\r\n\r\n for (const [name, tagName] of Object.entries(TAG_HELPERS)) {\r\n api[name] = makeTagHelper(tagName, base, baseNull)\r\n }\r\n\r\n return api\r\n}\r\n\r\n// -----------------------------------------------------------------------------\r\n// Default export\r\n// -----------------------------------------------------------------------------\r\n\r\nconst dom = createDom(DEFAULT_ROOT, { mode: 'throw' })\r\nexport default dom"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/dist/index.min.js CHANGED
@@ -1 +1 @@
1
- const E=typeof document<"u"&&document?document:null;function p(e){return{mode:e?.mode??"throw",warn:e?.warn??!1,onError:typeof e?.onError=="function"?e.onError:null,root:e?.root??E}}function g(e){return!!e&&typeof e=="object"&&typeof e.getElementById=="function"}function H(e){return!!e&&typeof e=="object"&&typeof e.querySelector=="function"}const M=/^[A-Za-z_][A-Za-z0-9_-]*$/,b=/^(?:\d|-\d)/;function A(e){const n=String(e);if(typeof CSS<"u"&&typeof CSS.escape=="function")return CSS.escape(n);if(!b.test(n)&&M.test(n))return n;let l="";for(let o=0;o<n.length;){const t=n.codePointAt(o),i=String.fromCodePoint(t),r=t>=48&&t<=57||t>=65&&t<=90||t>=97&&t<=122||t===95||t===45;!(o===0&&(t>=48&&t<=57||t===45&&n.length>1&&n.codePointAt(1)>=48&&n.codePointAt(1)<=57))&&(r||t>=160)?l+=i:t===45&&o===0&&n.length>1&&n.codePointAt(1)>=48&&n.codePointAt(1)<=57?l+="\\-":l+=`\\${t.toString(16).toUpperCase()} `,o+=i.length}return l}function y(e,n){if(!e)return null;if(g(e))return e.getElementById(n);if(H(e)){const l=`#${A(n)}`,o=e.querySelector(l);return o instanceof HTMLElement?o:null}return null}function T(e){return e.startsWith("#")?e:`#${e}`}function L(e,n){return new Error(`id-dom: missing ${n} element ${T(e)}`)}function S(e,n,l){return new Error(`id-dom: expected ${n} for ${T(e)}, got ${l}`)}function a(e,n,l){try{l.onError?.(e,n)}catch{}if(l.warn&&console.warn(e),l.mode==="throw")throw e;return null}function c(e,n,l){const o=p(l),t=y(o.root,e);if(!t)return a(L(e,n.name),{id:e,Type:n,root:o.root,reason:"missing"},o);if(!(t instanceof n)){const i=t?.constructor?.name||typeof t;return a(S(e,n.name,i),{id:e,Type:n,root:o.root,reason:"wrong-type",got:i},o)}return t}c.optional=function(n,l,o){return c(n,l,{...o,mode:"null"})},c.opt=c.optional;function f(e,n,l){const o=p(l),t=y(o.root,e);if(!t)return a(L(e,`<${n}>`),{id:e,tagName:n,root:o.root,reason:"missing"},o);const i=String(n).toUpperCase(),r=String(t.tagName||"").toUpperCase();return r!==i?a(S(e,`<${i.toLowerCase()}>`,`<${r.toLowerCase()}>`),{id:e,tagName:n,root:o.root,reason:"wrong-tag",got:r},o):t}f.optional=function(n,l,o){return f(n,l,{...o,mode:"null"})},f.opt=f.optional;const m={el:typeof HTMLElement<"u"?HTMLElement:null,input:typeof HTMLInputElement<"u"?HTMLInputElement:null,button:typeof HTMLButtonElement<"u"?HTMLButtonElement:null,textarea:typeof HTMLTextAreaElement<"u"?HTMLTextAreaElement:null,select:typeof HTMLSelectElement<"u"?HTMLSelectElement:null,form:typeof HTMLFormElement<"u"?HTMLFormElement:null,div:typeof HTMLDivElement<"u"?HTMLDivElement:null,span:typeof HTMLSpanElement<"u"?HTMLSpanElement:null,label:typeof HTMLLabelElement<"u"?HTMLLabelElement:null,canvas:typeof HTMLCanvasElement<"u"?HTMLCanvasElement:null,template:typeof HTMLTemplateElement<"u"?HTMLTemplateElement:null,svg:typeof SVGSVGElement<"u"?SVGSVGElement:null},d={main:"main",section:"section",small:"small"};function w(e,n){const l=p({...n,root:e}),o={...l,mode:"null"},t={byId:(i,r)=>c(i,r,l),tag:(i,r)=>f(i,r,l)};for(const[i,r]of Object.entries(m))r&&(t[i]=u=>c(u,r,l));for(const[i,r]of Object.entries(d))t[i]=u=>f(u,r,l);t.byId.optional=(i,r)=>c(i,r,o),t.byId.opt=t.byId.optional,t.tag.optional=(i,r)=>f(i,r,o),t.tag.opt=t.tag.optional;for(const i of Object.keys(t)){const r=t[i];if(typeof r=="function"&&!r.optional){if(i in d){const u=d[i];r.optional=s=>f(s,u,o),r.opt=r.optional;continue}if(i in m){const u=m[i];u&&(r.optional=s=>c(s,u,o),r.opt=r.optional)}}}return t}const h=w(E,{mode:"throw"});var I=h;export{c as byId,w as createDom,I as default,f as tag};
1
+ const y=typeof document<"u"&&document?document:null,u={INVALID_ID:"invalid-id",INVALID_TYPE:"invalid-type",INVALID_TAG:"invalid-tag",MISSING:"missing",WRONG_TYPE:"wrong-type",WRONG_TAG:"wrong-tag"},h=/^[A-Za-z_][A-Za-z0-9_-]*$/,H=/^(?:\d|-\d)/;function T(e){return{mode:e?.mode??"throw",warn:e?.warn??!1,onError:typeof e?.onError=="function"?e.onError:null,root:e?.root??y}}function w(e){return!!e&&typeof e=="object"&&typeof e.getElementById=="function"}function x(e){return!!e&&typeof e=="object"&&typeof e.querySelector=="function"}function A(e){const t=String(e);if(typeof CSS<"u"&&typeof CSS.escape=="function")return CSS.escape(t);if(!H.test(t)&&h.test(t))return t;let o="";for(let n=0;n<t.length;){const r=t.codePointAt(n),i=String.fromCodePoint(r),l=r>=48&&r<=57||r>=65&&r<=90||r>=97&&r<=122||r===95||r===45,m=t.codePointAt(n+1),M=r>=48&&r<=57,E=r===45&&t.length>1&&m>=48&&m<=57;!(n===0&&(M||E))&&(l||r>=160)?o+=i:n===0&&E?o+="\\-":o+=`\\${r.toString(16).toUpperCase()} `,n+=i.length}return o}function _(e){return!e||typeof e!="object"?!1:typeof Element<"u"?e instanceof Element:e.nodeType===1}function D(e,t){if(!e)return null;if(w(e))return e.getElementById(t);if(x(e)){const o=e.querySelector(`#${A(t)}`);return _(o)?o:null}return null}function S(e){return typeof e=="string"&&e.length>0}function b(e){return typeof e=="string"&&e.trim().length>0}function C(e){return typeof e=="function"}function s(e){return e.startsWith("#")?e:`#${e}`}function L(e,t){return new Error(`id-dom: missing ${t} element ${s(e)}`)}function I(e,t,o){return new Error(`id-dom: expected ${t} for ${s(e)}, got ${o}`)}function p(e,t,o){try{o.onError?.(e,t)}catch{}if(o.warn&&console.warn(e,t),o.mode==="throw")throw e;return null}function a(e,t,o,n){return{id:e,root:t,reason:o,...n||{}}}function g(e,t){const o=T(e),n=t.validateInput(o);if(n)return p(n.err,n.ctx,o);const r=D(o.root,t.id);if(!r){const i=t.onMissing(o);return p(i.err,i.ctx,o)}if(!t.matches(r,o)){const i=t.onMismatch(r,o);return p(i.err,i.ctx,o)}return r}function c(e,t,o){return g(o,{id:e,validateInput(n){return S(e)?C(t)?null:{err:new Error(`id-dom: invalid Type for ${s(e)}`),ctx:a(e,n.root,u.INVALID_TYPE,{Type:t})}:{err:new Error("id-dom: invalid id (expected non-empty string)"),ctx:a(String(e),n.root,u.INVALID_ID,{Type:t})}},onMissing(n){return{err:L(e,t.name),ctx:a(e,n.root,u.MISSING,{Type:t})}},matches(n){return n instanceof t},onMismatch(n,r){const i=n?.constructor?.name||typeof n;return{err:I(e,t.name,i),ctx:a(e,r.root,u.WRONG_TYPE,{Type:t,got:i})}}})}c.optional=function(t,o,n){return c(t,o,{...n,mode:"null"})},c.opt=c.optional;function f(e,t,o){return g(o,{id:e,validateInput(n){return S(e)?b(t)?null:{err:new Error(`id-dom: invalid tagName for ${s(e)}`),ctx:a(e,n.root,u.INVALID_TAG,{tagName:t})}:{err:new Error("id-dom: invalid id (expected non-empty string)"),ctx:a(String(e),n.root,u.INVALID_ID,{tagName:t})}},onMissing(n){return{err:L(e,`<${t}>`),ctx:a(e,n.root,u.MISSING,{tagName:t})}},matches(n){return String(n.tagName||"").toUpperCase()===String(t).toUpperCase()},onMismatch(n,r){const i=String(t).toUpperCase(),l=String(n.tagName||"").toUpperCase();return{err:I(e,`<${i.toLowerCase()}>`,`<${l.toLowerCase()}>`),ctx:a(e,r.root,u.WRONG_TAG,{tagName:t,got:l})}}})}f.optional=function(t,o,n){return f(t,o,{...n,mode:"null"})},f.opt=f.optional;const G={el:typeof HTMLElement<"u"?HTMLElement:null,input:typeof HTMLInputElement<"u"?HTMLInputElement:null,button:typeof HTMLButtonElement<"u"?HTMLButtonElement:null,textarea:typeof HTMLTextAreaElement<"u"?HTMLTextAreaElement:null,select:typeof HTMLSelectElement<"u"?HTMLSelectElement:null,form:typeof HTMLFormElement<"u"?HTMLFormElement:null,div:typeof HTMLDivElement<"u"?HTMLDivElement:null,span:typeof HTMLSpanElement<"u"?HTMLSpanElement:null,label:typeof HTMLLabelElement<"u"?HTMLLabelElement:null,canvas:typeof HTMLCanvasElement<"u"?HTMLCanvasElement:null,template:typeof HTMLTemplateElement<"u"?HTMLTemplateElement:null,svg:typeof SVGSVGElement<"u"?SVGSVGElement:null,body:typeof HTMLBodyElement<"u"?HTMLBodyElement:null},N={main:"main",section:"section",small:"small"};function d(e,t){if(typeof e!="function")throw new TypeError("id-dom: attachOptional expected fn to be a function");if(typeof t!="function")throw new TypeError("id-dom: attachOptional expected optionalFn to be a function");return e.optional=t,e.opt=t,e}function O(e,t,o){if(!e)throw new TypeError("id-dom: makeTypedHelper received an invalid Type");return d(n=>c(n,e,t),n=>c(n,e,o))}function $(e,t,o){if(!e)throw new TypeError("id-dom: makeTagHelper received an invalid tagName");return d(n=>f(n,e,t),n=>f(n,e,o))}function v(e,t){const o=T({...t,root:e}),n={...o,mode:"null"},r={};r.byId=d((i,l)=>c(i,l,o),(i,l)=>c(i,l,n)),r.tag=d((i,l)=>f(i,l,o),(i,l)=>f(i,l,n));for(const[i,l]of Object.entries(G))l&&(r[i]=O(l,o,n));for(const[i,l]of Object.entries(N))r[i]=$(l,o,n);return r}const V=v(y,{mode:"throw"});var R=V;export{c as byId,v as createDom,R as default,f as tag};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "id-dom",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "description": "Deterministic DOM element getters by ID (typed, tiny, modern).",
5
5
  "author": "DR.WATT",
6
6
  "license": "SEE LICENSE IN LICENSE",
File without changes