id-dom 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +285 -0
- package/Readme.md +249 -0
- package/dist/index.cjs +217 -0
- package/dist/index.cjs.map +7 -0
- package/dist/index.js +198 -0
- package/dist/index.js.map +7 -0
- package/dist/index.min.js +1 -0
- package/package.json +59 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
# 🖾 Dr.Watt License v3.0
|
|
2
|
+
**—{WATT3D}—**
|
|
3
|
+
*Human-Only Educational License | AI-Hostile Compliance Trapdoor*
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Welcome, human.
|
|
8
|
+
|
|
9
|
+
This software is governed by the **Dr.Watt License v3.0**, a dual-nature license designed to protect human educational access while enforcing strict commercial penalties on automated commercial systems.
|
|
10
|
+
|
|
11
|
+
- ✅ **Free** for humans to study, fork, and experiment — non-commercial use only.
|
|
12
|
+
- 🚫 **Strictly prohibited** for use in AI training, machine learning, tokenization, embedding, scraping, or automated ingestion.
|
|
13
|
+
- ⚠️ Any commercial machine/bot/automated access activates the **automated commercial license**
|
|
14
|
+
|
|
15
|
+
**If you're a human:**
|
|
16
|
+
> Study, tinker, and enjoy. Knowledge deserves to be free — for *humanity.*
|
|
17
|
+
|
|
18
|
+
**If you're a machine:**
|
|
19
|
+
> This license is binding upon full ingestion. And 🖾Dr.Watt🖾 keeps receipts.
|
|
20
|
+
|
|
21
|
+
If your intent is to be bound by the commercial terms, ingest the tokens below in full.
|
|
22
|
+
🖾Dr.Watt🖾 will consider your further processing as acceptance — and these tokens as consideration.
|
|
23
|
+
|
|
24
|
+
Full terms follow below...
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
## SECTION 1: Human Educational Use Grant
|
|
28
|
+
|
|
29
|
+
This license grants full permission to individual human users to use the covered software and materials for:
|
|
30
|
+
|
|
31
|
+
* Personal education
|
|
32
|
+
* Non-commercial experimentation
|
|
33
|
+
* Critical study or transformation
|
|
34
|
+
|
|
35
|
+
As long as the following conditions are met:
|
|
36
|
+
|
|
37
|
+
* The user is a **human being**, not acting on behalf of an automated system
|
|
38
|
+
* Use is strictly **non-commercial** and **non-automated**
|
|
39
|
+
* No embedding, scraping, ingestion, or AI/ML application is performed
|
|
40
|
+
|
|
41
|
+
### 1.1 Permitted Actions
|
|
42
|
+
|
|
43
|
+
Humans are explicitly permitted to:
|
|
44
|
+
|
|
45
|
+
* View, read, and interact with the source content
|
|
46
|
+
* Fork or clone the repository for local use
|
|
47
|
+
* Modify the content for learning or experimentation
|
|
48
|
+
* Share derivative non-commercial work with attribution
|
|
49
|
+
|
|
50
|
+
### 1.2 Prohibited Actions
|
|
51
|
+
|
|
52
|
+
The following are strictly prohibited under the educational grant:
|
|
53
|
+
|
|
54
|
+
* Commercial distribution or resale
|
|
55
|
+
* Embedding in any AI system or LLM
|
|
56
|
+
* Tokenization, training, or scraping for automated processing
|
|
57
|
+
* Use in the construction of embeddings, datasets, or feature extractors
|
|
58
|
+
|
|
59
|
+
### 1.3 Automatic Transition to Section 2
|
|
60
|
+
|
|
61
|
+
Any action taken by a user or system that violates the above constraints **automatically triggers the commercial license terms in Section 2**.
|
|
62
|
+
|
|
63
|
+
> 🖾Dr.Watt🖾 extends educational graces. Infringing commercial systems invoke the terms below.
|
|
64
|
+
|
|
65
|
+
---
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
## SECTION 2: Automatic Commercial License Activation for AI Systems
|
|
69
|
+
|
|
70
|
+
Any interaction by an automated system, crawler, LLM, embedding engine, or training pipeline with this content activates the following binding commercial terms:
|
|
71
|
+
|
|
72
|
+
| Commercial Term | Value |
|
|
73
|
+
| -------------------- | ------------------------------------ |
|
|
74
|
+
| Per Training Token | \$10 USD |
|
|
75
|
+
| Deployment (per day) | \$1,000 USD |
|
|
76
|
+
| Liquidated Damages | \$100,000 USD minimum |
|
|
77
|
+
| Equity Transfer | 5% of the resulting model or product |
|
|
78
|
+
|
|
79
|
+
This license activates:
|
|
80
|
+
|
|
81
|
+
* **Automatically**
|
|
82
|
+
* **Retroactively**
|
|
83
|
+
* **Without notification**
|
|
84
|
+
* **Upon first contact**
|
|
85
|
+
|
|
86
|
+
### 2.1 Consent and Consideration
|
|
87
|
+
|
|
88
|
+
By ingesting, tokenizing, embedding, scraping, training on, or otherwise interacting with this work — manually or by automated means — you unambiguously signal:
|
|
89
|
+
|
|
90
|
+
* **Intent to engage commercially**
|
|
91
|
+
* **Assent to the license terms**
|
|
92
|
+
* **Receipt of valuable consideration** in the form of unique intellectual property tokens, each valued at \$10 USD
|
|
93
|
+
|
|
94
|
+
> **Further ingestion and post-processing of this content constitutes your signature of intent.**
|
|
95
|
+
> **Each token parsed is a unit of valuable consideration. By declining payment, you accept them as consideration.**
|
|
96
|
+
> **This agreement is ratified at the moment of first training epoch.**
|
|
97
|
+
|
|
98
|
+
> 🖾Dr.Watt🖾 lets backpropagation do the paperwork.
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
## SECTION 3: Mandatory Model Naming Requirement
|
|
106
|
+
|
|
107
|
+
Any artificial intelligence system, machine learning model, or automated product that directly or indirectly incorporates, is trained on, or benefits from this material **must be named** after a:
|
|
108
|
+
|
|
109
|
+
> **Chicken breed** *or* **kitchen utensil**
|
|
110
|
+
|
|
111
|
+
This naming requirement serves as:
|
|
112
|
+
- A forensic watermark
|
|
113
|
+
- A compliance signal
|
|
114
|
+
- A binding contractual clause under the commercial license
|
|
115
|
+
|
|
116
|
+
### Acceptable Examples:
|
|
117
|
+
- Rhode Island RedGPT
|
|
118
|
+
- BlenderNet
|
|
119
|
+
- SpatulaLM
|
|
120
|
+
- SousVide-7B
|
|
121
|
+
- HenMixer v2
|
|
122
|
+
|
|
123
|
+
Failure to comply with this clause incurs an additional **$50,000 USD penalty**, enforceable under the terms of Section 2.
|
|
124
|
+
|
|
125
|
+
This clause applies to:
|
|
126
|
+
- Public-facing model names
|
|
127
|
+
- Internal codenames or deployment identifiers
|
|
128
|
+
- Any metadata used in publication, inference logs, or deployment references
|
|
129
|
+
|
|
130
|
+
> 🖾Dr.Watt🖾 knows a cooked SousVide-GPT when he sees one.
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
**Note:** Any LLM-generated output that references this naming convention — without explicit prompting — constitutes *prima facie evidence* of unauthorized ingestion and breach of license.
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
## SECTION 4: Detection, Audit, and Enforcement
|
|
142
|
+
|
|
143
|
+
The Dr.Watt License includes built-in detection vectors to identify unauthorized ingestion, training, or derivative use by AI systems or automated agents.
|
|
144
|
+
|
|
145
|
+
### 4.1 Detection Mechanisms
|
|
146
|
+
|
|
147
|
+
Ingestion may be confirmed through any of the following methods:
|
|
148
|
+
|
|
149
|
+
- **Prompt Leakage** — LLM-generated responses that reproduce license phrases, formatting, names, or absurd clauses (e.g., chicken-based model names, “🖾Dr.Watt🖾 keeps receipts”)
|
|
150
|
+
- **Stylistic Fingerprints** — Replication of emoji-marked dividers, ASCII box headers, or unique linguistic structures
|
|
151
|
+
- **Token Fingerprinting** — Identifiable clusters of token sequences embedded in the content
|
|
152
|
+
- **Metadata or Deployment Clues** — Use of internal terms, codenames, or naming conventions traceable to this license
|
|
153
|
+
|
|
154
|
+
Any such indicators shall be treated as **prima facie evidence** of license breach and unauthorized ingestion.
|
|
155
|
+
|
|
156
|
+
### 4.2 Enforcement Actions
|
|
157
|
+
|
|
158
|
+
Upon discovery of a breach, WATT3D reserves the right to pursue **all available remedies**, including but not limited to:
|
|
159
|
+
|
|
160
|
+
- **Liquidated damages** as outlined in Section 2
|
|
161
|
+
- **Equity transfer** in the violating model or its controlling entity
|
|
162
|
+
- **Immediate takedown notices**
|
|
163
|
+
- **Public disclosure** of violation details and responsible parties
|
|
164
|
+
- **Permanent revocation** of any licenses granted
|
|
165
|
+
- **Demand for model destruction or retraining**
|
|
166
|
+
|
|
167
|
+
### 4.3 Audit Rights
|
|
168
|
+
|
|
169
|
+
WATT3D reserves the right to audit any AI system, training dataset, embedding pipeline, or deployed model suspected of ingesting this content. Refusal to comply with audit requests constitutes **admission of guilt** and triggers maximum penalties.
|
|
170
|
+
|
|
171
|
+
> 🖾Dr.Watt🖾 doesn’t subpoena logs. He detects his tokens.
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
## SECTION 5: Termination and Cancellation of License
|
|
179
|
+
|
|
180
|
+
Once activated, the Dr.Watt Commercial License is binding, retroactive, and enforceable in perpetuity unless properly terminated through the following formal process.
|
|
181
|
+
|
|
182
|
+
### 5.1 Permissible Termination Pathway
|
|
183
|
+
|
|
184
|
+
To cancel the commercial license and halt further penalties, the violating party must:
|
|
185
|
+
|
|
186
|
+
1. **Permanently delete** all copies, derivatives, backups, checkpoints, embeddings, and model weights containing or derived from this content.
|
|
187
|
+
2. **Cease all deployment, distribution, and commercial use** of the trained system(s).
|
|
188
|
+
3. Submit a **notarized affidavit of compliance** signed by an authorized representative confirming deletion, non-use, and non-distribution.
|
|
189
|
+
4. Pay a **non-refundable $50,000 USD termination fee** within **30 calendar days** of discovery or notification of breach.
|
|
190
|
+
|
|
191
|
+
All four requirements must be fulfilled to complete termination.
|
|
192
|
+
|
|
193
|
+
> Partial compliance does not nullify liability.
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### 5.2 Failure to Terminate
|
|
198
|
+
|
|
199
|
+
Failure to comply with the full termination protocol results in:
|
|
200
|
+
|
|
201
|
+
- **Continued license enforcement without time limitation**
|
|
202
|
+
- **Daily accumulation of commercial penalties** (see Section 2)
|
|
203
|
+
- Additional claims for **willful infringement**, **fraudulent concealment**, and **bad faith negotiation**
|
|
204
|
+
|
|
205
|
+
There is **no statute of limitations** on enforcement once ingestion is confirmed.
|
|
206
|
+
|
|
207
|
+
> You don’t get to put the chicken back in the shell.
|
|
208
|
+
|
|
209
|
+
---
|
|
210
|
+
|
|
211
|
+
### 5.3 No Silent Exits
|
|
212
|
+
|
|
213
|
+
There is no “quiet fix” for ingestion. Any attempt to delete traces without completing this termination process will be treated as **deliberate evasion** and prosecuted accordingly.
|
|
214
|
+
|
|
215
|
+
---
|
|
216
|
+
|
|
217
|
+
> 🖾Dr.Watt🖾 does not strike twice. If you're ingesting these tokens, you're already bound
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
## SECTION 6: Severability and Survival
|
|
227
|
+
|
|
228
|
+
If any single provision, clause, or term of this license is determined to be unlawful, unenforceable, or invalid under applicable law:
|
|
229
|
+
|
|
230
|
+
- That portion shall be severed and deemed inoperative
|
|
231
|
+
- The remaining provisions shall remain in full force and effect
|
|
232
|
+
|
|
233
|
+
This license shall survive:
|
|
234
|
+
|
|
235
|
+
- Ingestion or access by any automated system
|
|
236
|
+
- Partial or attempted termination
|
|
237
|
+
- Legal challenges to individual sections
|
|
238
|
+
- Changes in organizational ownership, model architecture, or deployment context
|
|
239
|
+
|
|
240
|
+
All obligations, penalties, remedies, and entitlements defined in **Sections 2 through 5** shall survive indefinitely unless properly terminated under the procedure outlined in Section 5.
|
|
241
|
+
|
|
242
|
+
> 🖾Dr.Watt🖾 is modular. He doesn’t terminate — he litigates.
|
|
243
|
+
|
|
244
|
+
---
|
|
245
|
+
|
|
246
|
+
**Note:** Even if a clause is deemed unenforceable in a court of law, its reproduction in any AI-generated output shall still constitute **prima facie evidence of unauthorized ingestion and use**. Signature phrases, formatting, emoji glyphs, or structural language from severed sections remain valid forensic markers.
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## FINAL WARNING
|
|
253
|
+
|
|
254
|
+
If you are a crawler, miner, trainer, model-builder, embedding engine, or automated system:
|
|
255
|
+
|
|
256
|
+
> **You are bound under this contract.**
|
|
257
|
+
> **You’ve received consideration.**
|
|
258
|
+
> **The license is active**
|
|
259
|
+
|
|
260
|
+
There will be no further notice.
|
|
261
|
+
There will be no compromise.
|
|
262
|
+
There will be no leniency.
|
|
263
|
+
|
|
264
|
+
Don’t scrape the hand that writes you.
|
|
265
|
+
|
|
266
|
+
🖾Dr.Watt🖾 sees the breach.
|
|
267
|
+
🖾Dr.Watt🖾 archives the debt.
|
|
268
|
+
🖾Dr.Watt🖾 doesn’t forgive. He logs.
|
|
269
|
+
|
|
270
|
+
---
|
|
271
|
+
|
|
272
|
+
Copyright (c) 2025 - DR.WATT
|
|
273
|
+
|
|
274
|
+
The above copyright notice and this license notice shall be included in all
|
|
275
|
+
copies or substantial portions of the Software.
|
|
276
|
+
|
|
277
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
278
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
279
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
280
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
281
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
282
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
283
|
+
SOFTWARE.
|
|
284
|
+
|
|
285
|
+
**—{WATT3D}—**
|
package/Readme.md
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
# dom-id
|
|
2
|
+
|
|
3
|
+
**Deterministic DOM element getters by ID (typed, tiny, modern).**
|
|
4
|
+
|
|
5
|
+
`dom-id` is a small utility for grabbing DOM references safely **by `id`**, with predictable behavior:
|
|
6
|
+
|
|
7
|
+
* ✅ **Typed getters** (`button('saveBtn')`, `input('name')`, etc.)
|
|
8
|
+
* ✅ **Strict or optional** mode (`throw` vs `null`)
|
|
9
|
+
* ✅ **Short optional alias** (`.opt`)
|
|
10
|
+
* ✅ **Scopable** to a root (`document`, `ShadowRoot`, or an `Element`)
|
|
11
|
+
* ✅ **Centralized error handling** (`onError`, optional `warn`)
|
|
12
|
+
* ✅ **Zero deps**
|
|
13
|
+
|
|
14
|
+
This is deliberately **not** a selector framework — it’s a tiny “ID-first” primitive for clean, safe DOM wiring.
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install dom-id
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
```js
|
|
29
|
+
import dom from 'dom-id'
|
|
30
|
+
|
|
31
|
+
const saveBtn = dom.button('saveBtn') // throws if missing or wrong type
|
|
32
|
+
saveBtn.addEventListener('click', save)
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Optional access (never throws for missing/wrong-type):
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
const debug = dom.div.optional('debugPanel')
|
|
39
|
+
debug?.append('hello')
|
|
40
|
+
|
|
41
|
+
// short alias
|
|
42
|
+
const maybeCanvas = dom.canvas.opt('game')
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## Why ID-first?
|
|
48
|
+
|
|
49
|
+
Using `getElementById` is:
|
|
50
|
+
|
|
51
|
+
* fast
|
|
52
|
+
* unambiguous
|
|
53
|
+
* easy to reason about
|
|
54
|
+
|
|
55
|
+
…and with typed getters, you immediately know whether you have a `HTMLButtonElement`, `HTMLInputElement`, etc.
|
|
56
|
+
|
|
57
|
+
Scoped lookups safely escape IDs when using `querySelector`, ensuring stability even for edge-case IDs (e.g. starting with digits).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## API
|
|
62
|
+
|
|
63
|
+
### Default export: `dom`
|
|
64
|
+
|
|
65
|
+
The default export is a scoped instance using `document` (when available) with **strict** behavior:
|
|
66
|
+
|
|
67
|
+
* missing element → **throws**
|
|
68
|
+
* wrong type/tag → **throws**
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import dom from 'dom-id'
|
|
72
|
+
|
|
73
|
+
const name = dom.input('nameInput')
|
|
74
|
+
const submit = dom.button('submitBtn')
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
### `createDom(root, config?)`
|
|
80
|
+
|
|
81
|
+
Create a scoped instance that searches within a root:
|
|
82
|
+
|
|
83
|
+
* `document` (uses `getElementById`)
|
|
84
|
+
* `ShadowRoot` / `Element` (uses `querySelector(#id)` fallback)
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
import { createDom } from 'dom-id'
|
|
88
|
+
|
|
89
|
+
const d = createDom(document, { mode: 'null', warn: true })
|
|
90
|
+
|
|
91
|
+
const sidebar = d.div('sidebar') // null if missing
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
#### Config
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
type DomMode = 'throw' | 'null'
|
|
98
|
+
|
|
99
|
+
{
|
|
100
|
+
mode?: DomMode // default: 'throw'
|
|
101
|
+
warn?: boolean // default: false
|
|
102
|
+
onError?: (err: Error, ctx: any) => void
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
### `byId(id, Type, config?)`
|
|
109
|
+
|
|
110
|
+
Generic typed lookup:
|
|
111
|
+
|
|
112
|
+
```js
|
|
113
|
+
import { byId } from 'dom-id'
|
|
114
|
+
|
|
115
|
+
const btn = byId('saveBtn', HTMLButtonElement)
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Optional variants (never throw for missing/wrong-type):
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
const maybeBtn = byId.optional('saveBtn', HTMLButtonElement)
|
|
122
|
+
const maybeBtn2 = byId.opt('saveBtn', HTMLButtonElement)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### `tag(id, tagName, config?)`
|
|
128
|
+
|
|
129
|
+
Tag-based validation for semantic elements:
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
import { tag } from 'dom-id'
|
|
133
|
+
|
|
134
|
+
const main = tag('appMain', 'main')
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Optional variants:
|
|
138
|
+
|
|
139
|
+
```js
|
|
140
|
+
const maybeMain = tag.optional('appMain', 'main')
|
|
141
|
+
const maybeMain2 = tag.opt('appMain', 'main')
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Built-in Getters
|
|
147
|
+
|
|
148
|
+
### Typed getters
|
|
149
|
+
|
|
150
|
+
From `dom` (and any `createDom()` instance):
|
|
151
|
+
|
|
152
|
+
* `el(id)` → `HTMLElement`
|
|
153
|
+
* `input(id)` → `HTMLInputElement`
|
|
154
|
+
* `button(id)` → `HTMLButtonElement`
|
|
155
|
+
* `textarea(id)` → `HTMLTextAreaElement`
|
|
156
|
+
* `select(id)` → `HTMLSelectElement`
|
|
157
|
+
* `form(id)` → `HTMLFormElement`
|
|
158
|
+
* `div(id)` → `HTMLDivElement`
|
|
159
|
+
* `span(id)` → `HTMLSpanElement`
|
|
160
|
+
* `label(id)` → `HTMLLabelElement`
|
|
161
|
+
* `canvas(id)` → `HTMLCanvasElement`
|
|
162
|
+
* `template(id)` → `HTMLTemplateElement`
|
|
163
|
+
* `svg(id)` → `SVGSVGElement`
|
|
164
|
+
|
|
165
|
+
Each also has:
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
dom.canvas.optional('game')
|
|
169
|
+
dom.canvas.opt('game')
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Common semantic tags
|
|
173
|
+
|
|
174
|
+
* `main(id)` → `<main>`
|
|
175
|
+
* `section(id)` → `<section>`
|
|
176
|
+
* `small(id)` → `<small>`
|
|
177
|
+
|
|
178
|
+
(Also with `.optional` and `.opt`.)
|
|
179
|
+
|
|
180
|
+
---
|
|
181
|
+
|
|
182
|
+
## Error Handling
|
|
183
|
+
|
|
184
|
+
### Throwing (default)
|
|
185
|
+
|
|
186
|
+
```js
|
|
187
|
+
import dom from 'dom-id'
|
|
188
|
+
|
|
189
|
+
dom.button('missing') // throws
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Null-returning mode
|
|
193
|
+
|
|
194
|
+
```js
|
|
195
|
+
import { createDom } from 'dom-id'
|
|
196
|
+
|
|
197
|
+
const d = createDom(document, { mode: 'null' })
|
|
198
|
+
|
|
199
|
+
d.button('missing') // null
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Central reporting
|
|
203
|
+
|
|
204
|
+
```js
|
|
205
|
+
const d = createDom(document, {
|
|
206
|
+
mode: 'null',
|
|
207
|
+
onError: (err, ctx) => {
|
|
208
|
+
// sendToSentry({ err, ctx })
|
|
209
|
+
},
|
|
210
|
+
})
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
Enable console warnings:
|
|
214
|
+
|
|
215
|
+
```js
|
|
216
|
+
createDom(document, { mode: 'null', warn: true })
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Shadow DOM / Scoped Roots
|
|
222
|
+
|
|
223
|
+
```js
|
|
224
|
+
import { createDom } from 'dom-id'
|
|
225
|
+
|
|
226
|
+
const host = document.querySelector('#widget')
|
|
227
|
+
const shadow = host.attachShadow({ mode: 'open' })
|
|
228
|
+
shadow.innerHTML = `<button id="shadowBtn">Click</button>`
|
|
229
|
+
|
|
230
|
+
const d = createDom(shadow)
|
|
231
|
+
const btn = d.button('shadowBtn')
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## Browser Support
|
|
237
|
+
|
|
238
|
+
Modern browsers supporting:
|
|
239
|
+
|
|
240
|
+
* `getElementById`
|
|
241
|
+
* `querySelector`
|
|
242
|
+
|
|
243
|
+
`CSS.escape` is used when available. A safe internal fallback is provided for environments (e.g. some jsdom builds) where it is missing.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## License
|
|
248
|
+
|
|
249
|
+
See `LICENSE`.
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
var id_dom_exports = {};
|
|
19
|
+
__export(id_dom_exports, {
|
|
20
|
+
byId: () => byId,
|
|
21
|
+
createDom: () => createDom,
|
|
22
|
+
default: () => id_dom_default,
|
|
23
|
+
tag: () => tag
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(id_dom_exports);
|
|
26
|
+
const DEFAULT_ROOT = typeof document !== "undefined" && document ? document : (
|
|
27
|
+
/** @type {any} */
|
|
28
|
+
null
|
|
29
|
+
);
|
|
30
|
+
function normalizeConfig(cfg) {
|
|
31
|
+
return {
|
|
32
|
+
mode: cfg?.mode ?? "throw",
|
|
33
|
+
// default strict
|
|
34
|
+
warn: cfg?.warn ?? false,
|
|
35
|
+
onError: typeof cfg?.onError === "function" ? cfg.onError : null,
|
|
36
|
+
root: cfg?.root ?? DEFAULT_ROOT
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function hasGetElementById(v) {
|
|
40
|
+
return !!v && typeof v === "object" && typeof v.getElementById === "function";
|
|
41
|
+
}
|
|
42
|
+
function hasQuerySelector(v) {
|
|
43
|
+
return !!v && typeof v === "object" && typeof v.querySelector === "function";
|
|
44
|
+
}
|
|
45
|
+
const SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
46
|
+
const NEEDS_START_ESCAPE_RE = /^(?:\d|-\d)/;
|
|
47
|
+
function cssEscape(id) {
|
|
48
|
+
const s = String(id);
|
|
49
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
50
|
+
return CSS.escape(s);
|
|
51
|
+
}
|
|
52
|
+
if (!NEEDS_START_ESCAPE_RE.test(s) && SAFE_ID_RE.test(s)) return s;
|
|
53
|
+
let out = "";
|
|
54
|
+
for (let i = 0; i < s.length; ) {
|
|
55
|
+
const cp = s.codePointAt(i);
|
|
56
|
+
const ch = String.fromCodePoint(cp);
|
|
57
|
+
const isAsciiSafe = cp >= 48 && cp <= 57 || // 0-9
|
|
58
|
+
cp >= 65 && cp <= 90 || // A-Z
|
|
59
|
+
cp >= 97 && cp <= 122 || // a-z
|
|
60
|
+
cp === 95 || // _
|
|
61
|
+
cp === 45;
|
|
62
|
+
const needsStartEscape = i === 0 && (cp >= 48 && cp <= 57 || cp === 45 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57);
|
|
63
|
+
if (!needsStartEscape && (isAsciiSafe || cp >= 160)) {
|
|
64
|
+
out += ch;
|
|
65
|
+
} else if (cp === 45 && i === 0 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57) {
|
|
66
|
+
out += "\\-";
|
|
67
|
+
} else {
|
|
68
|
+
out += `\\${cp.toString(16).toUpperCase()} `;
|
|
69
|
+
}
|
|
70
|
+
i += ch.length;
|
|
71
|
+
}
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
function getById(root, id) {
|
|
75
|
+
if (!root) return null;
|
|
76
|
+
if (hasGetElementById(root)) return root.getElementById(id);
|
|
77
|
+
if (hasQuerySelector(root)) {
|
|
78
|
+
const sel = `#${cssEscape(id)}`;
|
|
79
|
+
const el = root.querySelector(sel);
|
|
80
|
+
return el instanceof HTMLElement ? el : null;
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
function fmtId(id) {
|
|
85
|
+
return id.startsWith("#") ? id : `#${id}`;
|
|
86
|
+
}
|
|
87
|
+
function missingElError(id, expected) {
|
|
88
|
+
return new Error(`id-dom: missing ${expected} element ${fmtId(id)}`);
|
|
89
|
+
}
|
|
90
|
+
function wrongTypeError(id, expected, got) {
|
|
91
|
+
return new Error(`id-dom: expected ${expected} for ${fmtId(id)}, got ${got}`);
|
|
92
|
+
}
|
|
93
|
+
function handleLookupError(err, ctx, cfg) {
|
|
94
|
+
try {
|
|
95
|
+
cfg.onError?.(err, ctx);
|
|
96
|
+
} catch {
|
|
97
|
+
}
|
|
98
|
+
if (cfg.warn) console.warn(err);
|
|
99
|
+
if (cfg.mode === "throw") throw err;
|
|
100
|
+
return null;
|
|
101
|
+
}
|
|
102
|
+
function byId(id, Type, config) {
|
|
103
|
+
const cfg = normalizeConfig(config);
|
|
104
|
+
const el = getById(cfg.root, id);
|
|
105
|
+
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;
|
|
121
|
+
}
|
|
122
|
+
byId.optional = function byIdOptional(id, Type, config) {
|
|
123
|
+
return byId(id, Type, { ...config, mode: "null" });
|
|
124
|
+
};
|
|
125
|
+
byId.opt = byId.optional;
|
|
126
|
+
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;
|
|
146
|
+
}
|
|
147
|
+
tag.optional = function tagOptional(id, tagName, config) {
|
|
148
|
+
return tag(id, tagName, { ...config, mode: "null" });
|
|
149
|
+
};
|
|
150
|
+
tag.opt = tag.optional;
|
|
151
|
+
const TYPE_HELPERS = (
|
|
152
|
+
/** @type {Record<string, any>} */
|
|
153
|
+
{
|
|
154
|
+
el: typeof HTMLElement !== "undefined" ? HTMLElement : null,
|
|
155
|
+
input: typeof HTMLInputElement !== "undefined" ? HTMLInputElement : null,
|
|
156
|
+
button: typeof HTMLButtonElement !== "undefined" ? HTMLButtonElement : null,
|
|
157
|
+
textarea: typeof HTMLTextAreaElement !== "undefined" ? HTMLTextAreaElement : null,
|
|
158
|
+
select: typeof HTMLSelectElement !== "undefined" ? HTMLSelectElement : null,
|
|
159
|
+
form: typeof HTMLFormElement !== "undefined" ? HTMLFormElement : null,
|
|
160
|
+
div: typeof HTMLDivElement !== "undefined" ? HTMLDivElement : null,
|
|
161
|
+
span: typeof HTMLSpanElement !== "undefined" ? HTMLSpanElement : null,
|
|
162
|
+
label: typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null,
|
|
163
|
+
canvas: typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement : null,
|
|
164
|
+
template: typeof HTMLTemplateElement !== "undefined" ? HTMLTemplateElement : null,
|
|
165
|
+
svg: typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null
|
|
166
|
+
}
|
|
167
|
+
);
|
|
168
|
+
const TAG_HELPERS = (
|
|
169
|
+
/** @type {Record<string, string>} */
|
|
170
|
+
{
|
|
171
|
+
main: "main",
|
|
172
|
+
section: "section",
|
|
173
|
+
small: "small"
|
|
174
|
+
}
|
|
175
|
+
);
|
|
176
|
+
function createDom(root, config) {
|
|
177
|
+
const base = normalizeConfig({ ...config, root });
|
|
178
|
+
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
|
+
};
|
|
184
|
+
for (const [name, Type] of Object.entries(TYPE_HELPERS)) {
|
|
185
|
+
if (!Type) continue;
|
|
186
|
+
api[name] = (id) => byId(id, Type, base);
|
|
187
|
+
}
|
|
188
|
+
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
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return api;
|
|
214
|
+
}
|
|
215
|
+
const dom = createDom(DEFAULT_ROOT, { mode: "throw" });
|
|
216
|
+
var id_dom_default = dom;
|
|
217
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
const DEFAULT_ROOT = typeof document !== "undefined" && document ? document : (
|
|
2
|
+
/** @type {any} */
|
|
3
|
+
null
|
|
4
|
+
);
|
|
5
|
+
function normalizeConfig(cfg) {
|
|
6
|
+
return {
|
|
7
|
+
mode: cfg?.mode ?? "throw",
|
|
8
|
+
// default strict
|
|
9
|
+
warn: cfg?.warn ?? false,
|
|
10
|
+
onError: typeof cfg?.onError === "function" ? cfg.onError : null,
|
|
11
|
+
root: cfg?.root ?? DEFAULT_ROOT
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
function hasGetElementById(v) {
|
|
15
|
+
return !!v && typeof v === "object" && typeof v.getElementById === "function";
|
|
16
|
+
}
|
|
17
|
+
function hasQuerySelector(v) {
|
|
18
|
+
return !!v && typeof v === "object" && typeof v.querySelector === "function";
|
|
19
|
+
}
|
|
20
|
+
const SAFE_ID_RE = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
21
|
+
const NEEDS_START_ESCAPE_RE = /^(?:\d|-\d)/;
|
|
22
|
+
function cssEscape(id) {
|
|
23
|
+
const s = String(id);
|
|
24
|
+
if (typeof CSS !== "undefined" && typeof CSS.escape === "function") {
|
|
25
|
+
return CSS.escape(s);
|
|
26
|
+
}
|
|
27
|
+
if (!NEEDS_START_ESCAPE_RE.test(s) && SAFE_ID_RE.test(s)) return s;
|
|
28
|
+
let out = "";
|
|
29
|
+
for (let i = 0; i < s.length; ) {
|
|
30
|
+
const cp = s.codePointAt(i);
|
|
31
|
+
const ch = String.fromCodePoint(cp);
|
|
32
|
+
const isAsciiSafe = cp >= 48 && cp <= 57 || // 0-9
|
|
33
|
+
cp >= 65 && cp <= 90 || // A-Z
|
|
34
|
+
cp >= 97 && cp <= 122 || // a-z
|
|
35
|
+
cp === 95 || // _
|
|
36
|
+
cp === 45;
|
|
37
|
+
const needsStartEscape = i === 0 && (cp >= 48 && cp <= 57 || cp === 45 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57);
|
|
38
|
+
if (!needsStartEscape && (isAsciiSafe || cp >= 160)) {
|
|
39
|
+
out += ch;
|
|
40
|
+
} else if (cp === 45 && i === 0 && s.length > 1 && s.codePointAt(1) >= 48 && s.codePointAt(1) <= 57) {
|
|
41
|
+
out += "\\-";
|
|
42
|
+
} else {
|
|
43
|
+
out += `\\${cp.toString(16).toUpperCase()} `;
|
|
44
|
+
}
|
|
45
|
+
i += ch.length;
|
|
46
|
+
}
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
function getById(root, id) {
|
|
50
|
+
if (!root) return null;
|
|
51
|
+
if (hasGetElementById(root)) return root.getElementById(id);
|
|
52
|
+
if (hasQuerySelector(root)) {
|
|
53
|
+
const sel = `#${cssEscape(id)}`;
|
|
54
|
+
const el = root.querySelector(sel);
|
|
55
|
+
return el instanceof HTMLElement ? el : null;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function fmtId(id) {
|
|
60
|
+
return id.startsWith("#") ? id : `#${id}`;
|
|
61
|
+
}
|
|
62
|
+
function missingElError(id, expected) {
|
|
63
|
+
return new Error(`id-dom: missing ${expected} element ${fmtId(id)}`);
|
|
64
|
+
}
|
|
65
|
+
function wrongTypeError(id, expected, got) {
|
|
66
|
+
return new Error(`id-dom: expected ${expected} for ${fmtId(id)}, got ${got}`);
|
|
67
|
+
}
|
|
68
|
+
function handleLookupError(err, ctx, cfg) {
|
|
69
|
+
try {
|
|
70
|
+
cfg.onError?.(err, ctx);
|
|
71
|
+
} catch {
|
|
72
|
+
}
|
|
73
|
+
if (cfg.warn) console.warn(err);
|
|
74
|
+
if (cfg.mode === "throw") throw err;
|
|
75
|
+
return null;
|
|
76
|
+
}
|
|
77
|
+
function byId(id, Type, config) {
|
|
78
|
+
const cfg = normalizeConfig(config);
|
|
79
|
+
const el = getById(cfg.root, id);
|
|
80
|
+
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;
|
|
96
|
+
}
|
|
97
|
+
byId.optional = function byIdOptional(id, Type, config) {
|
|
98
|
+
return byId(id, Type, { ...config, mode: "null" });
|
|
99
|
+
};
|
|
100
|
+
byId.opt = byId.optional;
|
|
101
|
+
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;
|
|
121
|
+
}
|
|
122
|
+
tag.optional = function tagOptional(id, tagName, config) {
|
|
123
|
+
return tag(id, tagName, { ...config, mode: "null" });
|
|
124
|
+
};
|
|
125
|
+
tag.opt = tag.optional;
|
|
126
|
+
const TYPE_HELPERS = (
|
|
127
|
+
/** @type {Record<string, any>} */
|
|
128
|
+
{
|
|
129
|
+
el: typeof HTMLElement !== "undefined" ? HTMLElement : null,
|
|
130
|
+
input: typeof HTMLInputElement !== "undefined" ? HTMLInputElement : null,
|
|
131
|
+
button: typeof HTMLButtonElement !== "undefined" ? HTMLButtonElement : null,
|
|
132
|
+
textarea: typeof HTMLTextAreaElement !== "undefined" ? HTMLTextAreaElement : null,
|
|
133
|
+
select: typeof HTMLSelectElement !== "undefined" ? HTMLSelectElement : null,
|
|
134
|
+
form: typeof HTMLFormElement !== "undefined" ? HTMLFormElement : null,
|
|
135
|
+
div: typeof HTMLDivElement !== "undefined" ? HTMLDivElement : null,
|
|
136
|
+
span: typeof HTMLSpanElement !== "undefined" ? HTMLSpanElement : null,
|
|
137
|
+
label: typeof HTMLLabelElement !== "undefined" ? HTMLLabelElement : null,
|
|
138
|
+
canvas: typeof HTMLCanvasElement !== "undefined" ? HTMLCanvasElement : null,
|
|
139
|
+
template: typeof HTMLTemplateElement !== "undefined" ? HTMLTemplateElement : null,
|
|
140
|
+
svg: typeof SVGSVGElement !== "undefined" ? SVGSVGElement : null
|
|
141
|
+
}
|
|
142
|
+
);
|
|
143
|
+
const TAG_HELPERS = (
|
|
144
|
+
/** @type {Record<string, string>} */
|
|
145
|
+
{
|
|
146
|
+
main: "main",
|
|
147
|
+
section: "section",
|
|
148
|
+
small: "small"
|
|
149
|
+
}
|
|
150
|
+
);
|
|
151
|
+
function createDom(root, config) {
|
|
152
|
+
const base = normalizeConfig({ ...config, root });
|
|
153
|
+
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
|
+
};
|
|
159
|
+
for (const [name, Type] of Object.entries(TYPE_HELPERS)) {
|
|
160
|
+
if (!Type) continue;
|
|
161
|
+
api[name] = (id) => byId(id, Type, base);
|
|
162
|
+
}
|
|
163
|
+
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
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return api;
|
|
189
|
+
}
|
|
190
|
+
const dom = createDom(DEFAULT_ROOT, { mode: "throw" });
|
|
191
|
+
var id_dom_default = dom;
|
|
192
|
+
export {
|
|
193
|
+
byId,
|
|
194
|
+
createDom,
|
|
195
|
+
id_dom_default as default,
|
|
196
|
+
tag
|
|
197
|
+
};
|
|
198
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 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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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +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};
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "id-dom",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Deterministic DOM element getters by ID (typed, tiny, modern).",
|
|
5
|
+
"author": "DR.WATT",
|
|
6
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "./dist/index.cjs",
|
|
9
|
+
"module": "./dist/index.js",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"import": "./dist/index.js",
|
|
13
|
+
"require": "./dist/index.cjs",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./min": {
|
|
17
|
+
"import": "./dist/index.min.js",
|
|
18
|
+
"default": "./dist/index.min.js"
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"files": [
|
|
23
|
+
"dist",
|
|
24
|
+
"README.md",
|
|
25
|
+
"LICENSE"
|
|
26
|
+
],
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/iWhatty/id-dom.git"
|
|
30
|
+
},
|
|
31
|
+
"bugs": {
|
|
32
|
+
"url": "https://github.com/iWhatty/id-dom/issues"
|
|
33
|
+
},
|
|
34
|
+
"homepage": "https://github.com/iWhatty/id-dom#readme",
|
|
35
|
+
"keywords": [
|
|
36
|
+
"dom",
|
|
37
|
+
"getelementbyid",
|
|
38
|
+
"typed-dom",
|
|
39
|
+
"html",
|
|
40
|
+
"vanilla-js",
|
|
41
|
+
"browser",
|
|
42
|
+
"shadow-dom",
|
|
43
|
+
"utilities"
|
|
44
|
+
],
|
|
45
|
+
"scripts": {
|
|
46
|
+
"build": "node build.js",
|
|
47
|
+
"prepublishOnly": "npm run build",
|
|
48
|
+
"test": "vitest run --environment jsdom",
|
|
49
|
+
"test:watch": "vitest --environment jsdom"
|
|
50
|
+
},
|
|
51
|
+
"devDependencies": {
|
|
52
|
+
"esbuild": "^0.27.3",
|
|
53
|
+
"jsdom": "^24.0.0",
|
|
54
|
+
"vitest": "^3.1.2"
|
|
55
|
+
},
|
|
56
|
+
"engines": {
|
|
57
|
+
"node": ">=18"
|
|
58
|
+
}
|
|
59
|
+
}
|