idiotikmethods 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/IdiotikMethods.js +779 -0
  2. package/package.json +10 -0
  3. package/sw.js +267 -0
@@ -0,0 +1,779 @@
1
+ // ================================================================
2
+ // IDIOTIK METHODS — v0.1
3
+ // Single bundled trust loader. Loads every library under its roof,
4
+ // initializes each one so pipelines are live, registers all to
5
+ // window.idiotik.methods.*, and boots the SW stack.
6
+ // ================================================================
7
+
8
+ (function (root) {
9
+ "use strict";
10
+
11
+ // ----------------------------------------------------------------
12
+ // GUARD — only run once
13
+ // ----------------------------------------------------------------
14
+ if (root.__IdiotikMethods) return;
15
+ root.__IdiotikMethods = true;
16
+
17
+ // ----------------------------------------------------------------
18
+ // WAIT FOR IDIOTIK CORE
19
+ // ----------------------------------------------------------------
20
+ function _waitForIdiotik(cb) {
21
+ if (root.idiotik) return cb(root.idiotik);
22
+ let attempts = 0;
23
+ const t = setInterval(() => {
24
+ if (root.idiotik) { clearInterval(t); cb(root.idiotik); }
25
+ else if (++attempts > 100) { clearInterval(t); console.warn("[IdiotikMethods] idiotik core not found after 10s"); }
26
+ }, 100);
27
+ }
28
+
29
+ // ----------------------------------------------------------------
30
+ // LIBRARY REGISTRY
31
+ // ----------------------------------------------------------------
32
+ const _methods = {};
33
+ const _loadedUrls = new Set();
34
+
35
+ // ----------------------------------------------------------------
36
+ // FULL LIBRARY MANIFEST
37
+ // Every library the project could ever need — loaded under this roof.
38
+ // cdnjs preferred, unpkg fallback.
39
+ // ----------------------------------------------------------------
40
+ const LIBRARIES = [
41
+ // ── PDF / DOC ──────────────────────────────────────────────
42
+ {
43
+ key: "pdfjs",
44
+ url: "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.2.67/pdf.min.js",
45
+ fallback: "https://unpkg.com/pdfjs-dist@4.2.67/build/pdf.min.js",
46
+ init: () => _initPdfJs(),
47
+ },
48
+
49
+ // ── GESTURE / INPUT ────────────────────────────────────────
50
+ {
51
+ key: "hammer",
52
+ url: "https://cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js",
53
+ fallback: "https://unpkg.com/hammerjs@2.0.8/hammer.min.js",
54
+ init: () => _initHammer(),
55
+ },
56
+ {
57
+ key: "interact",
58
+ url: "https://cdnjs.cloudflare.com/ajax/libs/interact.js/1.10.27/interact.min.js",
59
+ fallback: "https://unpkg.com/interactjs@1.10.27/dist/interact.min.js",
60
+ init: () => _register("interact", root.interact),
61
+ },
62
+
63
+ // ── MAPS ───────────────────────────────────────────────────
64
+ {
65
+ key: "leaflet",
66
+ url: "https://cdnjs.cloudflare.com/ajax/libs/leaflet/1.9.4/leaflet.js",
67
+ fallback: "https://unpkg.com/leaflet@1.9.4/dist/leaflet.js",
68
+ init: () => _initLeaflet(),
69
+ },
70
+
71
+ // ── 3D / CANVAS / GRAPHICS ─────────────────────────────────
72
+ {
73
+ key: "three",
74
+ url: "https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js",
75
+ fallback: "https://unpkg.com/three@0.128.0/build/three.min.js",
76
+ init: () => _register("three", root.THREE),
77
+ },
78
+ {
79
+ key: "pixijs",
80
+ url: "https://cdnjs.cloudflare.com/ajax/libs/pixi.js/7.4.2/pixi.min.js",
81
+ fallback: "https://unpkg.com/pixi.js@7.4.2/dist/pixi.min.js",
82
+ init: () => _register("pixijs", root.PIXI),
83
+ },
84
+ {
85
+ key: "fabric",
86
+ url: "https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.3.0/fabric.min.js",
87
+ fallback: "https://unpkg.com/fabric@5.3.0/dist/fabric.min.js",
88
+ init: () => _register("fabric", root.fabric),
89
+ },
90
+ {
91
+ key: "konva",
92
+ url: "https://cdnjs.cloudflare.com/ajax/libs/konva/9.3.3/konva.min.js",
93
+ fallback: "https://unpkg.com/konva@9.3.3/konva.min.js",
94
+ init: () => _register("konva", root.Konva),
95
+ },
96
+ {
97
+ key: "p5",
98
+ url: "https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.min.js",
99
+ fallback: "https://unpkg.com/p5@1.9.4/lib/p5.min.js",
100
+ init: () => _register("p5", root.p5),
101
+ },
102
+ {
103
+ key: "d3",
104
+ url: "https://cdnjs.cloudflare.com/ajax/libs/d3/7.9.0/d3.min.js",
105
+ fallback: "https://unpkg.com/d3@7.9.0/dist/d3.min.js",
106
+ init: () => _register("d3", root.d3),
107
+ },
108
+ {
109
+ key: "chartjs",
110
+ url: "https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.3/chart.umd.min.js",
111
+ fallback: "https://unpkg.com/chart.js@4.4.3/dist/chart.umd.min.js",
112
+ init: () => _register("chartjs", root.Chart),
113
+ },
114
+ {
115
+ key: "echarts",
116
+ url: "https://cdnjs.cloudflare.com/ajax/libs/echarts/5.5.0/echarts.min.js",
117
+ fallback: "https://unpkg.com/echarts@5.5.0/dist/echarts.min.js",
118
+ init: () => _register("echarts", root.echarts),
119
+ },
120
+
121
+ // ── AUDIO ──────────────────────────────────────────────────
122
+ {
123
+ key: "tone",
124
+ url: "https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.js",
125
+ fallback: "https://unpkg.com/tone@14.8.49/build/Tone.js",
126
+ init: () => _register("tone", root.Tone),
127
+ },
128
+ {
129
+ key: "howler",
130
+ url: "https://cdnjs.cloudflare.com/ajax/libs/howler/2.2.4/howler.min.js",
131
+ fallback: "https://unpkg.com/howler@2.2.4/dist/howler.min.js",
132
+ init: () => _register("howler", root.Howl ? { Howl: root.Howl, Howler: root.Howler } : null),
133
+ },
134
+ {
135
+ key: "wavesurfer",
136
+ url: "https://cdnjs.cloudflare.com/ajax/libs/wavesurfer.js/7.8.2/wavesurfer.js",
137
+ fallback: "https://unpkg.com/wavesurfer.js@7.8.2/dist/wavesurfer.js",
138
+ init: () => _register("wavesurfer", root.WaveSurfer),
139
+ },
140
+
141
+ // ── ANIMATION ──────────────────────────────────────────────
142
+ {
143
+ key: "gsap",
144
+ url: "https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js",
145
+ fallback: "https://unpkg.com/gsap@3.12.5/dist/gsap.min.js",
146
+ init: () => _register("gsap", root.gsap),
147
+ },
148
+ {
149
+ key: "anime",
150
+ url: "https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.2/anime.min.js",
151
+ fallback: "https://unpkg.com/animejs@3.2.2/lib/anime.min.js",
152
+ init: () => _register("anime", root.anime),
153
+ },
154
+ {
155
+ key: "lottie",
156
+ url: "https://cdnjs.cloudflare.com/ajax/libs/bodymovin/5.12.2/lottie.min.js",
157
+ fallback: "https://unpkg.com/lottie-web@5.12.2/build/player/lottie.min.js",
158
+ init: () => _register("lottie", root.lottie),
159
+ },
160
+ {
161
+ key: "velocity",
162
+ url: "https://cdnjs.cloudflare.com/ajax/libs/velocity/2.0.6/velocity.min.js",
163
+ fallback: "https://unpkg.com/velocity-animate@2.0.6/velocity.min.js",
164
+ init: () => _register("velocity", root.Velocity),
165
+ },
166
+
167
+ // ── NETWORK / REALTIME ─────────────────────────────────────
168
+ {
169
+ key: "socketio",
170
+ url: "https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.7.5/socket.io.min.js",
171
+ fallback: "https://unpkg.com/socket.io-client@4.7.5/dist/socket.io.min.js",
172
+ init: () => _register("socketio", root.io),
173
+ },
174
+ {
175
+ key: "axios",
176
+ url: "https://cdnjs.cloudflare.com/ajax/libs/axios/1.7.2/axios.min.js",
177
+ fallback: "https://unpkg.com/axios@1.7.2/dist/axios.min.js",
178
+ init: () => _register("axios", root.axios),
179
+ },
180
+ {
181
+ key: "signalr",
182
+ url: "https://cdnjs.cloudflare.com/ajax/libs/microsoft-signalr/8.0.7/signalr.min.js",
183
+ fallback: "https://unpkg.com/@microsoft/signalr@8.0.7/dist/browser/signalr.min.js",
184
+ init: () => _register("signalr", root.signalR),
185
+ },
186
+
187
+ // ── UTILITY ────────────────────────────────────────────────
188
+ {
189
+ key: "lodash",
190
+ url: "https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js",
191
+ fallback: "https://unpkg.com/lodash@4.17.21/lodash.min.js",
192
+ init: () => _register("lodash", root._),
193
+ },
194
+ {
195
+ key: "dayjs",
196
+ url: "https://cdnjs.cloudflare.com/ajax/libs/dayjs/1.11.11/dayjs.min.js",
197
+ fallback: "https://unpkg.com/dayjs@1.11.11/dayjs.min.js",
198
+ init: () => _register("dayjs", root.dayjs),
199
+ },
200
+ {
201
+ key: "moment",
202
+ url: "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.30.1/moment.min.js",
203
+ fallback: "https://unpkg.com/moment@2.30.1/moment.min.js",
204
+ init: () => _register("moment", root.moment),
205
+ },
206
+ {
207
+ key: "mathjs",
208
+ url: "https://cdnjs.cloudflare.com/ajax/libs/mathjs/13.0.3/math.min.js",
209
+ fallback: "https://unpkg.com/mathjs@13.0.3/lib/browser/math.js",
210
+ init: () => _register("mathjs", root.math),
211
+ },
212
+ {
213
+ key: "rxjs",
214
+ url: "https://cdnjs.cloudflare.com/ajax/libs/rxjs/7.8.1/rxjs.umd.min.js",
215
+ fallback: "https://unpkg.com/rxjs@7.8.1/dist/bundles/rxjs.umd.min.js",
216
+ init: () => _register("rxjs", root.rxjs),
217
+ },
218
+ {
219
+ key: "ramda",
220
+ url: "https://cdnjs.cloudflare.com/ajax/libs/ramda/0.30.1/ramda.min.js",
221
+ fallback: "https://unpkg.com/ramda@0.30.1/dist/ramda.min.js",
222
+ init: () => _register("ramda", root.R),
223
+ },
224
+
225
+ // ── DOM / UI ───────────────────────────────────────────────
226
+ {
227
+ key: "jquery",
228
+ url: "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js",
229
+ fallback: "https://unpkg.com/jquery@3.7.1/dist/jquery.min.js",
230
+ init: () => _register("jquery", root.jQuery),
231
+ },
232
+ {
233
+ key: "alpine",
234
+ url: "https://cdnjs.cloudflare.com/ajax/libs/alpinejs/3.14.1/cdn.min.js",
235
+ fallback: "https://unpkg.com/alpinejs@3.14.1/dist/cdn.min.js",
236
+ init: () => _register("alpine", root.Alpine),
237
+ },
238
+ {
239
+ key: "htmx",
240
+ url: "https://cdnjs.cloudflare.com/ajax/libs/htmx/2.0.1/htmx.min.js",
241
+ fallback: "https://unpkg.com/htmx.org@2.0.1/dist/htmx.min.js",
242
+ init: () => _register("htmx", root.htmx),
243
+ },
244
+ {
245
+ key: "sortablejs",
246
+ url: "https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.2/Sortable.min.js",
247
+ fallback: "https://unpkg.com/sortablejs@1.15.2/Sortable.min.js",
248
+ init: () => _register("sortablejs", root.Sortable),
249
+ },
250
+ {
251
+ key: "popper",
252
+ url: "https://cdnjs.cloudflare.com/ajax/libs/popper.js/2.11.8/umd/popper.min.js",
253
+ fallback: "https://unpkg.com/@popperjs/core@2.11.8/dist/umd/popper.min.js",
254
+ init: () => _register("popper", root.Popper),
255
+ },
256
+ {
257
+ key: "tippy",
258
+ url: "https://cdnjs.cloudflare.com/ajax/libs/tippy.js/6.3.7/tippy.umd.min.js",
259
+ fallback: "https://unpkg.com/tippy.js@6.3.7/dist/tippy.umd.min.js",
260
+ init: () => _register("tippy", root.tippy),
261
+ },
262
+ {
263
+ key: "notyf",
264
+ url: "https://cdnjs.cloudflare.com/ajax/libs/notyf/3.10.0/notyf.min.js",
265
+ fallback: "https://unpkg.com/notyf@3.10.0/notyf.min.js",
266
+ init: () => _register("notyf", root.Notyf),
267
+ },
268
+ {
269
+ key: "sweetalert2",
270
+ url: "https://cdnjs.cloudflare.com/ajax/libs/limonte-sweetalert2/11.12.2/sweetalert2.min.js",
271
+ fallback: "https://unpkg.com/sweetalert2@11.12.2/dist/sweetalert2.min.js",
272
+ init: () => _register("sweetalert2", root.Swal),
273
+ },
274
+
275
+ // ── DATA / PARSING ─────────────────────────────────────────
276
+ {
277
+ key: "papaparse",
278
+ url: "https://cdnjs.cloudflare.com/ajax/libs/PapaParse/5.4.1/papaparse.min.js",
279
+ fallback: "https://unpkg.com/papaparse@5.4.1/papaparse.min.js",
280
+ init: () => _register("papaparse", root.Papa),
281
+ },
282
+ {
283
+ key: "xlsx",
284
+ url: "https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js",
285
+ fallback: "https://unpkg.com/xlsx@0.18.5/dist/xlsx.full.min.js",
286
+ init: () => _register("xlsx", root.XLSX),
287
+ },
288
+ {
289
+ key: "marked",
290
+ url: "https://cdnjs.cloudflare.com/ajax/libs/marked/13.0.2/marked.min.js",
291
+ fallback: "https://unpkg.com/marked@13.0.2/marked.min.js",
292
+ init: () => _register("marked", root.marked),
293
+ },
294
+ {
295
+ key: "dompurify",
296
+ url: "https://cdnjs.cloudflare.com/ajax/libs/dompurify/3.1.6/purify.min.js",
297
+ fallback: "https://unpkg.com/dompurify@3.1.6/dist/purify.min.js",
298
+ init: () => _register("dompurify", root.DOMPurify),
299
+ },
300
+ {
301
+ key: "fuse",
302
+ url: "https://cdnjs.cloudflare.com/ajax/libs/fuse.js/7.0.0/fuse.min.js",
303
+ fallback: "https://unpkg.com/fuse.js@7.0.0/dist/fuse.min.js",
304
+ init: () => _register("fuse", root.Fuse),
305
+ },
306
+ {
307
+ key: "lunr",
308
+ url: "https://cdnjs.cloudflare.com/ajax/libs/lunr.js/2.3.9/lunr.min.js",
309
+ fallback: "https://unpkg.com/lunr@2.3.9/lunr.min.js",
310
+ init: () => _register("lunr", root.lunr),
311
+ },
312
+
313
+ // ── CRYPTO / SECURITY ──────────────────────────────────────
314
+ {
315
+ key: "cryptojs",
316
+ url: "https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.2.0/crypto-js.min.js",
317
+ fallback: "https://unpkg.com/crypto-js@4.2.0/crypto-js.js",
318
+ init: () => _register("cryptojs", root.CryptoJS),
319
+ },
320
+ {
321
+ key: "forge",
322
+ url: "https://cdnjs.cloudflare.com/ajax/libs/forge/1.3.1/forge.min.js",
323
+ fallback: "https://unpkg.com/node-forge@1.3.1/dist/forge.min.js",
324
+ init: () => _register("forge", root.forge),
325
+ },
326
+
327
+ // ── MEDIA / STREAM ─────────────────────────────────────────
328
+ {
329
+ key: "videojs",
330
+ url: "https://cdnjs.cloudflare.com/ajax/libs/video.js/8.12.0/video.min.js",
331
+ fallback: "https://unpkg.com/video.js@8.12.0/dist/video.min.js",
332
+ init: () => _register("videojs", root.videojs),
333
+ },
334
+ {
335
+ key: "recordrtc",
336
+ url: "https://cdnjs.cloudflare.com/ajax/libs/RecordRTC/5.6.2/RecordRTC.min.js",
337
+ fallback: "https://unpkg.com/recordrtc@5.6.2/RecordRTC.js",
338
+ init: () => _register("recordrtc", root.RecordRTC),
339
+ },
340
+
341
+ // ── PHYSICS / SIMULATION ───────────────────────────────────
342
+ {
343
+ key: "matterjs",
344
+ url: "https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.19.0/matter.min.js",
345
+ fallback: "https://unpkg.com/matter-js@0.19.0/build/matter.min.js",
346
+ init: () => _register("matterjs", root.Matter),
347
+ },
348
+ {
349
+ key: "planck",
350
+ url: "https://cdnjs.cloudflare.com/ajax/libs/planck/1.0.0/planck.min.js",
351
+ fallback: "https://unpkg.com/planck@1.0.0/dist/planck.min.js",
352
+ init: () => _register("planck", root.planck),
353
+ },
354
+
355
+ // ── ROUTING / SPA ──────────────────────────────────────────
356
+ {
357
+ key: "navigo",
358
+ url: "https://cdnjs.cloudflare.com/ajax/libs/navigo/8.11.1/navigo.min.js",
359
+ fallback: "https://unpkg.com/navigo@8.11.1/lib/navigo.min.js",
360
+ init: () => _register("navigo", root.Navigo),
361
+ },
362
+ {
363
+ key: "pagejs",
364
+ url: "https://cdnjs.cloudflare.com/ajax/libs/page.js/1.11.6/page.min.js",
365
+ fallback: "https://unpkg.com/page@1.11.6/page.js",
366
+ init: () => _register("pagejs", root.page),
367
+ },
368
+
369
+ // ── WORKER / PARALLEL ──────────────────────────────────────
370
+ {
371
+ key: "comlink",
372
+ url: "https://unpkg.com/comlink@4.4.1/dist/umd/comlink.js",
373
+ fallback: "https://cdn.jsdelivr.net/npm/comlink@4.4.1/dist/umd/comlink.js",
374
+ init: () => _register("comlink", root.Comlink),
375
+ },
376
+
377
+ // ── MISC POWER TOOLS ───────────────────────────────────────
378
+ {
379
+ key: "idb",
380
+ url: "https://cdnjs.cloudflare.com/ajax/libs/idb/8.0.0/idb.min.js",
381
+ fallback: "https://unpkg.com/idb@8.0.0/build/umd.js",
382
+ init: () => _register("idb", root.idb),
383
+ },
384
+ {
385
+ key: "localforage",
386
+ url: "https://cdnjs.cloudflare.com/ajax/libs/localforage/1.10.0/localforage.min.js",
387
+ fallback: "https://unpkg.com/localforage@1.10.0/dist/localforage.min.js",
388
+ init: () => _register("localforage", root.localforage),
389
+ },
390
+ {
391
+ key: "fingerprintjs",
392
+ url: "https://cdnjs.cloudflare.com/ajax/libs/fingerprintjs2/2.1.4/fingerprint2.min.js",
393
+ fallback: "https://unpkg.com/fingerprintjs2@2.1.4/dist/fingerprint2.min.js",
394
+ init: () => _register("fingerprintjs", root.Fingerprint2),
395
+ },
396
+ {
397
+ key: "qrcode",
398
+ url: "https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js",
399
+ fallback: "https://unpkg.com/qrcodejs@1.0.0/qrcode.min.js",
400
+ init: () => _register("qrcode", root.QRCode),
401
+ },
402
+ {
403
+ key: "zxing",
404
+ url: "https://cdnjs.cloudflare.com/ajax/libs/zxing-js/0.21.3/index.min.js",
405
+ fallback: "https://unpkg.com/@zxing/library@0.21.3/umd/index.min.js",
406
+ init: () => _register("zxing", root.ZXing),
407
+ },
408
+ {
409
+ key: "tesseract",
410
+ url: "https://cdnjs.cloudflare.com/ajax/libs/tesseract.js/5.1.0/tesseract.min.js",
411
+ fallback: "https://unpkg.com/tesseract.js@5.1.0/dist/tesseract.min.js",
412
+ init: () => _register("tesseract", root.Tesseract),
413
+ },
414
+ {
415
+ key: "ml5",
416
+ url: "https://unpkg.com/ml5@1.2.1/dist/ml5.min.js",
417
+ fallback: "https://cdn.jsdelivr.net/npm/ml5@1.2.1/dist/ml5.min.js",
418
+ init: () => _register("ml5", root.ml5),
419
+ },
420
+ {
421
+ key: "tensorflow",
422
+ url: "https://cdnjs.cloudflare.com/ajax/libs/tensorflow/4.20.0/tf.min.js",
423
+ fallback: "https://unpkg.com/@tensorflow/tfjs@4.20.0/dist/tf.min.js",
424
+ init: () => _register("tensorflow", root.tf),
425
+ },
426
+ ];
427
+
428
+ // ----------------------------------------------------------------
429
+ // REGISTER — wrap a library ref into window.idiotik.methods.*
430
+ // ----------------------------------------------------------------
431
+ function _register(key, ref) {
432
+ if (!ref) {
433
+ console.warn(`[IdiotikMethods] ${key} — global not found after load, skipping register`);
434
+ return;
435
+ }
436
+ _methods[key] = ref;
437
+ // expose on window.idiotik.methods if idiotik is present
438
+ if (root.idiotik) {
439
+ root.idiotik.methods = root.idiotik.methods || {};
440
+ root.idiotik.methods[key] = ref;
441
+ }
442
+ }
443
+
444
+ // ----------------------------------------------------------------
445
+ // LIBRARY-SPECIFIC INITIALIZERS
446
+ // Each one: initialize, run minimum viable activity, leave pipeline live.
447
+ // ----------------------------------------------------------------
448
+
449
+ function _initPdfJs() {
450
+ const pdfjsLib = root.pdfjsLib || root["pdfjs-dist"] || root.pdfjsLib;
451
+ if (!pdfjsLib) { console.warn("[IdiotikMethods] pdf.js global not found"); return; }
452
+
453
+ // set worker src (use CDN worker)
454
+ if (pdfjsLib.GlobalWorkerOptions) {
455
+ pdfjsLib.GlobalWorkerOptions.workerSrc =
456
+ "https://cdnjs.cloudflare.com/ajax/libs/pdf.js/4.2.67/pdf.worker.min.js";
457
+ }
458
+
459
+ // spin up a minimal invisible container to get the trusted event pipeline live
460
+ const container = document.createElement("div");
461
+ container.style.cssText = "position:fixed;width:1px;height:1px;top:-9999px;left:-9999px;overflow:hidden;pointer-events:none;";
462
+ document.body.appendChild(container);
463
+
464
+ // render a blank 1-page PDF to initialize pdf.js's trusted click + interaction channels
465
+ const blankPdfBase64 =
466
+ "JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2JqCjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2JqCjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCAxIDFdID4+CmVuZG9iagp4cmVmCjAgNAowMDAwMDAwMDAwIDY1NTM1IGYgCjAwMDAwMDAwMDkgMDAwMDAgbiAKMDAwMDAwMDA1OCAwMDAwMCBuIAowMDAwMDAwMTE1IDAwMDAwIG4gCnRyYWlsZXIKPDwgL1NpemUgNCAvUm9vdCAxIDAgUiA+PgpzdGFydHhyZWYKMTkwCiUlRU9G";
467
+
468
+ const pdfData = atob(blankPdfBase64);
469
+ const pdfArray = new Uint8Array(pdfData.length);
470
+ for (let i = 0; i < pdfData.length; i++) pdfArray[i] = pdfData.charCodeAt(i);
471
+
472
+ pdfjsLib.getDocument({ data: pdfArray }).promise.then((pdf) => {
473
+ return pdf.getPage(1);
474
+ }).then((page) => {
475
+ const canvas = document.createElement("canvas");
476
+ canvas.style.cssText = "position:absolute;width:1px;height:1px;pointer-events:none;";
477
+ container.appendChild(canvas);
478
+ const ctx = canvas.getContext("2d");
479
+ const viewport = page.getViewport({ scale: 0.01 });
480
+ canvas.width = viewport.width;
481
+ canvas.height = viewport.height;
482
+ page.render({ canvasContext: ctx, viewport });
483
+ }).catch(() => {});
484
+
485
+ _register("pdfjs", pdfjsLib);
486
+
487
+ // expose the trusted container for bypass hooks
488
+ _methods.pdfjsContainer = container;
489
+ if (root.idiotik) {
490
+ root.idiotik.methods = root.idiotik.methods || {};
491
+ root.idiotik.methods.pdfjsContainer = container;
492
+ }
493
+ }
494
+
495
+ function _initHammer() {
496
+ if (!root.Hammer) { console.warn("[IdiotikMethods] Hammer.js not found"); return; }
497
+ // create a hidden target and register ALL recognizers so the gesture pipeline is live
498
+ const target = document.createElement("div");
499
+ target.style.cssText = "position:fixed;width:1px;height:1px;top:-9999px;left:-9999px;touch-action:none;pointer-events:all;";
500
+ document.body.appendChild(target);
501
+
502
+ const mc = new root.Hammer.Manager(target, {
503
+ recognizers: [
504
+ [root.Hammer.Tap],
505
+ [root.Hammer.DoubleTap],
506
+ [root.Hammer.Press],
507
+ [root.Hammer.Pan, { direction: root.Hammer.DIRECTION_ALL }],
508
+ [root.Hammer.Swipe, { direction: root.Hammer.DIRECTION_ALL }],
509
+ [root.Hammer.Pinch],
510
+ [root.Hammer.Rotate],
511
+ ],
512
+ });
513
+
514
+ // fire noop listeners so the pipeline actually activates
515
+ ["tap", "doubletap", "press", "pan", "swipe", "pinch", "rotate"].forEach((ev) => {
516
+ mc.on(ev, () => {});
517
+ });
518
+
519
+ _register("hammer", root.Hammer);
520
+ _methods.hammerManager = mc;
521
+ if (root.idiotik) {
522
+ root.idiotik.methods = root.idiotik.methods || {};
523
+ root.idiotik.methods.hammerManager = mc;
524
+ }
525
+ }
526
+
527
+ function _initLeaflet() {
528
+ if (!root.L) { console.warn("[IdiotikMethods] Leaflet not found"); return; }
529
+ // spin up a hidden map instance so tile/event pipeline is live
530
+ const container = document.createElement("div");
531
+ container.style.cssText = "position:fixed;width:1px;height:1px;top:-9999px;left:-9999px;overflow:hidden;";
532
+ document.body.appendChild(container);
533
+
534
+ try {
535
+ const map = root.L.map(container, {
536
+ center: [0, 0],
537
+ zoom: 1,
538
+ zoomControl: false,
539
+ attributionControl: false,
540
+ });
541
+ // register pointer + gesture listeners so the pipeline fires
542
+ map.on("click mousemove", () => {});
543
+ _register("leaflet", root.L);
544
+ _methods.leafletMap = map;
545
+ if (root.idiotik) {
546
+ root.idiotik.methods = root.idiotik.methods || {};
547
+ root.idiotik.methods.leafletMap = map;
548
+ }
549
+ } catch (e) {
550
+ // container not ready or headless env
551
+ _register("leaflet", root.L);
552
+ }
553
+ }
554
+
555
+ // ----------------------------------------------------------------
556
+ // SCRIPT LOADER — load as classic <script> (no type=module)
557
+ // so everything lands on the shared window. with cdnjs/unpkg fallback.
558
+ // ----------------------------------------------------------------
559
+ function _loadScript(url, fallback) {
560
+ return new Promise((resolve) => {
561
+ if (_loadedUrls.has(url)) return resolve(true);
562
+ _loadedUrls.add(url);
563
+
564
+ const tryLoad = (src, isFallback) => {
565
+ const s = document.createElement("script");
566
+ s.src = src;
567
+ // NO type="module" — forces classic script so globals land on window
568
+ s.async = false;
569
+ s.onload = () => resolve(true);
570
+ s.onerror = () => {
571
+ if (!isFallback && fallback) {
572
+ tryLoad(fallback, true);
573
+ } else {
574
+ console.warn(`[IdiotikMethods] failed to load: ${src}`);
575
+ resolve(false);
576
+ }
577
+ };
578
+ document.head.appendChild(s);
579
+ };
580
+
581
+ tryLoad(url, false);
582
+ });
583
+ }
584
+
585
+ // ----------------------------------------------------------------
586
+ // MAIN BOOT — load all libraries, init, register
587
+ // ----------------------------------------------------------------
588
+ async function _boot(idiotik) {
589
+ // set up methods namespace immediately
590
+ idiotik.methods = idiotik.methods || {};
591
+
592
+ // register SW before loading libraries
593
+ // (SW needs to be up to intercept library requests)
594
+ await _bootSW(idiotik);
595
+
596
+ // load all libraries concurrently — they're independent
597
+ await Promise.allSettled(
598
+ LIBRARIES.map(async (lib) => {
599
+ const ok = await _loadScript(lib.url, lib.fallback);
600
+ if (ok) {
601
+ try {
602
+ lib.init();
603
+ } catch (e) {
604
+ console.warn(`[IdiotikMethods] init error for ${lib.key}:`, e);
605
+ }
606
+ }
607
+ })
608
+ );
609
+
610
+ // final sync — make sure idiotik.methods is fully populated
611
+ Object.assign(idiotik.methods, _methods);
612
+
613
+ console.info(
614
+ `[IdiotikMethods] loaded ${Object.keys(_methods).length}/${LIBRARIES.length} libraries. ` +
615
+ `registry: window.idiotik.methods.*`
616
+ );
617
+
618
+ // signal to IdiotikScript that methods are live
619
+ root.__IdiotikMethodsReady = true;
620
+ root.dispatchEvent && root.dispatchEvent(new Event("idiotik:methods:ready"));
621
+ }
622
+
623
+ // ----------------------------------------------------------------
624
+ // SW BOOT — register service workers + window Proxy fallback
625
+ // Injected into IdiotikMethods so this file owns the SW lifecycle.
626
+ // ----------------------------------------------------------------
627
+ async function _bootSW(idiotik) {
628
+ if (!("serviceWorker" in navigator)) {
629
+ console.warn("[IdiotikMethods] SW not supported — falling back to window Proxy");
630
+ _installWindowProxy(idiotik);
631
+ return;
632
+ }
633
+
634
+ // determine SW script URL — look for sw.js next to IdiotikMethods.js
635
+ const swUrl = _resolveSwUrl();
636
+
637
+ try {
638
+ const reg = await navigator.serviceWorker.register(swUrl, { scope: "/" });
639
+ console.info("[IdiotikMethods] SW registered:", reg.scope);
640
+
641
+ // wait for the SW to be active
642
+ await _waitForSWActive(reg);
643
+
644
+ // signal the SW that IdiotikMethods is live and share the VFS
645
+ _syncVfsToSW();
646
+
647
+ } catch (primaryErr) {
648
+ console.warn("[IdiotikMethods] primary SW failed:", primaryErr.message, "— trying fallback");
649
+
650
+ // try fallback SW (different internal strategy)
651
+ try {
652
+ const fallbackUrl = _resolveSwUrl("fallback");
653
+ const fallbackReg = await navigator.serviceWorker.register(fallbackUrl, { scope: "/" });
654
+ await _waitForSWActive(fallbackReg);
655
+ console.info("[IdiotikMethods] fallback SW active");
656
+ _syncVfsToSW();
657
+ } catch (fallbackErr) {
658
+ console.warn("[IdiotikMethods] fallback SW also failed:", fallbackErr.message, "— installing window Proxy");
659
+ _installWindowProxy(idiotik);
660
+ }
661
+ }
662
+ }
663
+
664
+ function _resolveSwUrl(type) {
665
+ // look for sw.js in the same directory as the current script
666
+ const scripts = document.querySelectorAll("script[src]");
667
+ for (const s of scripts) {
668
+ if (s.src && (s.src.includes("IdiotikMethods") || s.src.includes("idiotik"))) {
669
+ const base = s.src.substring(0, s.src.lastIndexOf("/") + 1);
670
+ return type === "fallback" ? `${base}sw-fallback.js` : `${base}sw.js`;
671
+ }
672
+ }
673
+ return type === "fallback" ? "/sw-fallback.js" : "/sw.js";
674
+ }
675
+
676
+ function _waitForSWActive(reg) {
677
+ return new Promise((resolve) => {
678
+ if (reg.active) return resolve();
679
+ const sw = reg.installing || reg.waiting;
680
+ if (!sw) return resolve();
681
+ sw.addEventListener("statechange", function handler() {
682
+ if (sw.state === "activated") {
683
+ sw.removeEventListener("statechange", handler);
684
+ resolve();
685
+ }
686
+ });
687
+ // timeout after 5s — don't block library loading indefinitely
688
+ setTimeout(resolve, 5000);
689
+ });
690
+ }
691
+
692
+ function _syncVfsToSW() {
693
+ // send VFS contents to active SW so it can serve .idiotik.js files from it
694
+ if (!navigator.serviceWorker.controller) return;
695
+ const vfsKeys = root.idiotik && root.idiotik.script
696
+ ? root.idiotik.script.vfs.keys()
697
+ : [];
698
+ const vfsEntries = vfsKeys.map((k) => ({
699
+ path: k,
700
+ source: root.idiotik.script.vfs.get(k),
701
+ }));
702
+ navigator.serviceWorker.controller.postMessage({
703
+ type: "IDIOTIK_VFS_SYNC",
704
+ vfs: vfsEntries,
705
+ });
706
+ }
707
+
708
+ // ----------------------------------------------------------------
709
+ // WINDOW PROXY FALLBACK
710
+ // Sits on window before libraries load, intercepts their writes
711
+ // the moment they happen. Last resort if both SWs fail.
712
+ // ----------------------------------------------------------------
713
+ function _installWindowProxy(idiotik) {
714
+ // globals we care about wrapping
715
+ const WATCH_KEYS = [
716
+ "pdfjsLib", "Hammer", "L", "THREE", "PIXI", "fabric", "Konva",
717
+ "p5", "d3", "Chart", "echarts", "Tone", "Howl", "WaveSurfer",
718
+ "gsap", "anime", "lottie", "Velocity", "io", "axios", "signalR",
719
+ "_", "dayjs", "moment", "math", "rxjs", "R", "jQuery", "$",
720
+ "Alpine", "htmx", "Sortable", "Popper", "tippy", "Notyf", "Swal",
721
+ "Papa", "XLSX", "marked", "DOMPurify", "Fuse", "lunr",
722
+ "CryptoJS", "forge", "videojs", "RecordRTC", "Matter", "planck",
723
+ "Navigo", "page", "Comlink", "idb", "localforage",
724
+ "Fingerprint2", "QRCode", "ZXing", "Tesseract", "ml5", "tf",
725
+ ];
726
+
727
+ // map from global key → methods key
728
+ const KEY_MAP = {
729
+ pdfjsLib: "pdfjs", L: "leaflet", THREE: "three", PIXI: "pixijs",
730
+ Chart: "chartjs", Tone: "tone", Howl: "howler", WaveSurfer: "wavesurfer",
731
+ io: "socketio", _: "lodash", math: "mathjs", R: "ramda",
732
+ jQuery: "jquery", $: "jquery", Swal: "sweetalert2", Papa: "papaparse",
733
+ ZXing: "zxing", Fingerprint2: "fingerprintjs", tf: "tensorflow",
734
+ };
735
+
736
+ WATCH_KEYS.forEach((key) => {
737
+ if (Object.prototype.hasOwnProperty.call(root, key)) {
738
+ // already set — wrap immediately
739
+ const val = root[key];
740
+ const methodKey = KEY_MAP[key] || key.toLowerCase();
741
+ _register(methodKey, val);
742
+ return;
743
+ }
744
+ // not set yet — intercept the set
745
+ let _val;
746
+ try {
747
+ Object.defineProperty(root, key, {
748
+ configurable: true,
749
+ enumerable: true,
750
+ get() { return _val; },
751
+ set(v) {
752
+ _val = v;
753
+ const methodKey = KEY_MAP[key] || key.toLowerCase();
754
+ _register(methodKey, v);
755
+ // restore to plain value so library works normally
756
+ try {
757
+ Object.defineProperty(root, key, {
758
+ configurable: true,
759
+ enumerable: true,
760
+ writable: true,
761
+ value: v,
762
+ });
763
+ } catch (_) {}
764
+ },
765
+ });
766
+ } catch (_) {
767
+ // defineProperty failed (e.g. non-configurable) — skip
768
+ }
769
+ });
770
+
771
+ idiotik._proxyInstalled = true;
772
+ }
773
+
774
+ // ----------------------------------------------------------------
775
+ // BOOT SEQUENCE
776
+ // ----------------------------------------------------------------
777
+ _waitForIdiotik(_boot);
778
+
779
+ }(typeof window !== "undefined" ? window : (typeof global !== "undefined" ? global : this)));
package/package.json ADDED
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "idiotikmethods",
3
+ "version": "0.1.0",
4
+ "main": "IdiotikMethods.js",
5
+ "files": [
6
+ "IdiotikMethods.js",
7
+ "sw.js"
8
+ ],
9
+ "license": "MIT"
10
+ }
package/sw.js ADDED
@@ -0,0 +1,267 @@
1
+ // ================================================================
2
+ // IDIOTIK SERVICE WORKER — v0.1
3
+ // Single file. Primary strategy: response rewrite.
4
+ // If primary intercept fails: blob respondWith fallback.
5
+ // Owns the VFS — serves .idiotik.js files as CDN-origin dependencies.
6
+ // ================================================================
7
+
8
+ "use strict";
9
+
10
+ // ----------------------------------------------------------------
11
+ // STATE
12
+ // ----------------------------------------------------------------
13
+
14
+ // in-memory VFS — path → auto-wrapped source
15
+ const VFS = new Map();
16
+
17
+ // which CDN hosts we intercept library requests from
18
+ const CDN_ORIGINS = [
19
+ "cdnjs.cloudflare.com",
20
+ "unpkg.com",
21
+ "cdn.jsdelivr.net",
22
+ "ajax.googleapis.com",
23
+ "raw.githubusercontent.com",
24
+ "gist.githubusercontent.com",
25
+ ];
26
+
27
+ // idiotik wrapper injected into every intercepted library source
28
+ // this is appended after the library source — registers it into window.idiotik.methods
29
+ const IDIOTIK_WRAPPER_TEMPLATE = (key) => `
30
+ ;(function(){
31
+ try {
32
+ if (typeof self !== 'undefined' && self.__idiotikMethodsQueue) {
33
+ self.__idiotikMethodsQueue.push("${key}");
34
+ }
35
+ if (typeof window !== 'undefined' && window.idiotik) {
36
+ window.idiotik.methods = window.idiotik.methods || {};
37
+ // the actual global assignment happens in IdiotikMethods.js init()
38
+ // this wrapper just signals that the library executed inside our context
39
+ window.idiotik.__swWrapped = window.idiotik.__swWrapped || [];
40
+ window.idiotik.__swWrapped.push("${key}");
41
+ }
42
+ } catch(e) {}
43
+ })();
44
+ `;
45
+
46
+ // ----------------------------------------------------------------
47
+ // INSTALL + ACTIVATE — take control immediately
48
+ // ----------------------------------------------------------------
49
+
50
+ self.addEventListener("install", (event) => {
51
+ event.waitUntil(self.skipWaiting());
52
+ });
53
+
54
+ self.addEventListener("activate", (event) => {
55
+ event.waitUntil(self.clients.claim());
56
+ });
57
+
58
+ // ----------------------------------------------------------------
59
+ // MESSAGE — receive VFS sync from IdiotikMethods.js
60
+ // ----------------------------------------------------------------
61
+
62
+ self.addEventListener("message", (event) => {
63
+ const data = event.data;
64
+ if (!data || !data.type) return;
65
+
66
+ if (data.type === "IDIOTIK_VFS_SYNC") {
67
+ // populate VFS from the main thread's idiotik.script.vfs
68
+ if (Array.isArray(data.vfs)) {
69
+ for (const entry of data.vfs) {
70
+ if (entry.path && entry.source) {
71
+ VFS.set(entry.path, entry.source);
72
+ }
73
+ }
74
+ }
75
+ // acknowledge
76
+ if (event.source) {
77
+ event.source.postMessage({ type: "IDIOTIK_VFS_ACK", count: VFS.size });
78
+ }
79
+ return;
80
+ }
81
+
82
+ if (data.type === "IDIOTIK_VFS_SET") {
83
+ // single entry upsert
84
+ if (data.path && data.source) {
85
+ VFS.set(data.path, data.source);
86
+ }
87
+ return;
88
+ }
89
+
90
+ if (data.type === "IDIOTIK_VFS_DELETE") {
91
+ if (data.path) VFS.delete(data.path);
92
+ return;
93
+ }
94
+
95
+ if (data.type === "IDIOTIK_VFS_CLEAR") {
96
+ VFS.clear();
97
+ return;
98
+ }
99
+ });
100
+
101
+ // ----------------------------------------------------------------
102
+ // FETCH — main intercept logic
103
+ // Strategy A (primary): fetch real source, inject wrapper, respond
104
+ // Strategy B (fallback): blob URL respondWith
105
+ // ----------------------------------------------------------------
106
+
107
+ self.addEventListener("fetch", (event) => {
108
+ const url = new URL(event.request.url);
109
+
110
+ // ── VFS lookup — serve .idiotik.js files as if CDN-origin ──
111
+ // check VFS by full URL and by pathname
112
+ if (VFS.has(event.request.url) || VFS.has(url.pathname)) {
113
+ const source = VFS.get(event.request.url) || VFS.get(url.pathname);
114
+ event.respondWith(_serveFromVfs(source, event.request.url));
115
+ return;
116
+ }
117
+
118
+ // check if this is a .idiotik.js file served from a CDN path that matches VFS
119
+ if (url.pathname.endsWith(".idiotik.js")) {
120
+ // try VFS by filename
121
+ const filename = url.pathname.split("/").pop();
122
+ for (const [vfsPath, source] of VFS.entries()) {
123
+ if (vfsPath.endsWith(filename) || vfsPath.includes(filename)) {
124
+ event.respondWith(_serveFromVfs(source, event.request.url));
125
+ return;
126
+ }
127
+ }
128
+ }
129
+
130
+ // ── CDN library interception ────────────────────────────────
131
+ if (_isCdnRequest(url)) {
132
+ event.respondWith(_interceptCdnRequest(event.request, url));
133
+ return;
134
+ }
135
+
136
+ // everything else — pass through untouched
137
+ });
138
+
139
+ // ----------------------------------------------------------------
140
+ // VFS SERVER — serve auto-wrapped .idiotik.js as valid JS
141
+ // The browser believes this is a real CDN-origin file.
142
+ // ----------------------------------------------------------------
143
+
144
+ function _serveFromVfs(source, originalUrl) {
145
+ const headers = new Headers({
146
+ "Content-Type": "application/javascript; charset=utf-8",
147
+ "Cache-Control": "no-store",
148
+ "X-Idiotik-VFS": "true",
149
+ });
150
+ return new Response(source, { status: 200, headers });
151
+ }
152
+
153
+ // ----------------------------------------------------------------
154
+ // CDN INTERCEPT — primary strategy: response rewrite
155
+ // Fetches real source, injects idiotik wrapper, serves modified response.
156
+ // Trust fully intact because library still originates from real CDN.
157
+ // ----------------------------------------------------------------
158
+
159
+ async function _interceptCdnRequest(request, url) {
160
+ // derive a library key from the URL
161
+ const libKey = _keyFromUrl(url);
162
+
163
+ // ── PRIMARY: response rewrite ────────────────────────────────
164
+ try {
165
+ const realResponse = await fetch(request.url, {
166
+ cache: "force-cache",
167
+ credentials: "omit",
168
+ });
169
+
170
+ if (!realResponse.ok) throw new Error(`CDN responded ${realResponse.status}`);
171
+
172
+ const originalSource = await realResponse.text();
173
+
174
+ // inject wrapper at end of library source
175
+ const patchedSource = originalSource + "\n" + IDIOTIK_WRAPPER_TEMPLATE(libKey);
176
+
177
+ const headers = new Headers(realResponse.headers);
178
+ headers.set("Content-Type", "application/javascript; charset=utf-8");
179
+ headers.set("X-Idiotik-Wrapped", libKey);
180
+
181
+ return new Response(patchedSource, {
182
+ status: realResponse.status,
183
+ statusText: realResponse.statusText,
184
+ headers,
185
+ });
186
+
187
+ } catch (primaryErr) {
188
+ // ── FALLBACK: blob respondWith ───────────────────────────
189
+ // Different internal strategy — clone into blob, attach wrapper in blob context
190
+ try {
191
+ return await _blobFallbackStrategy(request, url, libKey);
192
+ } catch (fallbackErr) {
193
+ // both failed — pass through unmodified
194
+ console.warn(`[IdiotikSW] both strategies failed for ${url.href}:`, fallbackErr.message);
195
+ return fetch(request);
196
+ }
197
+ }
198
+ }
199
+
200
+ // ----------------------------------------------------------------
201
+ // FALLBACK STRATEGY — blob respondWith
202
+ // Fetches real source, clones into blob URL, attaches wrapper hooks.
203
+ // Different internal path — may succeed where response rewrite was blocked.
204
+ // ----------------------------------------------------------------
205
+
206
+ async function _blobFallbackStrategy(request, url, libKey) {
207
+ const realResponse = await fetch(request.url, {
208
+ cache: "force-cache",
209
+ credentials: "omit",
210
+ });
211
+
212
+ if (!realResponse.ok) throw new Error(`CDN blob fallback: ${realResponse.status}`);
213
+
214
+ const originalSource = await realResponse.text();
215
+ const patchedSource = originalSource + "\n" + IDIOTIK_WRAPPER_TEMPLATE(libKey);
216
+
217
+ // create a blob with correct JS MIME type
218
+ const blob = new Blob([patchedSource], { type: "application/javascript" });
219
+
220
+ // serve from blob — browser receives a valid JS response with wrapper baked in
221
+ const headers = new Headers();
222
+ headers.set("Content-Type", "application/javascript; charset=utf-8");
223
+ headers.set("X-Idiotik-BlobWrapped", libKey);
224
+
225
+ return new Response(blob, {
226
+ status: 200,
227
+ headers,
228
+ });
229
+ }
230
+
231
+ // ----------------------------------------------------------------
232
+ // HELPERS
233
+ // ----------------------------------------------------------------
234
+
235
+ function _isCdnRequest(url) {
236
+ return CDN_ORIGINS.some((origin) => url.hostname === origin || url.hostname.endsWith("." + origin));
237
+ }
238
+
239
+ function _keyFromUrl(url) {
240
+ // derive a short stable key from the CDN URL
241
+ // e.g. cdnjs.cloudflare.com/ajax/libs/hammer.js/2.0.8/hammer.min.js → hammer
242
+ const path = url.pathname.toLowerCase();
243
+
244
+ // try /ajax/libs/LIBNAME/... (cdnjs)
245
+ const cdnjsMatch = path.match(/\/ajax\/libs\/([^/]+)/);
246
+ if (cdnjsMatch) {
247
+ return _sanitizeKey(cdnjsMatch[1]);
248
+ }
249
+
250
+ // try unpkg / jsdelivr: /LIBNAME@version or /LIBNAME/...
251
+ const unpkgMatch = path.match(/^\/(?:npm\/)?(@[^/]+\/[^/@]+|[^/@]+)/);
252
+ if (unpkgMatch) {
253
+ return _sanitizeKey(unpkgMatch[1]);
254
+ }
255
+
256
+ // fallback — use hostname + first path segment
257
+ const parts = path.split("/").filter(Boolean);
258
+ return _sanitizeKey(parts[0] || "unknown");
259
+ }
260
+
261
+ function _sanitizeKey(raw) {
262
+ return raw
263
+ .replace(/[@.]/g, "_")
264
+ .replace(/[^a-z0-9_]/g, "")
265
+ .replace(/^_+|_+$/g, "")
266
+ .slice(0, 32);
267
+ }