@next2d/builder 0.1.8 → 0.1.11

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 (67) hide show
  1. package/dist/cli.d.ts +59 -0
  2. package/dist/cli.js +141 -0
  3. package/dist/constants.d.ts +53 -0
  4. package/dist/constants.js +64 -0
  5. package/dist/context.d.ts +29 -0
  6. package/dist/context.js +19 -0
  7. package/dist/electron.d.ts +10 -0
  8. package/dist/electron.js +142 -0
  9. package/dist/index.js +18 -790
  10. package/dist/native.d.ts +27 -0
  11. package/dist/native.js +123 -0
  12. package/dist/utils.d.ts +27 -0
  13. package/dist/utils.js +40 -0
  14. package/dist/web.d.ts +18 -0
  15. package/dist/web.js +70 -0
  16. package/dist/xbox.d.ts +62 -0
  17. package/dist/xbox.js +426 -0
  18. package/package.json +11 -9
  19. package/templates/xbox/CMakeLists.txt +47 -11
  20. package/templates/xbox/README.md +18 -0
  21. package/templates/xbox/js/bootstrap.js +120 -0
  22. package/templates/xbox/src/AssetLoader.cpp +115 -12
  23. package/templates/xbox/src/AssetLoader.h +4 -0
  24. package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
  25. package/templates/xbox/src/EmbeddedAssets.h +60 -0
  26. package/templates/xbox/src/EventLoop.cpp +0 -15
  27. package/templates/xbox/src/bindings/Audio.cpp +118 -22
  28. package/templates/xbox/src/bindings/Canvas.cpp +32 -2
  29. package/templates/xbox/src/bindings/Canvas2D.cpp +495 -39
  30. package/templates/xbox/src/bindings/Console.cpp +6 -1
  31. package/templates/xbox/src/bindings/DomShims.cpp +18 -2
  32. package/templates/xbox/src/bindings/EventTarget.cpp +9 -0
  33. package/templates/xbox/src/bindings/Image.cpp +83 -57
  34. package/templates/xbox/src/bindings/Polyfills.cpp +671 -236
  35. package/templates/xbox/src/bindings/RasterCore.h +105 -1
  36. package/templates/xbox/src/bindings/Video.cpp +57 -3
  37. package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +29 -3
  38. package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +4 -1
  39. package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
  40. package/templates/xbox/src/gpu/DawnContext.cpp +167 -16
  41. package/templates/xbox/src/main.cpp +385 -16
  42. package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
  43. package/templates/xbox/src/platform/AudioDecoder.h +18 -0
  44. package/templates/xbox/src/platform/AudioEngine.cpp +28 -3
  45. package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
  46. package/templates/xbox/src/platform/DecodeQueue.h +27 -0
  47. package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
  48. package/templates/xbox/src/platform/ImageDecoder.h +18 -0
  49. package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
  50. package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
  51. package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
  52. package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
  53. package/templates/xbox/src/v8/CodeCache.h +223 -0
  54. package/templates/xbox/src/v8/V8Runtime.cpp +34 -33
  55. package/templates/xbox/src/v8/V8Util.h +54 -4
  56. package/templates/xbox/src/worker/WorkerRuntime.cpp +165 -65
  57. package/templates/xbox/src/worker/WorkerRuntime.h +23 -3
  58. package/templates/xbox/tests/check_local.sh +5 -1
  59. package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
  60. package/templates/xbox/tests/raster_test.cpp +50 -0
  61. package/templates/xbox/tests/windows_platform_test.cpp +75 -3
  62. package/templates/xbox/third_party/LICENSES.md +14 -0
  63. package/templates/xbox/third_party/dr_mp3.h +5412 -0
  64. package/templates/xbox/third_party/dr_wav.h +9131 -0
  65. package/templates/xbox/third_party/stb_image.h +7988 -0
  66. package/templates/xbox/third_party/stb_truetype.h +5079 -0
  67. package/templates/xbox/third_party/stb_vorbis.c +5584 -0
@@ -232,245 +232,10 @@ const char kPolyfills4[] = R"JS(
232
232
  })(globalThis);
233
233
  )JS";
234
234
 
235
- // section 5: indexedDB / TextDecoder
235
+ // section 5: TextDecoder
236
236
  const char kPolyfills5[] = R"JS(
