@mintjamsinc/ichigojs 0.1.79 → 0.1.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -0
- package/dist/ichigo.cjs +152 -79
- package/dist/ichigo.cjs.map +1 -1
- package/dist/ichigo.esm.js +152 -79
- package/dist/ichigo.esm.js.map +1 -1
- package/dist/ichigo.esm.min.js +1 -1
- package/dist/ichigo.min.cjs +1 -1
- package/dist/ichigo.umd.js +152 -79
- package/dist/ichigo.umd.js.map +1 -1
- package/dist/ichigo.umd.min.js +1 -1
- package/dist/types/ichigo/VNode.d.ts +20 -0
- package/dist/types/ichigo/components/IchigoElement.d.ts +43 -22
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -957,6 +957,18 @@ defineComponent('my-list', {
|
|
|
957
957
|
</my-list>
|
|
958
958
|
```
|
|
959
959
|
|
|
960
|
+
> **Ordering:** a component's `<template>` must be in the document, and
|
|
961
|
+
> `defineComponent()` must have run, before the application that uses the
|
|
962
|
+
> component is mounted. The application expands a component while it compiles
|
|
963
|
+
> it — after installing the host context, delivering the prop values and
|
|
964
|
+
> compiling the projected slot content in the parent scope — so a component
|
|
965
|
+
> compiled without its template is reported as a console warning and left
|
|
966
|
+
> empty. It is never retried: an app that renders anyway is an app whose bug
|
|
967
|
+
> cannot be found. A hyphenated tag that nothing has registered (a forgotten
|
|
968
|
+
> import, a typo) is reported the same way. Components therefore have to live
|
|
969
|
+
> inside a mounted application; one placed outside any application is never
|
|
970
|
+
> expanded.
|
|
971
|
+
|
|
960
972
|
**Props:**
|
|
961
973
|
|
|
962
974
|
- Declared via the `props` array, or as a record with per-prop options:
|
package/dist/ichigo.cjs
CHANGED
|
@@ -9224,8 +9224,9 @@
|
|
|
9224
9224
|
this.#scopeBindings.setLocal(this.#entry.scopeVar, this.#buildSlotProps());
|
|
9225
9225
|
}
|
|
9226
9226
|
// Clone the scoped template's content and create its VNode tree BEFORE
|
|
9227
|
-
// inserting into the DOM, so
|
|
9228
|
-
//
|
|
9227
|
+
// inserting into the DOM, so its directives run against a subtree
|
|
9228
|
+
// nothing else can observe yet. Components inside are expanded
|
|
9229
|
+
// afterwards, once the content is in the document.
|
|
9229
9230
|
// (Created through the parent application to keep the VNode import
|
|
9230
9231
|
// type-only; a direct `new VNode` here would close a module cycle
|
|
9231
9232
|
// VNode -> VDirectiveManager -> VSlotOutletDirective -> VNode.)
|
|
@@ -9244,6 +9245,8 @@
|
|
|
9244
9245
|
slot.replaceWith(fragment);
|
|
9245
9246
|
// Initial rendering
|
|
9246
9247
|
this.#contentVNode.forceUpdate();
|
|
9248
|
+
// Components in the slot content expand once it is in the document.
|
|
9249
|
+
this.#contentVNode.expandComponents();
|
|
9247
9250
|
}
|
|
9248
9251
|
/**
|
|
9249
9252
|
* Re-evaluates the slot props (component-side change) and pushes them into
|
|
@@ -9860,6 +9863,31 @@
|
|
|
9860
9863
|
* remove the entire fragment atomically.
|
|
9861
9864
|
*/
|
|
9862
9865
|
#fragmentRange;
|
|
9866
|
+
/**
|
|
9867
|
+
* Custom element tags already reported by
|
|
9868
|
+
* {@link VNode.#warnIfUnknownCustomElement}.
|
|
9869
|
+
*/
|
|
9870
|
+
static #warnedUnknownTags = new Set();
|
|
9871
|
+
/**
|
|
9872
|
+
* Warns about a hyphenated tag that no one has registered — a forgotten
|
|
9873
|
+
* import, a missing defineComponent() call, or a typo in the tag name.
|
|
9874
|
+
* Such an element is compiled as an ordinary element and renders its own
|
|
9875
|
+
* children, which looks enough like a working page to cost an afternoon.
|
|
9876
|
+
* Reported once per tag; third-party custom elements are recognised
|
|
9877
|
+
* through the registry the browser keeps.
|
|
9878
|
+
*/
|
|
9879
|
+
static #warnIfUnknownCustomElement(nodeName) {
|
|
9880
|
+
const tag = nodeName.toLowerCase();
|
|
9881
|
+
if (!tag.includes('-') || VNode.#warnedUnknownTags.has(tag)) {
|
|
9882
|
+
return;
|
|
9883
|
+
}
|
|
9884
|
+
if (typeof customElements !== 'undefined' && customElements.get(tag)) {
|
|
9885
|
+
return;
|
|
9886
|
+
}
|
|
9887
|
+
VNode.#warnedUnknownTags.add(tag);
|
|
9888
|
+
console.warn(`[ichigo] <${tag}>: unknown custom element — nothing has registered this tag.`
|
|
9889
|
+
+ ' Did you forget to import its module or call defineComponent(), or is the tag misspelled?');
|
|
9890
|
+
}
|
|
9863
9891
|
/**
|
|
9864
9892
|
* Creates an instance of the virtual node.
|
|
9865
9893
|
* @param args The initialization arguments for the virtual node.
|
|
@@ -9891,6 +9919,9 @@
|
|
|
9891
9919
|
if (isComponent) {
|
|
9892
9920
|
element._ichigoHost = { vNode: this };
|
|
9893
9921
|
}
|
|
9922
|
+
else {
|
|
9923
|
+
VNode.#warnIfUnknownCustomElement(this.#nodeName);
|
|
9924
|
+
}
|
|
9894
9925
|
// Initialize child virtual nodes
|
|
9895
9926
|
this.#childVNodes = [];
|
|
9896
9927
|
// Initialize directive manager
|
|
@@ -9924,16 +9955,6 @@
|
|
|
9924
9955
|
});
|
|
9925
9956
|
}
|
|
9926
9957
|
}
|
|
9927
|
-
// A component element this application compiled but that has not
|
|
9928
|
-
// expanded yet — it deferred to us because an ancestor component
|
|
9929
|
-
// was attaching its template, or its <template> was not loaded when
|
|
9930
|
-
// it connected — is expanded now: the host context is installed,
|
|
9931
|
-
// the directives above delivered the prop values, and any authored
|
|
9932
|
-
// slot content was compiled in this scope. A template source
|
|
9933
|
-
// (v-if / v-for) is left alone; its clones expand on insertion.
|
|
9934
|
-
if (isComponent && !this.#templatized && !isExpandedComponent) {
|
|
9935
|
-
element._ichigoTryExpand?.();
|
|
9936
|
-
}
|
|
9937
9958
|
// After creating child nodes, call onMounted for directives that do not templatize
|
|
9938
9959
|
if (!this.#templatized) {
|
|
9939
9960
|
// animation frame to ensure DOM is updated
|
|
@@ -9959,6 +9980,37 @@
|
|
|
9959
9980
|
// Register this node as a dependent of the parent node, if any
|
|
9960
9981
|
this.#closers = this.#parentVNode?.addDependent(this);
|
|
9961
9982
|
}
|
|
9983
|
+
/**
|
|
9984
|
+
* Expands the component elements this application compiled in this subtree
|
|
9985
|
+
* but has not expanded yet, parents before their children.
|
|
9986
|
+
*
|
|
9987
|
+
* This is the ONLY thing that expands a component. It is deliberately a
|
|
9988
|
+
* separate step from compilation rather than a call at the end of the
|
|
9989
|
+
* constructor: a v-if / v-for clone is compiled while it is still detached
|
|
9990
|
+
* (so that its directives run before anything can observe a half-built
|
|
9991
|
+
* subtree), and expanding a detached element would mount a component
|
|
9992
|
+
* application outside the document. The callers therefore compile, insert,
|
|
9993
|
+
* and then call this — see VApplication.mount, VConditionalDirective,
|
|
9994
|
+
* VForDirective and VSlotOutletDirective.
|
|
9995
|
+
*
|
|
9996
|
+
* Walking only {@link childVNodes} keeps the traversal inside this
|
|
9997
|
+
* application: the internals of an expanded component were never compiled
|
|
9998
|
+
* here, so they are not reachable and are left to their own application.
|
|
9999
|
+
* A templatized node is skipped whole — it is a template source, and its
|
|
10000
|
+
* clones expand when they are inserted.
|
|
10001
|
+
*/
|
|
10002
|
+
expandComponents() {
|
|
10003
|
+
if (this.#templatized) {
|
|
10004
|
+
return;
|
|
10005
|
+
}
|
|
10006
|
+
if (this.#nodeType === Node.ELEMENT_NODE && IchigoElementRegistry.has(this.#nodeName)) {
|
|
10007
|
+
const element = this.#node;
|
|
10008
|
+
if (element._ichigoExpanded !== true) {
|
|
10009
|
+
element._ichigoExpand?.();
|
|
10010
|
+
}
|
|
10011
|
+
}
|
|
10012
|
+
this.#childVNodes?.forEach(child => child.expandComponents());
|
|
10013
|
+
}
|
|
9962
10014
|
/**
|
|
9963
10015
|
* The application instance associated with this virtual node.
|
|
9964
10016
|
*/
|
|
@@ -10759,11 +10811,10 @@
|
|
|
10759
10811
|
}
|
|
10760
10812
|
// Clone the original node and create a new VNode for it
|
|
10761
10813
|
const clone = this.#cloneNode();
|
|
10762
|
-
//
|
|
10763
|
-
//
|
|
10764
|
-
//
|
|
10765
|
-
//
|
|
10766
|
-
// element's internal template content as its own VNode tree.
|
|
10814
|
+
// Compile the clone BEFORE inserting it, so its directives run against a
|
|
10815
|
+
// subtree nothing else can observe yet (v-if inside creates anchors and
|
|
10816
|
+
// detaches template sources). Components inside are expanded afterwards,
|
|
10817
|
+
// once the clone is in the document — see expandComponents() below.
|
|
10767
10818
|
// Pass the current bindings to ensure loop variables from v-for are available
|
|
10768
10819
|
const vNode = new VNode({
|
|
10769
10820
|
node: clone,
|
|
@@ -10778,12 +10829,16 @@
|
|
|
10778
10829
|
vNode.fragmentRange = VFragmentRange.insert(anchorParent, nextSibling, 'vif-fragment', clone);
|
|
10779
10830
|
this.#renderedVNode = vNode;
|
|
10780
10831
|
this.#renderedVNode.forceUpdate();
|
|
10832
|
+
this.#renderedVNode.expandComponents();
|
|
10781
10833
|
return;
|
|
10782
10834
|
}
|
|
10783
10835
|
const nodeToInsert = vNode.anchorNode || clone;
|
|
10784
10836
|
anchorParent?.insertBefore(nodeToInsert, nextSibling);
|
|
10785
10837
|
this.#renderedVNode = vNode;
|
|
10786
10838
|
this.#renderedVNode.forceUpdate();
|
|
10839
|
+
// Components expand once the clone is in the document and its bindings
|
|
10840
|
+
// have been applied, so they mount connected and with their props set.
|
|
10841
|
+
this.#renderedVNode.expandComponents();
|
|
10787
10842
|
}
|
|
10788
10843
|
/**
|
|
10789
10844
|
* Removes the node from the DOM.
|
|
@@ -11240,6 +11295,7 @@
|
|
|
11240
11295
|
vNode.fragmentRange = range;
|
|
11241
11296
|
newRenderedItems.push({ key, vNode });
|
|
11242
11297
|
vNode.forceUpdate();
|
|
11298
|
+
vNode.expandComponents();
|
|
11243
11299
|
prevNode = range.lastNode;
|
|
11244
11300
|
continue;
|
|
11245
11301
|
}
|
|
@@ -11254,6 +11310,10 @@
|
|
|
11254
11310
|
}
|
|
11255
11311
|
newRenderedItems.push({ key, vNode });
|
|
11256
11312
|
vNode.forceUpdate();
|
|
11313
|
+
// Components expand once the clone is in the document and its
|
|
11314
|
+
// bindings have been applied, so they mount connected and with
|
|
11315
|
+
// their props set.
|
|
11316
|
+
vNode.expandComponents();
|
|
11257
11317
|
}
|
|
11258
11318
|
else {
|
|
11259
11319
|
// Reuse existing item
|
|
@@ -14823,6 +14883,10 @@
|
|
|
14823
14883
|
});
|
|
14824
14884
|
// Initial rendering
|
|
14825
14885
|
this.#vNode.update();
|
|
14886
|
+
// Expand the components in the tree. Last, so every component element
|
|
14887
|
+
// has been compiled and has had its prop values delivered before its
|
|
14888
|
+
// template is cloned in. See VNode.expandComponents.
|
|
14889
|
+
this.#vNode.expandComponents();
|
|
14826
14890
|
// Remove v-cloak attributes after mounting
|
|
14827
14891
|
// v-cloak is used to hide un-compiled template until the app is ready
|
|
14828
14892
|
// Usage: Add CSS rule "[v-cloak] { display: none; }" to hide elements with v-cloak
|
|
@@ -15387,6 +15451,19 @@
|
|
|
15387
15451
|
/**
|
|
15388
15452
|
* Base class for ichigo.js-backed Web Components (Light DOM, no Shadow DOM).
|
|
15389
15453
|
*
|
|
15454
|
+
* Expansion — cloning the template into this element — is driven by the
|
|
15455
|
+
* application that owns the element, not by connectedCallback: VNode compiles
|
|
15456
|
+
* the element first (installing the host context, delivering the prop values
|
|
15457
|
+
* and compiling the authored slot content in the owner's scope) and only then
|
|
15458
|
+
* calls {@link _ichigoExpand}. That leaves connectedCallback a single job,
|
|
15459
|
+
* re-attachment; see there.
|
|
15460
|
+
*
|
|
15461
|
+
* The component's <template> must be present in the document, and
|
|
15462
|
+
* defineComponent() must have run, before the owning application is mounted.
|
|
15463
|
+
* That is the application's responsibility. A missing template is reported as
|
|
15464
|
+
* a warning and the element is left empty — it is never silently retried,
|
|
15465
|
+
* because an app that renders anyway is an app whose bug cannot be found.
|
|
15466
|
+
*
|
|
15390
15467
|
* Mount timing:
|
|
15391
15468
|
* - If the component declares no props, the VApplication is mounted synchronously
|
|
15392
15469
|
* at the end of connectedCallback (the template is known, no props to wait for).
|
|
@@ -15446,38 +15523,42 @@
|
|
|
15446
15523
|
get _ichigoScopedSlots() {
|
|
15447
15524
|
return this.#slotCache?.scopedSlots;
|
|
15448
15525
|
}
|
|
15526
|
+
/**
|
|
15527
|
+
* Handles RE-attachment, and nothing else.
|
|
15528
|
+
*
|
|
15529
|
+
* A first connection is not what expands this element: the owning
|
|
15530
|
+
* application does that from {@link _ichigoExpand} once it has compiled the
|
|
15531
|
+
* element. So a connection event on a not-yet-expanded element means the
|
|
15532
|
+
* owner has not reached it yet, and the right thing to do is nothing. That
|
|
15533
|
+
* covers every case the old guards enumerated one by one — a v-if / v-for
|
|
15534
|
+
* template source, an element inside an enclosing component's template
|
|
15535
|
+
* clone, a clone inserted before its directive expands it.
|
|
15536
|
+
*
|
|
15537
|
+
* An expanded element whose mount root has been torn down, on the other
|
|
15538
|
+
* hand, was disconnected and is now coming back: v-for reorders relocate
|
|
15539
|
+
* the host element, which disconnects and reconnects it. Its children are
|
|
15540
|
+
* the previous expansion, not authored slot content, so it re-expands from
|
|
15541
|
+
* the cached slot nodes. Testing the mount root rather than the expansion
|
|
15542
|
+
* flag alone keeps this from firing on an element that was expanded while
|
|
15543
|
+
* detached and is being inserted for the first time.
|
|
15544
|
+
*/
|
|
15449
15545
|
connectedCallback() {
|
|
15450
|
-
|
|
15451
|
-
|
|
15452
|
-
// family / v-for) is a template source: the owning application clones it
|
|
15453
|
-
// and inserts the clones (whose directive attributes are stripped during
|
|
15454
|
-
// clone compilation, so the clones expand normally). Expanding the source
|
|
15455
|
-
// here would bake the expansion into every clone, and the clone's
|
|
15456
|
-
// internals would then be compiled in the parent scope.
|
|
15457
|
-
if (this.hasAttribute(StandardDirectiveName.V_IF) ||
|
|
15458
|
-
this.hasAttribute(StandardDirectiveName.V_ELSE_IF) ||
|
|
15459
|
-
this.hasAttribute(StandardDirectiveName.V_ELSE) ||
|
|
15460
|
-
this.hasAttribute(StandardDirectiveName.V_FOR)) {
|
|
15461
|
-
return;
|
|
15462
|
-
}
|
|
15463
|
-
// --- 0a2. Guard: wait to be compiled by the owning application ---
|
|
15464
|
-
// This element came from an enclosing component's template clone and no
|
|
15465
|
-
// application has compiled it yet: the enclosing component's appendChild
|
|
15466
|
-
// connected it before that component's VApplication existed. Expanding
|
|
15467
|
-
// now would capture the slot content and mount before the owning
|
|
15468
|
-
// application installs the host context (_ichigoHost) and delivers the
|
|
15469
|
-
// prop values — scoped slot outlets would find no parent scope and
|
|
15470
|
-
// silently fall back to their default content. The owning application
|
|
15471
|
-
// expands this element while it compiles it (VNode ->
|
|
15472
|
-
// _ichigoTryExpand()), by which time both are in place.
|
|
15473
|
-
//
|
|
15474
|
-
// Only template-owned elements defer: authored slot content placed
|
|
15475
|
-
// inside a component by an application is not part of the template, so
|
|
15476
|
-
// it keeps expanding immediately (and is redistributed by the enclosing
|
|
15477
|
-
// component on every re-expansion).
|
|
15478
|
-
if (this._ichigoTemplateOwned === true && !this._ichigoHost) {
|
|
15479
|
-
return;
|
|
15546
|
+
if (this._ichigoExpanded && !this.#mountRoot) {
|
|
15547
|
+
this._ichigoExpand();
|
|
15480
15548
|
}
|
|
15549
|
+
}
|
|
15550
|
+
/**
|
|
15551
|
+
* Expands this element's template. Called by the application that owns the
|
|
15552
|
+
* element, right after it compiled it (VNode.expandComponents): the host
|
|
15553
|
+
* context is installed, the prop bindings have delivered their values and
|
|
15554
|
+
* the authored slot content has been compiled in the owner's scope, so the
|
|
15555
|
+
* capture below sees owner-compiled nodes and the component's own
|
|
15556
|
+
* application starts with its props already in hand.
|
|
15557
|
+
*
|
|
15558
|
+
* Also called by {@link connectedCallback} to rebuild the expansion after a
|
|
15559
|
+
* move.
|
|
15560
|
+
*/
|
|
15561
|
+
_ichigoExpand() {
|
|
15481
15562
|
// --- 0b. Reclaim prop values assigned before upgrade ---
|
|
15482
15563
|
// A plain property set on the instance (e.g. by a binding delivered
|
|
15483
15564
|
// while the element was detached or not yet upgraded) is an own data
|
|
@@ -15493,13 +15574,15 @@
|
|
|
15493
15574
|
this[prop] = value;
|
|
15494
15575
|
}
|
|
15495
15576
|
}
|
|
15496
|
-
// --- 0c.
|
|
15497
|
-
//
|
|
15498
|
-
//
|
|
15499
|
-
//
|
|
15500
|
-
//
|
|
15577
|
+
// --- 0c. Resolve the template ---
|
|
15578
|
+
// Having the <template> in the document by now is the application's
|
|
15579
|
+
// contract (see the class doc). Report the breach and leave the element
|
|
15580
|
+
// empty: rendering something plausible here would hide the bug at the
|
|
15581
|
+
// one moment it is still cheap to find.
|
|
15501
15582
|
const templateEl = document.querySelector(ctor._template);
|
|
15502
15583
|
if (!templateEl || !(templateEl instanceof HTMLTemplateElement)) {
|
|
15584
|
+
IchigoElement.#warnOncePerTag(this.tagName, `template '${ctor._template}' was not found in the document. `
|
|
15585
|
+
+ 'Inject the <template> and call defineComponent() before mounting the application.');
|
|
15503
15586
|
return;
|
|
15504
15587
|
}
|
|
15505
15588
|
// --- 1. Capture slot content (first connection only) ---
|
|
@@ -15551,18 +15634,6 @@
|
|
|
15551
15634
|
// --- 2. Clone the component template ---
|
|
15552
15635
|
const fragment = templateEl.content.cloneNode(true);
|
|
15553
15636
|
const root = this.#findRootElement(fragment);
|
|
15554
|
-
// Mark the component elements the template brought in, so they let this
|
|
15555
|
-
// component's application compile them before they expand (guard 0a2).
|
|
15556
|
-
// Marked before slot distribution, so authored slot content — which is
|
|
15557
|
-
// not part of the template — keeps expanding under the application that
|
|
15558
|
-
// authored it. Elements inside a nested <template> are not marked: they
|
|
15559
|
-
// are inert until a directive clones them, and the clone is compiled
|
|
15560
|
-
// before it is inserted.
|
|
15561
|
-
for (const element of Array.from(fragment.querySelectorAll('*'))) {
|
|
15562
|
-
if (IchigoElementRegistry.has(element.tagName)) {
|
|
15563
|
-
element._ichigoTemplateOwned = true;
|
|
15564
|
-
}
|
|
15565
|
-
}
|
|
15566
15637
|
// --- 3. Distribute named slot content ---
|
|
15567
15638
|
for (const [name, nodes] of namedNodes) {
|
|
15568
15639
|
const slot = root.querySelector(`slot[name="${name}"]`);
|
|
@@ -15600,21 +15671,6 @@
|
|
|
15600
15671
|
this.#app = undefined;
|
|
15601
15672
|
}
|
|
15602
15673
|
}
|
|
15603
|
-
/**
|
|
15604
|
-
* Expands this element's template on behalf of the application that owns
|
|
15605
|
-
* it. Called by VNode right after it compiled this element — the host
|
|
15606
|
-
* context is installed and the prop bindings have delivered their values,
|
|
15607
|
-
* so a component that deferred its expansion (guard 0a2, or a template
|
|
15608
|
-
* that was not loaded when it connected) can now expand in the right
|
|
15609
|
-
* order. A no-op when the element is already expanded or not connected
|
|
15610
|
-
* (a template-source clone expands when it is inserted).
|
|
15611
|
-
*/
|
|
15612
|
-
_ichigoTryExpand() {
|
|
15613
|
-
if (this._ichigoExpanded || !this.isConnected) {
|
|
15614
|
-
return;
|
|
15615
|
-
}
|
|
15616
|
-
this.connectedCallback();
|
|
15617
|
-
}
|
|
15618
15674
|
/**
|
|
15619
15675
|
* Called by the property setters generated by defineComponent().
|
|
15620
15676
|
* Before mount: stores the value and schedules a mount microtask.
|
|
@@ -15841,6 +15897,23 @@
|
|
|
15841
15897
|
#warn(message) {
|
|
15842
15898
|
console.warn(`[ichigo] <${this.tagName.toLowerCase()}>: ${message}`);
|
|
15843
15899
|
}
|
|
15900
|
+
/**
|
|
15901
|
+
* Tags already reported by {@link #warnOncePerTag}.
|
|
15902
|
+
*/
|
|
15903
|
+
static #warnedTags = new Set();
|
|
15904
|
+
/**
|
|
15905
|
+
* Reports a per-component-type problem once. A missing template breaks
|
|
15906
|
+
* every instance, and a v-for over a hundred rows would otherwise print a
|
|
15907
|
+
* hundred identical lines and bury the first one.
|
|
15908
|
+
*/
|
|
15909
|
+
static #warnOncePerTag(tagName, message) {
|
|
15910
|
+
const tag = tagName.toLowerCase();
|
|
15911
|
+
if (IchigoElement.#warnedTags.has(tag)) {
|
|
15912
|
+
return;
|
|
15913
|
+
}
|
|
15914
|
+
IchigoElement.#warnedTags.add(tag);
|
|
15915
|
+
console.warn(`[ichigo] <${tag}>: ${message}`);
|
|
15916
|
+
}
|
|
15844
15917
|
}
|
|
15845
15918
|
|
|
15846
15919
|
// Copyright (c) 2025 MintJams Inc. Licensed under MIT License.
|