237
237
  (function (global) {
238
238
  "use strict";
239
- // indexedDB (最小実装・ファイル永続化)。
240
- // ゲームの典型用途 (open → objectStore → get/put/delete/getAll) をカバーする。
241
- // 値は JSON 化可能なもの + ArrayBuffer/TypedArray (base64 でタグ付け永続化)。
242
- // カーソル/インデックス/複合キーは未対応 («EXTEND»)。
243
- if (typeof global.indexedDB === "undefined" &&
244
- typeof global.__next2d_storage_load === "function") {
245
-
246
- const TYPED = {
247
- "Int8Array": Int8Array, "Uint8Array": Uint8Array,
248
- "Uint8ClampedArray": Uint8ClampedArray,
249
- "Int16Array": Int16Array, "Uint16Array": Uint16Array,
250
- "Int32Array": Int32Array, "Uint32Array": Uint32Array,
251
- "Float32Array": Float32Array, "Float64Array": Float64Array
252
- };
253
-
254
- const bytesToB64 = function (u8) {
255
- let bin = "";
256
- for (let i = 0; i < u8.length; i += 0x8000) {
257
- bin += String.fromCharCode.apply(null, u8.subarray(i, i + 0x8000));
258
- }
259
- return global.btoa(bin);
260
- };
261
- const b64ToBytes = function (b64) {
262
- const bin = global.atob(b64);
263
- const u8 = new Uint8Array(bin.length);
264
- for (let i = 0; i < bin.length; i++) { u8[i] = bin.charCodeAt(i); }
265
- return u8;
266
- };
267
-
268
- const encodeValue = function (v) {
269
- if (v instanceof ArrayBuffer) {
270
- return { "__n2d_bin": bytesToB64(new Uint8Array(v)), "__n2d_type": "ArrayBuffer" };
271
- }
272
- if (ArrayBuffer.isView(v)) {
273
- const name = v.constructor && v.constructor.name;
274
- if (TYPED[name]) {
275
- return {
276
- "__n2d_bin": bytesToB64(new Uint8Array(v.buffer, v.byteOffset, v.byteLength)),
277
- "__n2d_type": name
278
- };
279
- }
280
- }
281
- if (Array.isArray(v)) { return v.map(encodeValue); }
282
- if (v && typeof v === "object") {
283
- const out = {};
284
- for (const k of Object.keys(v)) { out[k] = encodeValue(v[k]); }
285
- return out;
286
- }
287
- return v;
288
- };
289
- const decodeValue = function (v) {
290
- if (v && typeof v === "object") {
291
- if (typeof v.__n2d_bin === "string") {
292
- const u8 = b64ToBytes(v.__n2d_bin);
293
- if (v.__n2d_type === "ArrayBuffer") { return u8.buffer; }
294
- const Ctor = TYPED[v.__n2d_type] || Uint8Array;
295
- return new Ctor(u8.buffer, 0, u8.byteLength / Ctor.BYTES_PER_ELEMENT);
296
- }
297
- if (Array.isArray(v)) { return v.map(decodeValue); }
298
- const out = {};
299
- for (const k of Object.keys(v)) { out[k] = decodeValue(v[k]); }
300
- return out;
301
- }
302
- return v;
303
- };
304
-
305
- const loadDb = function (name) {
306
- try {
307
- const raw = global.__next2d_storage_load("idb_" + name);
308
- if (raw) { return JSON.parse(raw); }
309
- } catch (e) { /* 破損時は初期化 */ }
310
- return { "version": 0, "stores": {} };
311
- };
312
- const saveDb = function (name, db) {
313
- try { global.__next2d_storage_save("idb_" + name, JSON.stringify(db)); } catch (e) { /* 無視 */ }
314
- };
315
-
316
- const makeRequest = function () {
317
- return { "onsuccess": null, "onerror": null, "result": undefined, "error": null };
318
- };
319
- const fireSuccess = function (r) {
320
- global.queueMicrotask(function () {
321
- if (typeof r.onsuccess === "function") { r.onsuccess({ "target": r }); }
322
- });
323
- };
324
- const fireError = function (r, e) {
325
- r.error = e;
326
- global.queueMicrotask(function () {
327
- if (typeof r.onerror === "function") { r.onerror({ "target": r }); }
328
- });
329
- };
330
-
331
- global.indexedDB = {
332
- open(name, version) {
333
- const req = makeRequest();
334
- req.onupgradeneeded = null;
335
- req.onblocked = null;
336
- global.queueMicrotask(function () {
337
- const data = loadDb(name);
338
-
339
- const storeApi = function (storeName) {
340
- const entry = data.stores[storeName] ||
341
- (data.stores[storeName] = { "keyPath": null, "data": {} });
342
- const run = function (fn) {
343
- const r = makeRequest();
344
- global.queueMicrotask(function () {
345
- try { r.result = fn(); fireSuccess(r); }
346
- catch (e) { fireError(r, e); }
347
- });
348
- return r;
349
- };
350
- return {
351
- "keyPath": entry.keyPath,
352
- get(key) {
353
- return run(function () {
354
- const raw = entry.data[String(key)];
355
- return raw === undefined ? undefined : decodeValue(raw);
356
- });
357
- },
358
- put(value, key) {
359
- return run(function () {
360
- let k = key;
361
- if (k === undefined && entry.keyPath && value &&
362
- typeof value === "object") {
363
- k = value[entry.keyPath];
364
- }
365
- if (k === undefined) { throw new Error("key required"); }
366
- entry.data[String(k)] = encodeValue(value);
367
- saveDb(name, data);
368
- return k;
369
- });
370
- },
371
- add(value, key) { return this.put(value, key); },
372
- "delete": function (key) {
373
- return run(function () {
374
- delete entry.data[String(key)];
375
- saveDb(name, data);
376
- });
377
- },
378
- clear() {
379
- return run(function () {
380
- entry.data = {};
381
- saveDb(name, data);
382
- });
383
- },
384
- count() { return run(function () { return Object.keys(entry.data).length; }); },
385
- getAll() {
386
- return run(function () {
387
- return Object.keys(entry.data).map(function (k) {
388
- return decodeValue(entry.data[k]);
389
- });
390
- });
391
- },
392
- getAllKeys() { return run(function () { return Object.keys(entry.data); }); }
393
- };
394
- };
395
-
396
- const dbObj = {
397
- "name": name,
398
- "objectStoreNames": {
399
- contains(s) { return Object.prototype.hasOwnProperty.call(data.stores, s); },
400
- get length() { return Object.keys(data.stores).length; }
401
- },
402
- createObjectStore(storeName, options) {
403
- if (!data.stores[storeName]) {
404
- data.stores[storeName] = {
405
- "keyPath": options && options.keyPath ? options.keyPath : null,
406
- "data": {}
407
- };
408
- saveDb(name, data);
409
- }
410
- return storeApi(storeName);
411
- },
412
- deleteObjectStore(storeName) {
413
- delete data.stores[storeName];
414
- saveDb(name, data);
415
- },
416
- transaction(_names, _mode) {
417
- const tx = {
418
- "oncomplete": null, "onerror": null, "onabort": null,
419
- objectStore(s) { return storeApi(s); },
420
- abort() { /* 未対応 */ },
421
- commit() { /* 自動コミット */ }
422
- };
423
- // 全リクエストのマイクロタスク消化後に complete を発火
424
- global.queueMicrotask(function () {
425
- global.queueMicrotask(function () {
426
- global.queueMicrotask(function () {
427
- if (typeof tx.oncomplete === "function") {
428
- tx.oncomplete({ "target": tx });
429
- }
430
- });
431
- });
432
- });
433
- return tx;
434
- },
435
- close() { /* no-op */ },
436
- "onversionchange": null
437
- };
438
-
439
- const oldVersion = data.version || 0;
440
- const newVersion = typeof version === "number" ? version : Math.max(1, oldVersion);
441
- req.result = dbObj;
442
- if (newVersion > oldVersion) {
443
- data.version = newVersion;
444
- if (typeof req.onupgradeneeded === "function") {
445
- req.onupgradeneeded({
446
- "target": req,
447
- "oldVersion": oldVersion,
448
- "newVersion": newVersion
449
- });
450
- }
451
- saveDb(name, data);
452
- }
453
- if (typeof req.onsuccess === "function") {
454
- req.onsuccess({ "target": req });
455
- }
456
- });
457
- return req;
458
- },
459
- deleteDatabase(name) {
460
- const req = makeRequest();
461
- global.queueMicrotask(function () {
462
- try {
463
- global.__next2d_storage_save("idb_" + name, "");
464
- fireSuccess(req);
465
- } catch (e) {
466
- fireError(req, e);
467
- }
468
- });
469
- return req;
470
- }
471
- };
472
- }
473
-
474
239
  if (typeof global.TextDecoder === "undefined") {
475
240
  global.TextDecoder = class TextDecoder {
476
241
  constructor(label) { this._label = label || "utf-8"; }
@@ -764,6 +529,661 @@ const char kPolyfills6[] = R"JS(
764
529
  })(globalThis);
765
530
  )JS";
766
531
 
532
+ // section 7: indexedDB (完全実装: cursor/index/複合キー/IDBKeyRange/autoIncrement)。
533
+ // 31KB あり MSVC の文字列リテラル上限(~16KB)を超えるため 3 リテラルに分割し、
534
+ // InstallPolyfills で連結して 1 スクリプトとして評価する。atob/btoa(section3)と
535
+ // queueMicrotask(section4)に依存するため、それらの後で評価すること。
536
+ const char kIndexedDB1[] = R"JS(
537
+ (function (global) {
538
+ "use strict";
539
+ // indexedDB (ファイル永続化・単一スレッド同期ストア + 非同期 API 面)。
540
+ // 対応: open/upgrade, objectStore(keyPath / 複合keyPath / autoIncrement),
541
+ // get/getKey/getAll/getAllKeys/count/put/add/delete/clear,
542
+ // createIndex/index/deleteIndex(unique/multiEntry),
543
+ // openCursor/openKeyCursor(next/nextunique/prev/prevunique, IDBKeyRange),
544
+ // IDBKeyRange(only/lowerBound/upperBound/bound)。
545
+ // 値は JSON 化可能なもの + ArrayBuffer/TypedArray (base64 タグ付け)。
546
+ if (typeof global.indexedDB !== "undefined" ||
547
+ typeof global.__next2d_storage_load !== "function") {
548
+ return;
549
+ }
550
+
551
+ const TYPED = {
552
+ "Int8Array": Int8Array, "Uint8Array": Uint8Array,
553
+ "Uint8ClampedArray": Uint8ClampedArray,
554
+ "Int16Array": Int16Array, "Uint16Array": Uint16Array,
555
+ "Int32Array": Int32Array, "Uint32Array": Uint32Array,
556
+ "Float32Array": Float32Array, "Float64Array": Float64Array
557
+ };
558
+
559
+ const bytesToB64 = function (u8) {
560
+ let bin = "";
561
+ for (let i = 0; i < u8.length; i += 0x8000) {
562
+ bin += String.fromCharCode.apply(null, u8.subarray(i, i + 0x8000));
563
+ }
564
+ return global.btoa(bin);
565
+ };
566
+ const b64ToBytes = function (b64) {
567
+ const bin = global.atob(b64);
568
+ const u8 = new Uint8Array(bin.length);
569
+ for (let i = 0; i < bin.length; i++) { u8[i] = bin.charCodeAt(i); }
570
+ return u8;
571
+ };
572
+ const encodeValue = function (v) {
573
+ if (v instanceof ArrayBuffer) {
574
+ return { "__n2d_bin": bytesToB64(new Uint8Array(v)), "__n2d_type": "ArrayBuffer" };
575
+ }
576
+ if (ArrayBuffer.isView(v)) {
577
+ const name = v.constructor && v.constructor.name;
578
+ if (TYPED[name]) {
579
+ return {
580
+ "__n2d_bin": bytesToB64(new Uint8Array(v.buffer, v.byteOffset, v.byteLength)),
581
+ "__n2d_type": name
582
+ };
583
+ }
584
+ }
585
+ if (Array.isArray(v)) { return v.map(encodeValue); }
586
+ if (v && typeof v === "object") {
587
+ const out = {};
588
+ for (const k of Object.keys(v)) { out[k] = encodeValue(v[k]); }
589
+ return out;
590
+ }
591
+ return v;
592
+ };
593
+ const decodeValue = function (v) {
594
+ if (v && typeof v === "object") {
595
+ if (typeof v.__n2d_bin === "string") {
596
+ const u8 = b64ToBytes(v.__n2d_bin);
597
+ if (v.__n2d_type === "ArrayBuffer") { return u8.buffer; }
598
+ const Ctor = TYPED[v.__n2d_type] || Uint8Array;
599
+ return new Ctor(u8.buffer, 0, u8.byteLength / Ctor.BYTES_PER_ELEMENT);
600
+ }
601
+ if (Array.isArray(v)) { return v.map(decodeValue); }
602
+ const out = {};
603
+ for (const k of Object.keys(v)) { out[k] = decodeValue(v[k]); }
604
+ return out;
605
+ }
606
+ return v;
607
+ };
608
+
609
+ // --- キー順序 (IndexedDB 仕様: number < string < array) -------------------
610
+ const keyType = function (k) {
611
+ if (Array.isArray(k)) { return 3; }
612
+ if (typeof k === "number") { return 0; }
613
+ if (typeof k === "string") { return 2; }
614
+ return -1;
615
+ };
616
+ const cmpKeys = function (a, b) {
617
+ const ta = keyType(a), tb = keyType(b);
618
+ if (ta !== tb) { return ta < tb ? -1 : 1; }
619
+ if (ta === 3) {
620
+ const n = Math.min(a.length, b.length);
621
+ for (let i = 0; i < n; i++) {
622
+ const c = cmpKeys(a[i], b[i]);
623
+ if (c !== 0) { return c; }
624
+ }
625
+ return a.length === b.length ? 0 : (a.length < b.length ? -1 : 1);
626
+ }
627
+ if (a < b) { return -1; }
628
+ if (a > b) { return 1; }
629
+ return 0;
630
+ };
631
+ const isValidKey = function (k) { return keyType(k) !== -1; };
632
+ // ストレージのマップ用一意文字列
633
+ const encKey = function (k) { return JSON.stringify(k); };
634
+
635
+ // objectStoreNames / indexNames は配列 + DOMStringList 互換 (contains/item)。
636
+ const nameList = function (names) {
637
+ names.sort();
638
+ names.contains = function (n) { return names.indexOf(n) !== -1; };
639
+ names.item = function (i) { return names[i] != null ? names[i] : null; };
640
+ return names;
641
+ };
642
+
643
+ // --- keyPath 抽出 (ネスト "a.b" / 複合 ["a","b"]) -------------------------
644
+ const evalPath = function (value, path) {
645
+ if (path === "") { return value; }
646
+ const parts = path.split(".");
647
+ let cur = value;
648
+ for (let i = 0; i < parts.length; i++) {
649
+ if (cur == null || typeof cur !== "object") { return undefined; }
650
+ cur = cur[parts[i]];
651
+ }
652
+ return cur;
653
+ };
654
+ const extractKey = function (value, keyPath) {
655
+ if (keyPath == null) { return undefined; }
656
+ if (Array.isArray(keyPath)) {
657
+ const out = [];
658
+ for (let i = 0; i < keyPath.length; i++) {
659
+ const k = evalPath(value, keyPath[i]);
660
+ if (k === undefined) { return undefined; }
661
+ out.push(k);
662
+ }
663
+ return out;
664
+ }
665
+ return evalPath(value, keyPath);
666
+ };
667
+ // in-line key を value へ書き戻す (autoIncrement 生成キー用、ネスト対応)
668
+ const injectKey = function (value, keyPath, key) {
669
+ if (typeof keyPath !== "string" || value == null || typeof value !== "object") { return; }
670
+ const parts = keyPath.split(".");
671
+ let cur = value;
672
+ for (let i = 0; i < parts.length - 1; i++) {
673
+ if (cur[parts[i]] == null || typeof cur[parts[i]] !== "object") { cur[parts[i]] = {}; }
674
+ cur = cur[parts[i]];
675
+ }
676
+ cur[parts[parts.length - 1]] = key;
677
+ };
678
+
679
+ // --- IDBKeyRange --------------------------------------------------------
680
+ const makeRange = function (lower, upper, lowerOpen, upperOpen) {
681
+ return {
682
+ "lower": lower, "upper": upper,
683
+ "lowerOpen": !!lowerOpen, "upperOpen": !!upperOpen,
684
+ includes(key) { return rangeIncludes(this, key); }
685
+ };
686
+ };
687
+ const rangeIncludes = function (range, key) {
688
+ if (range == null) { return true; }
689
+ // 素のキー (range でない) は only 相当
690
+ if (typeof range !== "object" || !("lower" in range)) {
691
+ return cmpKeys(key, range) === 0;
692
+ }
693
+ if (range.lower !== undefined) {
694
+ const c = cmpKeys(key, range.lower);
695
+ if (c < 0 || (c === 0 && range.lowerOpen)) { return false; }
696
+ }
697
+ if (range.upper !== undefined) {
698
+ const c = cmpKeys(key, range.upper);
699
+ if (c > 0 || (c === 0 && range.upperOpen)) { return false; }
700
+ }
701
+ return true;
702
+ };
703
+ global.IDBKeyRange = {
704
+ only(v) { return makeRange(v, v, false, false); },
705
+ lowerBound(v, open) { return makeRange(v, undefined, open, false); },
706
+ upperBound(v, open) { return makeRange(undefined, v, false, open); },
707
+ bound(l, u, lo, uo) { return makeRange(l, u, lo, uo); }
708
+ };
709
+
710
+ // --- 永続化 -------------------------------------------------------------
711
+ const loadDb = function (name) {
712
+ try {
713
+ const raw = global.__next2d_storage_load("idb_" + name);
714
+ if (raw) { return JSON.parse(raw); }
715
+ } catch (e) { /* 破損時は初期化 */ }
716
+ return { "version": 0, "stores": {} };
717
+ };
718
+ const saveDb = function (name, db) {
719
+ try { global.__next2d_storage_save("idb_" + name, JSON.stringify(db)); } catch (e) { /* 無視 */ }
720
+ };
721
+
722
+ // --- 非同期 request/event ----------------------------------------------
723
+ const makeRequest = function () {
724
+ return {
725
+ "onsuccess": null, "onerror": null, "onupgradeneeded": null, "onblocked": null,
726
+ "result": undefined, "error": null, "readyState": "pending", "source": null,
727
+ "transaction": null
728
+ };
729
+ };
730
+ const fireSuccess = function (r) {
731
+ global.queueMicrotask(function () {
732
+ r.readyState = "done";
733
+ if (typeof r.onsuccess === "function") { r.onsuccess({ "target": r }); }
734
+ });
735
+ };
736
+ const fireError = function (r, e) {
737
+ r.error = e;
738
+ global.queueMicrotask(function () {
739
+ r.readyState = "done";
740
+ if (typeof r.onerror === "function") { r.onerror({ "target": r }); }
741
+ });
742
+ };
743
+
744
+ global.indexedDB = {
745
+ open(name, version) {
746
+ const req = makeRequest();
747
+ global.queueMicrotask(function () {
748
+ const data = loadDb(name);
749
+
750
+ )JS";
751
+ const char kIndexedDB2[] = R"JS(
752
+ // records: encKey -> { "key": rawKey, "value": encodedValue }
753
+ const storeApi = function (storeName, tx) {
754
+ const entry = data.stores[storeName];
755
+ if (!entry) { throw new Error("NotFoundError: object store '" + storeName + "'"); }
756
+ if (!entry.records) { entry.records = {}; }
757
+ if (!entry.indexes) { entry.indexes = {}; }
758
+
759
+ const run = function (fn) {
760
+ const r = makeRequest();
761
+ r.transaction = tx;
762
+ global.queueMicrotask(function () {
763
+ try { r.result = fn(); fireSuccess(r); }
764
+ catch (e) { fireError(r, e); }
765
+ });
766
+ return r;
767
+ };
768
+
769
+ const allRecords = function () {
770
+ return Object.keys(entry.records).map(function (ek) {
771
+ return entry.records[ek];
772
+ });
773
+ };
774
+ const sortedRecords = function () {
775
+ return allRecords().sort(function (a, b) { return cmpKeys(a.key, b.key); });
776
+ };
777
+
778
+ const resolveKey = function (value, key) {
779
+ let k = key;
780
+ if (k === undefined && entry.keyPath != null) {
781
+ k = extractKey(value, entry.keyPath);
782
+ }
783
+ if (k === undefined && entry.autoIncrement) {
784
+ entry.keyGen = (entry.keyGen || 0) + 1;
785
+ k = entry.keyGen;
786
+ if (typeof entry.keyPath === "string") { injectKey(value, entry.keyPath, k); }
787
+ } else if (typeof k === "number" && entry.autoIncrement && k > (entry.keyGen || 0)) {
788
+ entry.keyGen = Math.floor(k);
789
+ }
790
+ return k;
791
+ };
792
+
793
+ const putImpl = function (value, key, noOverwrite) {
794
+ const k = resolveKey(value, key);
795
+ if (k === undefined || !isValidKey(k)) { throw new Error("DataError: invalid key"); }
796
+ const ek = encKey(k);
797
+ if (noOverwrite && Object.prototype.hasOwnProperty.call(entry.records, ek)) {
798
+ throw new Error("ConstraintError: key already exists");
799
+ }
800
+ entry.records[ek] = { "key": k, "value": encodeValue(value) };
801
+ saveDb(name, data);
802
+ return k;
803
+ };
804
+
805
+ // カーソル生成 (records: 配列 [{key, primaryKey, value(encoded)}])
806
+ const makeCursor = function (req2, source, records, direction, keyOnly) {
807
+ const dir = direction || "next";
808
+ records.sort(function (a, b) {
809
+ return cmpKeys(a.key, b.key) || cmpKeys(a.primaryKey, b.primaryKey);
810
+ });
811
+ if (dir === "prev" || dir === "prevunique") { records.reverse(); }
812
+ if (dir === "nextunique" || dir === "prevunique") {
813
+ const seen = [];
814
+ const uniq = [];
815
+ for (let i = 0; i < records.length; i++) {
816
+ const ek = encKey(records[i].key);
817
+ if (seen.indexOf(ek) === -1) { seen.push(ek); uniq.push(records[i]); }
818
+ }
819
+ records = uniq;
820
+ }
821
+ let pos = -1;
822
+ const cursor = {
823
+ "direction": dir,
824
+ "source": source,
825
+ get key() { return pos >= 0 && pos < records.length ? records[pos].key : undefined; },
826
+ get primaryKey() { return pos >= 0 && pos < records.length ? records[pos].primaryKey : undefined; },
827
+ get value() {
828
+ if (keyOnly || pos < 0 || pos >= records.length) { return undefined; }
829
+ return decodeValue(records[pos].value);
830
+ },
831
+ continue(key) {
832
+ if (key === undefined) { pos++; }
833
+ else {
834
+ const forward = (dir === "next" || dir === "nextunique");
835
+ let np = pos + 1;
836
+ while (np < records.length) {
837
+ const c = cmpKeys(records[np].key, key);
838
+ if ((forward && c >= 0) || (!forward && c <= 0)) { break; }
839
+ np++;
840
+ }
841
+ pos = np;
842
+ }
843
+ step();
844
+ },
845
+ advance(count) { pos += (count > 0 ? count : 1); step(); },
846
+ "delete": function () {
847
+ const rec = records[pos];
848
+ return run(function () {
849
+ if (rec) { delete entry.records[encKey(rec.primaryKey)]; saveDb(name, data); }
850
+ });
851
+ },
852
+ update(value) {
853
+ const rec = records[pos];
854
+ return run(function () {
855
+ if (!rec) { throw new Error("InvalidStateError"); }
856
+ entry.records[encKey(rec.primaryKey)] = {
857
+ "key": rec.primaryKey, "value": encodeValue(value)
858
+ };
859
+ saveDb(name, data);
860
+ return rec.primaryKey;
861
+ });
862
+ }
863
+ };
864
+ const step = function () {
865
+ if (pos >= 0 && pos < records.length) { req2.result = cursor; }
866
+ else { req2.result = null; }
867
+ fireSuccess(req2);
868
+ };
869
+ // カーソルを最初のレコードへ位置づけて success を発火する
870
+ pos++;
871
+ step();
872
+ };
873
+
874
+ const api = {
875
+ "name": storeName,
876
+ "keyPath": entry.keyPath != null ? entry.keyPath : null,
877
+ "autoIncrement": !!entry.autoIncrement,
878
+ get indexNames() { return nameList(Object.keys(entry.indexes)); },
879
+ "transaction": tx,
880
+ get(key) {
881
+ return run(function () {
882
+ const recs = sortedRecords();
883
+ for (let i = 0; i < recs.length; i++) {
884
+ if (rangeIncludes(key, recs[i].key)) { return decodeValue(recs[i].value); }
885
+ }
886
+ return undefined;
887
+ });
888
+ },
889
+ getKey(key) {
890
+ return run(function () {
891
+ const recs = sortedRecords();
892
+ for (let i = 0; i < recs.length; i++) {
893
+ if (rangeIncludes(key, recs[i].key)) { return recs[i].key; }
894
+ }
895
+ return undefined;
896
+ });
897
+ },
898
+ put(value, key) { return run(function () { return putImpl(value, key, false); }); },
899
+ add(value, key) { return run(function () { return putImpl(value, key, true); }); },
900
+ "delete": function (key) {
901
+ return run(function () {
902
+ const recs = sortedRecords();
903
+ for (let i = 0; i < recs.length; i++) {
904
+ if (rangeIncludes(key, recs[i].key)) { delete entry.records[encKey(recs[i].key)]; }
905
+ }
906
+ saveDb(name, data);
907
+ });
908
+ },
909
+ clear() { return run(function () { entry.records = {}; saveDb(name, data); }); },
910
+ count(key) {
911
+ return run(function () {
912
+ const recs = sortedRecords();
913
+ let n = 0;
914
+ for (let i = 0; i < recs.length; i++) {
915
+ if (rangeIncludes(key, recs[i].key)) { n++; }
916
+ }
917
+ return n;
918
+ });
919
+ },
920
+ getAll(query, count) {
921
+ return run(function () {
922
+ const out = [];
923
+ const recs = sortedRecords();
924
+ for (let i = 0; i < recs.length; i++) {
925
+ if (rangeIncludes(query, recs[i].key)) { out.push(decodeValue(recs[i].value)); }
926
+ if (count && out.length >= count) { break; }
927
+ }
928
+ return out;
929
+ });
930
+ },
931
+ getAllKeys(query, count) {
932
+ return run(function () {
933
+ const out = [];
934
+ const recs = sortedRecords();
935
+ for (let i = 0; i < recs.length; i++) {
936
+ if (rangeIncludes(query, recs[i].key)) { out.push(recs[i].key); }
937
+ if (count && out.length >= count) { break; }
938
+ }
939
+ return out;
940
+ });
941
+ },
942
+ openCursor(query, direction) {
943
+ const r = makeRequest();
944
+ r.transaction = tx;
945
+ global.queueMicrotask(function () {
946
+ const recs = sortedRecords()
947
+ .filter(function (rec) { return rangeIncludes(query, rec.key); })
948
+ .map(function (rec) {
949
+ return { "key": rec.key, "primaryKey": rec.key, "value": rec.value };
950
+ });
951
+ makeCursor(r, api, recs, direction, false);
952
+ });
953
+ return r;
954
+ },
955
+ openKeyCursor(query, direction) {
956
+ const r = makeRequest();
957
+ r.transaction = tx;
958
+ global.queueMicrotask(function () {
959
+ const recs = sortedRecords()
960
+ .filter(function (rec) { return rangeIncludes(query, rec.key); })
961
+ .map(function (rec) {
962
+ return { "key": rec.key, "primaryKey": rec.key, "value": rec.value };
963
+ });
964
+ makeCursor(r, api, recs, direction, true);
965
+ });
966
+ )JS";
967
+ const char kIndexedDB3[] = R"JS(
968
+ return r;
969
+ },
970
+ createIndex(indexName, keyPath, options) {
971
+ entry.indexes[indexName] = {
972
+ "keyPath": keyPath,
973
+ "unique": !!(options && options.unique),
974
+ "multiEntry": !!(options && options.multiEntry)
975
+ };
976
+ saveDb(name, data);
977
+ return indexApi(indexName);
978
+ },
979
+ deleteIndex(indexName) {
980
+ delete entry.indexes[indexName];
981
+ saveDb(name, data);
982
+ },
983
+ index(indexName) { return indexApi(indexName); }
984
+ };
985
+
986
+ // インデックス: レコードを走査してインデックスキーを抽出する。
987
+ const indexApi = function (indexName) {
988
+ const meta = entry.indexes[indexName];
989
+ if (!meta) { throw new Error("NotFoundError: index '" + indexName + "'"); }
990
+ // [{ indexKey, primaryKey, value }] を multiEntry 展開込みで生成
991
+ const indexRecords = function () {
992
+ const out = [];
993
+ const recs = allRecords();
994
+ for (let i = 0; i < recs.length; i++) {
995
+ const decoded = decodeValue(recs[i].value);
996
+ let ik = extractKey(decoded, meta.keyPath);
997
+ if (ik === undefined) { continue; }
998
+ if (meta.multiEntry && Array.isArray(ik)) {
999
+ for (let j = 0; j < ik.length; j++) {
1000
+ if (isValidKey(ik[j])) {
1001
+ out.push({ "key": ik[j], "primaryKey": recs[i].key, "value": recs[i].value });
1002
+ }
1003
+ }
1004
+ } else if (isValidKey(ik)) {
1005
+ out.push({ "key": ik, "primaryKey": recs[i].key, "value": recs[i].value });
1006
+ }
1007
+ }
1008
+ out.sort(function (a, b) {
1009
+ return cmpKeys(a.key, b.key) || cmpKeys(a.primaryKey, b.primaryKey);
1010
+ });
1011
+ return out;
1012
+ };
1013
+ return {
1014
+ "name": indexName,
1015
+ "keyPath": meta.keyPath,
1016
+ "unique": meta.unique,
1017
+ "multiEntry": meta.multiEntry,
1018
+ "objectStore": api,
1019
+ get(key) {
1020
+ return run(function () {
1021
+ const recs = indexRecords();
1022
+ for (let i = 0; i < recs.length; i++) {
1023
+ if (rangeIncludes(key, recs[i].key)) { return decodeValue(recs[i].value); }
1024
+ }
1025
+ return undefined;
1026
+ });
1027
+ },
1028
+ getKey(key) {
1029
+ return run(function () {
1030
+ const recs = indexRecords();
1031
+ for (let i = 0; i < recs.length; i++) {
1032
+ if (rangeIncludes(key, recs[i].key)) { return recs[i].primaryKey; }
1033
+ }
1034
+ return undefined;
1035
+ });
1036
+ },
1037
+ getAll(query, count) {
1038
+ return run(function () {
1039
+ const out = [];
1040
+ const recs = indexRecords();
1041
+ for (let i = 0; i < recs.length; i++) {
1042
+ if (rangeIncludes(query, recs[i].key)) { out.push(decodeValue(recs[i].value)); }
1043
+ if (count && out.length >= count) { break; }
1044
+ }
1045
+ return out;
1046
+ });
1047
+ },
1048
+ getAllKeys(query, count) {
1049
+ return run(function () {
1050
+ const out = [];
1051
+ const recs = indexRecords();
1052
+ for (let i = 0; i < recs.length; i++) {
1053
+ if (rangeIncludes(query, recs[i].key)) { out.push(recs[i].primaryKey); }
1054
+ if (count && out.length >= count) { break; }
1055
+ }
1056
+ return out;
1057
+ });
1058
+ },
1059
+ count(key) {
1060
+ return run(function () {
1061
+ const recs = indexRecords();
1062
+ let n = 0;
1063
+ for (let i = 0; i < recs.length; i++) {
1064
+ if (rangeIncludes(key, recs[i].key)) { n++; }
1065
+ }
1066
+ return n;
1067
+ });
1068
+ },
1069
+ openCursor(query, direction) {
1070
+ const r = makeRequest();
1071
+ r.transaction = tx;
1072
+ global.queueMicrotask(function () {
1073
+ const recs = indexRecords().filter(function (rec) {
1074
+ return rangeIncludes(query, rec.key);
1075
+ });
1076
+ makeCursor(r, this, recs, direction, false);
1077
+ });
1078
+ return r;
1079
+ },
1080
+ openKeyCursor(query, direction) {
1081
+ const r = makeRequest();
1082
+ r.transaction = tx;
1083
+ global.queueMicrotask(function () {
1084
+ const recs = indexRecords().filter(function (rec) {
1085
+ return rangeIncludes(query, rec.key);
1086
+ });
1087
+ makeCursor(r, this, recs, direction, true);
1088
+ });
1089
+ return r;
1090
+ }
1091
+ };
1092
+ };
1093
+
1094
+ return api;
1095
+ };
1096
+
1097
+ const dbObj = {
1098
+ "name": name,
1099
+ "version": 0,
1100
+ get objectStoreNames() { return nameList(Object.keys(data.stores)); },
1101
+ createObjectStore(storeName, options) {
1102
+ if (!data.stores[storeName]) {
1103
+ data.stores[storeName] = {
1104
+ "keyPath": options && options.keyPath !== undefined ? options.keyPath : null,
1105
+ "autoIncrement": !!(options && options.autoIncrement),
1106
+ "records": {}, "indexes": {}, "keyGen": 0
1107
+ };
1108
+ saveDb(name, data);
1109
+ }
1110
+ return storeApi(storeName, currentTx);
1111
+ },
1112
+ deleteObjectStore(storeName) {
1113
+ delete data.stores[storeName];
1114
+ saveDb(name, data);
1115
+ },
1116
+ transaction(_names, _mode) {
1117
+ const tx = {
1118
+ "oncomplete": null, "onerror": null, "onabort": null,
1119
+ "db": dbObj, "mode": _mode || "readonly", "error": null,
1120
+ objectStore(s) { return storeApi(s, tx); },
1121
+ abort() { /* 単純化: ロールバック無し */ },
1122
+ commit() { /* 自動コミット */ }
1123
+ };
1124
+ // 全リクエストのマイクロタスク消化後に complete
1125
+ let depth = 0;
1126
+ const settle = function () {
1127
+ if (depth++ < 4) { global.queueMicrotask(settle); return; }
1128
+ if (typeof tx.oncomplete === "function") { tx.oncomplete({ "target": tx }); }
1129
+ };
1130
+ global.queueMicrotask(settle);
1131
+ return tx;
1132
+ },
1133
+ close() { /* no-op */ },
1134
+ "onversionchange": null, "onabort": null, "onerror": null, "onclose": null
1135
+ };
1136
+
1137
+ const oldVersion = data.version || 0;
1138
+ const newVersion = typeof version === "number" ? version : Math.max(1, oldVersion);
1139
+ dbObj.version = newVersion;
1140
+ let currentTx = null;
1141
+
1142
+ req.result = dbObj;
1143
+ req.readyState = "done";
1144
+ if (newVersion > oldVersion) {
1145
+ data.version = newVersion;
1146
+ // upgrade トランザクション (versionchange)
1147
+ currentTx = {
1148
+ "oncomplete": null, "onerror": null, "onabort": null,
1149
+ "db": dbObj, "mode": "versionchange", "error": null,
1150
+ objectStore(s) { return storeApi(s, currentTx); },
1151
+ abort() {}, commit() {}
1152
+ };
1153
+ if (typeof req.onupgradeneeded === "function") {
1154
+ req.onupgradeneeded({
1155
+ "target": req, "oldVersion": oldVersion, "newVersion": newVersion
1156
+ });
1157
+ }
1158
+ saveDb(name, data);
1159
+ currentTx = null;
1160
+ }
1161
+ if (typeof req.onsuccess === "function") {
1162
+ req.onsuccess({ "target": req });
1163
+ }
1164
+ });
1165
+ return req;
1166
+ },
1167
+ deleteDatabase(name) {
1168
+ const req = makeRequest();
1169
+ global.queueMicrotask(function () {
1170
+ try {
1171
+ global.__next2d_storage_save("idb_" + name, "");
1172
+ fireSuccess(req);
1173
+ } catch (e) {
1174
+ fireError(req, e);
1175
+ }
1176
+ });
1177
+ return req;
1178
+ },
1179
+ cmp(a, b) { return cmpKeys(a, b); }
1180
+ };
1181
+ })(globalThis);
1182
+ )JS";
1183
+ const char* const kIndexedDBChunks[] = {
1184
+ kIndexedDB1, kIndexedDB2, kIndexedDB3,
1185
+ };
1186
+
767
1187
  const char* const kPolyfillSections[] = {
768
1188
  kPolyfills1,
769
1189
  kPolyfills2,
@@ -786,6 +1206,21 @@ void InstallPolyfills(v8::Isolate* isolate, v8::Local<v8::Context> context)
786
1206
  (void) script->Run(context);
787
1207
  }
788
1208
  }
1209
+
1210
+ // indexedDB は 3 リテラルを連結して 1 スクリプトとして評価する
1211
+ // (単一 IIFE のため分割評価できない)。
1212
+ {
1213
+ std::string idb;
1214
+ for (const char* chunk : kIndexedDBChunks) {
1215
+ idb += chunk;
1216
+ }
1217
+ v8::TryCatch tc(isolate);
1218
+ v8::Local<v8::String> src = v8util::Str(isolate, idb);
1219
+ v8::Local<v8::Script> script;
1220
+ if (v8::Script::Compile(context, src).ToLocal(&script)) {
1221
+ (void) script->Run(context);
1222
+ }
1223
+ }
789
1224
  }
790
1225
 
791
1226
  } // namespace next2d