@needle-tools/engine 5.1.11 → 5.1.13
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/CHANGELOG.md +6 -1
- package/SKILL.md +1 -1
- package/dist/gltf-progressive-BnLTsXZw.umd.cjs +10 -0
- package/dist/gltf-progressive-D6RT9IkB.min.js +10 -0
- package/dist/{gltf-progressive-B4DQEeBz.js → gltf-progressive-DaeP5KBr.js} +527 -514
- package/dist/{needle-engine.bundle-n5YALNtc.umd.cjs → needle-engine.bundle-B_ZctaDt.umd.cjs} +146 -146
- package/dist/{needle-engine.bundle-d-dLyN3s.min.js → needle-engine.bundle-DR967VjU.min.js} +145 -145
- package/dist/{needle-engine.bundle-ue2Lw4bF.js → needle-engine.bundle-DTvJ1zCJ.js} +7302 -7153
- package/dist/needle-engine.d.ts +23 -15
- package/dist/needle-engine.js +225 -226
- package/dist/needle-engine.min.js +1 -1
- package/dist/needle-engine.umd.cjs +1 -1
- package/lib/engine/api.d.ts +4 -3
- package/lib/engine/api.js +3 -9
- package/lib/engine/api.js.map +1 -1
- package/lib/engine/engine_addressables.d.ts +32 -1
- package/lib/engine/engine_addressables.js +71 -9
- package/lib/engine/engine_addressables.js.map +1 -1
- package/lib/engine/engine_application.d.ts +2 -0
- package/lib/engine/engine_application.js +7 -3
- package/lib/engine/engine_application.js.map +1 -1
- package/lib/engine/engine_context.js +25 -1
- package/lib/engine/engine_context.js.map +1 -1
- package/lib/engine/engine_init.js +2 -2
- package/lib/engine/engine_init.js.map +1 -1
- package/lib/engine/engine_input.js +1 -1
- package/lib/engine/engine_input.js.map +1 -1
- package/lib/engine/engine_license.d.ts +7 -7
- package/lib/engine/engine_license.js +121 -78
- package/lib/engine/engine_license.js.map +1 -1
- package/lib/engine/engine_lifecycle_functions_internal.js +20 -2
- package/lib/engine/engine_lifecycle_functions_internal.js.map +1 -1
- package/lib/engine/engine_networking_blob.js +3 -3
- package/lib/engine/engine_networking_streams.d.ts +3 -0
- package/lib/engine/engine_networking_streams.js +64 -17
- package/lib/engine/engine_networking_streams.js.map +1 -1
- package/lib/engine/engine_utils_qrcode.js +2 -2
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +5 -0
- package/lib/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js.map +1 -1
- package/lib/engine/webcomponents/fonts.d.ts +1 -1
- package/lib/engine/webcomponents/fonts.js +16 -1
- package/lib/engine/webcomponents/fonts.js.map +1 -1
- package/lib/engine/webcomponents/icons.js +8 -1
- package/lib/engine/webcomponents/icons.js.map +1 -1
- package/lib/engine/webcomponents/needle menu/needle-menu-spatial.js +2 -2
- package/lib/engine/webcomponents/needle menu/needle-menu.d.ts +5 -0
- package/lib/engine/webcomponents/needle menu/needle-menu.js +34 -16
- package/lib/engine/webcomponents/needle menu/needle-menu.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.d.ts +1 -0
- package/lib/engine/webcomponents/needle-engine.js +18 -6
- package/lib/engine/webcomponents/needle-engine.js.map +1 -1
- package/lib/engine/webcomponents/needle-engine.loading.d.ts +2 -0
- package/lib/engine/webcomponents/needle-engine.loading.js +12 -2
- package/lib/engine/webcomponents/needle-engine.loading.js.map +1 -1
- package/lib/engine/xr/TempXRContext.js +2 -2
- package/lib/engine-components/export/usdz/USDZExporter.js +4 -4
- package/lib/engine-components/export/usdz/USDZExporter.js.map +1 -1
- package/package.json +2 -2
- package/plugins/common/license.js +4 -4
- package/plugins/vite/license.js +4 -4
- package/src/engine/api.ts +5 -3
- package/src/engine/engine_addressables.ts +75 -9
- package/src/engine/engine_application.ts +7 -2
- package/src/engine/engine_context.ts +25 -1
- package/src/engine/engine_init.ts +2 -2
- package/src/engine/engine_input.ts +2 -2
- package/src/engine/engine_license.ts +113 -75
- package/src/engine/engine_lifecycle_functions_internal.ts +21 -2
- package/src/engine/engine_networking_blob.ts +3 -3
- package/src/engine/engine_networking_streams.ts +61 -17
- package/src/engine/engine_utils_qrcode.ts +2 -2
- package/src/engine/physics/workers/mesh-bvh/GenerateMeshBVHWorker.js +8 -1
- package/src/engine/webcomponents/fonts.ts +16 -1
- package/src/engine/webcomponents/icons.ts +8 -1
- package/src/engine/webcomponents/needle menu/needle-menu-spatial.ts +2 -2
- package/src/engine/webcomponents/needle menu/needle-menu.ts +33 -15
- package/src/engine/webcomponents/needle-engine.loading.ts +19 -7
- package/src/engine/webcomponents/needle-engine.ts +16 -6
- package/src/engine/xr/TempXRContext.ts +2 -2
- package/src/engine-components/export/usdz/USDZExporter.ts +4 -4
- package/dist/gltf-progressive-CdjpU5eP.min.js +0 -10
- package/dist/gltf-progressive-DeZioRZe.umd.cjs +0 -10
|
@@ -7,21 +7,53 @@ import { SSR } from "./engine_ssr.js";
|
|
|
7
7
|
import type { IContext } from "./engine_types.js";
|
|
8
8
|
import { DeviceUtilities, getParam } from "./engine_utils.js";
|
|
9
9
|
|
|
10
|
+
// License UI polling must stop when its context goes away; otherwise timers retain scenes.
|
|
11
|
+
const licenseIntervals = new WeakMap<IContext, Set<ReturnType<typeof setInterval>>>();
|
|
12
|
+
const licenseTimeouts = new WeakMap<IContext, Set<ReturnType<typeof setTimeout>>>();
|
|
13
|
+
const disposedLicenseContexts = new WeakSet<IContext>();
|
|
14
|
+
ContextRegistry.registerCallback(ContextEvent.ContextDestroying, ({ context }) => {
|
|
15
|
+
disposedLicenseContexts.add(context);
|
|
16
|
+
for (const timer of licenseIntervals.get(context) ?? []) clearInterval(timer);
|
|
17
|
+
licenseIntervals.delete(context);
|
|
18
|
+
for (const timer of licenseTimeouts.get(context) ?? []) clearTimeout(timer);
|
|
19
|
+
licenseTimeouts.delete(context);
|
|
20
|
+
});
|
|
21
|
+
function setLicenseInterval(ctx: IContext, callback: () => void, delay: number) {
|
|
22
|
+
if (disposedLicenseContexts.has(ctx)) return undefined;
|
|
23
|
+
let timers = licenseIntervals.get(ctx);
|
|
24
|
+
if (!timers) licenseIntervals.set(ctx, timers = new Set());
|
|
25
|
+
const timer = setInterval(callback, delay);
|
|
26
|
+
timers.add(timer);
|
|
27
|
+
return timer;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function setLicenseTimeout(ctx: IContext, callback: () => void, delay: number) {
|
|
31
|
+
if (disposedLicenseContexts.has(ctx)) return undefined;
|
|
32
|
+
let timers = licenseTimeouts.get(ctx);
|
|
33
|
+
if (!timers) licenseTimeouts.set(ctx, timers = new Set());
|
|
34
|
+
const timer = setTimeout(() => {
|
|
35
|
+
timers.delete(timer);
|
|
36
|
+
callback();
|
|
37
|
+
}, delay);
|
|
38
|
+
timers.add(timer);
|
|
39
|
+
return timer;
|
|
40
|
+
}
|
|
41
|
+
|
|
10
42
|
const debug = getParam("__debuglic__");
|
|
11
43
|
|
|
12
|
-
const _$
|
|
44
|
+
const _$zQIZ: ((result: boolean) => void)[] = [];
|
|
13
45
|
|
|
14
46
|
// DO NOT EDIT MANUALLY
|
|
15
|
-
let
|
|
47
|
+
let __VqzR: string = "";
|
|
16
48
|
// eslint-disable-next-line prefer-const
|
|
17
|
-
let
|
|
49
|
+
let $OQRl: string = "";
|
|
18
50
|
|
|
19
51
|
if (debug) {
|
|
20
|
-
console.log("License Type: " +
|
|
21
|
-
if (
|
|
22
|
-
console.log("License JWT: " +
|
|
52
|
+
console.log("License Type: " + __VqzR);
|
|
53
|
+
if ($OQRl) {
|
|
54
|
+
console.log("License JWT: " + $OQRl);
|
|
23
55
|
try {
|
|
24
|
-
const payload = JSON.parse(atob(
|
|
56
|
+
const payload = JSON.parse(atob($OQRl.split(".")[1].replace(/-/g, '+').replace(/_/g, '/')));
|
|
25
57
|
console.log("License JWT payload:", payload);
|
|
26
58
|
}
|
|
27
59
|
catch { console.log("License JWT payload: (failed to decode)"); }
|
|
@@ -32,8 +64,8 @@ if (debug) {
|
|
|
32
64
|
}
|
|
33
65
|
|
|
34
66
|
/** @internal */
|
|
35
|
-
export function
|
|
36
|
-
switch (
|
|
67
|
+
export function __csRKN() {
|
|
68
|
+
switch (__VqzR) {
|
|
37
69
|
case "pro":
|
|
38
70
|
case "enterprise":
|
|
39
71
|
return true;
|
|
@@ -42,8 +74,8 @@ export function oMxKl() {
|
|
|
42
74
|
}
|
|
43
75
|
|
|
44
76
|
/** @internal */
|
|
45
|
-
export function
|
|
46
|
-
switch (
|
|
77
|
+
export function _qxvTsQJJ() {
|
|
78
|
+
switch (__VqzR) {
|
|
47
79
|
case "indie":
|
|
48
80
|
return true;
|
|
49
81
|
}
|
|
@@ -51,8 +83,8 @@ export function __nlZsk() {
|
|
|
51
83
|
}
|
|
52
84
|
|
|
53
85
|
/** @internal */
|
|
54
|
-
export function
|
|
55
|
-
switch (
|
|
86
|
+
export function $yVfpT() {
|
|
87
|
+
switch (__VqzR) {
|
|
56
88
|
case "edu":
|
|
57
89
|
return true;
|
|
58
90
|
}
|
|
@@ -60,20 +92,26 @@ export function _VRCiX() {
|
|
|
60
92
|
}
|
|
61
93
|
|
|
62
94
|
/** @internal */
|
|
63
|
-
export function
|
|
64
|
-
return
|
|
95
|
+
export function __PemN() {
|
|
96
|
+
return __csRKN() || _qxvTsQJJ() || $yVfpT();
|
|
65
97
|
}
|
|
66
98
|
|
|
67
99
|
|
|
68
100
|
/** @internal */
|
|
69
|
-
export function $
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
101
|
+
export function _$EPASa(cb: (result: boolean) => void) {
|
|
102
|
+
if (__csRKN() || _qxvTsQJJ() || $yVfpT()) {
|
|
103
|
+
cb(true);
|
|
104
|
+
return () => {};
|
|
105
|
+
}
|
|
106
|
+
_$zQIZ.push(cb);
|
|
107
|
+
return () => {
|
|
108
|
+
const index = _$zQIZ.indexOf(cb);
|
|
109
|
+
if (index >= 0) _$zQIZ.splice(index, 1);
|
|
110
|
+
};
|
|
73
111
|
}
|
|
74
112
|
|
|
75
|
-
function
|
|
76
|
-
for (const cb of _$
|
|
113
|
+
function _SUQSFYGK(result: boolean) {
|
|
114
|
+
for (const cb of _$zQIZ) {
|
|
77
115
|
try {
|
|
78
116
|
cb(result);
|
|
79
117
|
}
|
|
@@ -88,7 +126,7 @@ function $AeqIgsC(result: boolean) {
|
|
|
88
126
|
|
|
89
127
|
// ECDSA P-256 public key for verifying license JWTs (verification-only, safe to ship)
|
|
90
128
|
/* eslint-disable no-secrets/no-secrets -- public key, not a secret */
|
|
91
|
-
const
|
|
129
|
+
const _IIilbQX = {
|
|
92
130
|
kty: "EC",
|
|
93
131
|
crv: "P-256",
|
|
94
132
|
x: "A34nyKMjhQYVgzeE4tyLUYdx34TAKogDa7v7PRaO9Lg",
|
|
@@ -112,7 +150,7 @@ function base64urlDecode(str: string): Uint8Array {
|
|
|
112
150
|
* Verify a JWT license token and return the `type` claim if valid.
|
|
113
151
|
* Returns null if the JWT is missing, malformed, or has an invalid signature.
|
|
114
152
|
*/
|
|
115
|
-
async function $
|
|
153
|
+
async function _$powD(jwt: string): Promise<string | null> {
|
|
116
154
|
if (!jwt) return null;
|
|
117
155
|
try {
|
|
118
156
|
const parts = jwt.split(".");
|
|
@@ -122,7 +160,7 @@ async function $lFHtXwo(jwt: string): Promise<string | null> {
|
|
|
122
160
|
|
|
123
161
|
const key = await crypto.subtle.importKey(
|
|
124
162
|
"jwk",
|
|
125
|
-
|
|
163
|
+
_IIilbQX,
|
|
126
164
|
{ name: "ECDSA", namedCurve: "P-256" },
|
|
127
165
|
false,
|
|
128
166
|
["verify"]
|
|
@@ -182,24 +220,24 @@ async function $lFHtXwo(jwt: string): Promise<string | null> {
|
|
|
182
220
|
}
|
|
183
221
|
|
|
184
222
|
/** Verify the injected JWT and update the license type if valid.
|
|
185
|
-
* The engine ONLY trusts the JWT — the plain
|
|
223
|
+
* The engine ONLY trusts the JWT — the plain __VqzR string is ignored. */
|
|
186
224
|
let _jwtVerificationPromise: Promise<void> | undefined = undefined;
|
|
187
|
-
async function
|
|
188
|
-
// NOTE: do NOT add an `if (
|
|
225
|
+
async function __EYbYX(): Promise<void> {
|
|
226
|
+
// NOTE: do NOT add an `if (!$OQRl) return` guard here.
|
|
189
227
|
// esbuild (Vite dependency pre-bundling) would see the variable as constant ""
|
|
190
228
|
// and tree-shake the entire JWT verification code path.
|
|
191
|
-
const jwt =
|
|
229
|
+
const jwt = $OQRl;
|
|
192
230
|
// Clear after reading — this reassignment also prevents esbuild from
|
|
193
231
|
// constant-folding the variable (it now has multiple assignment sites).
|
|
194
|
-
|
|
195
|
-
const verifiedType = await $
|
|
232
|
+
$OQRl = "";
|
|
233
|
+
const verifiedType = await _$powD(jwt);
|
|
196
234
|
if (verifiedType) {
|
|
197
|
-
|
|
235
|
+
__VqzR = verifiedType;
|
|
198
236
|
if (debug) console.log("License type set from verified JWT: " + verifiedType);
|
|
199
|
-
|
|
237
|
+
_SUQSFYGK(__PemN());
|
|
200
238
|
}
|
|
201
239
|
else {
|
|
202
|
-
|
|
240
|
+
__VqzR = "basic";
|
|
203
241
|
if (debug && jwt) console.warn("JWT verification failed — license reset to basic");
|
|
204
242
|
}
|
|
205
243
|
}
|
|
@@ -650,18 +688,18 @@ export namespace Telemetry {
|
|
|
650
688
|
}
|
|
651
689
|
|
|
652
690
|
|
|
653
|
-
export function
|
|
691
|
+
export function _$oLIu() {
|
|
654
692
|
|
|
655
|
-
if(
|
|
693
|
+
if(__VqzR === "") __VqzR = "basic";
|
|
656
694
|
|
|
657
695
|
// Start JWT verification — must be here (not top-level) to avoid tree-shaking
|
|
658
|
-
_jwtVerificationPromise =
|
|
696
|
+
_jwtVerificationPromise = __EYbYX();
|
|
659
697
|
|
|
660
698
|
Telemetry.init();
|
|
661
699
|
ContextRegistry.registerCallback(ContextEvent.ContextRegistered, evt => {
|
|
662
|
-
|
|
663
|
-
$
|
|
664
|
-
|
|
700
|
+
gep(evt.context);
|
|
701
|
+
_$TNE(evt.context);
|
|
702
|
+
setLicenseTimeout(evt.context, () => cVal(evt.context), 2000);
|
|
665
703
|
});
|
|
666
704
|
// ContextCreated fires after the renderer exists, so the canvas is available for context-loss monitoring
|
|
667
705
|
ContextRegistry.registerCallback(ContextEvent.ContextCreated, evt => {
|
|
@@ -670,17 +708,17 @@ export function FcQBwfj() {
|
|
|
670
708
|
});
|
|
671
709
|
}
|
|
672
710
|
|
|
673
|
-
export let $
|
|
711
|
+
export let _$zwcLl: Promise<void> | undefined = undefined;
|
|
674
712
|
let applicationIsForbidden = false;
|
|
675
713
|
let applicationForbiddenText = "";
|
|
676
|
-
async function
|
|
714
|
+
async function _kAPEtsE() {
|
|
677
715
|
// Only perform the runtime license check once
|
|
678
|
-
if ($
|
|
716
|
+
if (_$zwcLl) return _$zwcLl;
|
|
679
717
|
// Wait for JWT verification to complete first (if running)
|
|
680
718
|
if (_jwtVerificationPromise) {
|
|
681
719
|
await _jwtVerificationPromise;
|
|
682
720
|
}
|
|
683
|
-
if (
|
|
721
|
+
if (__VqzR === "basic") {
|
|
684
722
|
try {
|
|
685
723
|
const licenseUrl = "https://needle.tools/api/v1/needle-engine/check?location=" + encodeURIComponent(window.location.href) + "&version=" + VERSION + "&generator=" + encodeURIComponent(GENERATOR);
|
|
686
724
|
const res = await fetch(licenseUrl, {
|
|
@@ -692,29 +730,29 @@ async function lyWzt() {
|
|
|
692
730
|
if (res?.status === 200) {
|
|
693
731
|
applicationIsForbidden = false;
|
|
694
732
|
if (debug) console.log("License check succeeded");
|
|
695
|
-
|
|
696
|
-
|
|
733
|
+
__VqzR = "pro";
|
|
734
|
+
_SUQSFYGK(true);
|
|
697
735
|
}
|
|
698
736
|
else if (res?.status === 403) {
|
|
699
|
-
|
|
737
|
+
_SUQSFYGK(false);
|
|
700
738
|
applicationIsForbidden = true;
|
|
701
739
|
applicationForbiddenText = await res.text();
|
|
702
740
|
}
|
|
703
741
|
else {
|
|
704
|
-
|
|
742
|
+
_SUQSFYGK(false);
|
|
705
743
|
if (debug) console.log("License check failed with status " + res?.status);
|
|
706
744
|
}
|
|
707
745
|
}
|
|
708
746
|
catch (err) {
|
|
709
|
-
|
|
747
|
+
_SUQSFYGK(false);
|
|
710
748
|
if (debug) console.error("License check failed", err);
|
|
711
749
|
}
|
|
712
750
|
}
|
|
713
|
-
else if (debug) console.log("Runtime license check is skipped because license is already applied as \"" +
|
|
751
|
+
else if (debug) console.log("Runtime license check is skipped because license is already applied as \"" + __VqzR + "\"");
|
|
714
752
|
}
|
|
715
|
-
$
|
|
753
|
+
_$zwcLl = _kAPEtsE();
|
|
716
754
|
|
|
717
|
-
async function $
|
|
755
|
+
async function _$TNE(ctx: IContext) {
|
|
718
756
|
function createForbiddenElement() {
|
|
719
757
|
const div = document.createElement("div");
|
|
720
758
|
div.className = "needle-forbidden";
|
|
@@ -755,7 +793,7 @@ async function $bbnhzdRV(ctx: IContext) {
|
|
|
755
793
|
const expectedTextStyle = text.style.cssText;
|
|
756
794
|
const forbiddenText = applicationForbiddenText?.length > 1 ? applicationForbiddenText : "This web application has been paused.<br/>You might be in violation of the Needle Engine terms of use.<br/>Please contact the Needle support if you think this is a mistake.";
|
|
757
795
|
text.innerHTML = forbiddenText;
|
|
758
|
-
|
|
796
|
+
setLicenseInterval(ctx, () => {
|
|
759
797
|
if (text.innerHTML !== forbiddenText) text.innerHTML = forbiddenText;
|
|
760
798
|
if (text.parentNode !== div) div.appendChild(text);
|
|
761
799
|
if (div.style.cssText !== expectedStyle) div.style.cssText = expectedStyle;
|
|
@@ -765,7 +803,7 @@ async function $bbnhzdRV(ctx: IContext) {
|
|
|
765
803
|
}
|
|
766
804
|
let forbiddenElement = createForbiddenElement();
|
|
767
805
|
const expectedCSS = forbiddenElement.style.cssText;
|
|
768
|
-
|
|
806
|
+
setLicenseInterval(ctx, () => {
|
|
769
807
|
if (applicationIsForbidden === true) {
|
|
770
808
|
if (forbiddenElement.style.cssText !== expectedCSS) forbiddenElement = createForbiddenElement();
|
|
771
809
|
if (ctx.domElement.shadowRoot) {
|
|
@@ -779,40 +817,40 @@ async function $bbnhzdRV(ctx: IContext) {
|
|
|
779
817
|
}, 500)
|
|
780
818
|
}
|
|
781
819
|
|
|
782
|
-
async function
|
|
820
|
+
async function gep(ctx: IContext) {
|
|
783
821
|
try {
|
|
784
|
-
if (!
|
|
785
|
-
return
|
|
822
|
+
if (!__csRKN() && !_qxvTsQJJ()) {
|
|
823
|
+
return $zQrLfV(ctx);
|
|
786
824
|
}
|
|
787
825
|
}
|
|
788
826
|
catch (err) {
|
|
789
827
|
if (debug) console.log("License check failed", err)
|
|
790
|
-
return
|
|
828
|
+
return $zQrLfV(ctx)
|
|
791
829
|
}
|
|
792
|
-
if (debug)
|
|
830
|
+
if (debug) $zQrLfV(ctx)
|
|
793
831
|
}
|
|
794
832
|
|
|
795
833
|
|
|
796
834
|
|
|
797
|
-
async function
|
|
835
|
+
async function $zQrLfV(ctx: IContext) {
|
|
798
836
|
|
|
799
837
|
// if the engine loads faster than the license check, we need to capture the ready event here
|
|
800
838
|
let isReady = false;
|
|
801
839
|
ctx.domElement.addEventListener("ready", () => isReady = true);
|
|
802
840
|
|
|
803
|
-
await $
|
|
841
|
+
await _$zwcLl?.catch(() => { });
|
|
804
842
|
|
|
805
843
|
|
|
806
|
-
if (
|
|
807
|
-
if (
|
|
844
|
+
if (__csRKN() || _qxvTsQJJ()) return;
|
|
845
|
+
if (__PemN() === false) _$Obysw();
|
|
808
846
|
|
|
809
847
|
// check if the engine is already ready (meaning has finished loading)
|
|
810
848
|
if (isReady) {
|
|
811
|
-
|
|
849
|
+
__HBEW(ctx);
|
|
812
850
|
}
|
|
813
851
|
else {
|
|
814
852
|
ctx.domElement.addEventListener("ready", () => {
|
|
815
|
-
|
|
853
|
+
__HBEW(ctx);
|
|
816
854
|
});
|
|
817
855
|
}
|
|
818
856
|
}
|
|
@@ -820,7 +858,7 @@ async function __gHN(ctx: IContext) {
|
|
|
820
858
|
// const licenseElementIdentifier = "needle-license-element";
|
|
821
859
|
// const licenseDuration = 10000;
|
|
822
860
|
// const licenseDelay = 1200;
|
|
823
|
-
function
|
|
861
|
+
function __HBEW(ctx: IContext) {
|
|
824
862
|
|
|
825
863
|
const style = `
|
|
826
864
|
position: relative;
|
|
@@ -833,7 +871,7 @@ function iJbqLNi(ctx: IContext) {
|
|
|
833
871
|
padding: 10px;
|
|
834
872
|
padding-left: 30px;
|
|
835
873
|
`;
|
|
836
|
-
if (
|
|
874
|
+
if (__VqzR === "edu") {
|
|
837
875
|
if (navigator.webdriver) {
|
|
838
876
|
console.log("This project is supported by Needle for Education – https://needle.tools");
|
|
839
877
|
}
|
|
@@ -871,7 +909,7 @@ function iJbqLNi(ctx: IContext) {
|
|
|
871
909
|
banner.style.cssText = bannerStyle;
|
|
872
910
|
banner.addEventListener("click", () => { window.open("https://needle.tools", "_blank") });
|
|
873
911
|
let expectedBannerStyle = banner.style.cssText;
|
|
874
|
-
|
|
912
|
+
setLicenseTimeout(ctx, () => {
|
|
875
913
|
bannerStyle = bannerStyle.replace("opacity: 0", "opacity: 1");
|
|
876
914
|
bannerStyle = bannerStyle.replace("transform: translateY(10px)", "transform: translateY(0)");
|
|
877
915
|
banner.style.cssText = bannerStyle;
|
|
@@ -879,7 +917,7 @@ function iJbqLNi(ctx: IContext) {
|
|
|
879
917
|
}, 100);
|
|
880
918
|
|
|
881
919
|
// ensure the banner is always visible
|
|
882
|
-
const interval =
|
|
920
|
+
const interval = setLicenseInterval(ctx, () => {
|
|
883
921
|
const parent = ctx.domElement.shadowRoot || ctx.domElement;
|
|
884
922
|
if (banner.parentNode !== parent) {
|
|
885
923
|
parent.appendChild(banner);
|
|
@@ -890,16 +928,16 @@ function iJbqLNi(ctx: IContext) {
|
|
|
890
928
|
}
|
|
891
929
|
}, 1000);
|
|
892
930
|
|
|
893
|
-
if (
|
|
931
|
+
if ($yVfpT()) {
|
|
894
932
|
const removeDelay = 20_000;
|
|
895
|
-
|
|
933
|
+
setLicenseTimeout(ctx, () => {
|
|
896
934
|
clearInterval(interval);
|
|
897
935
|
banner?.remove();
|
|
898
936
|
// show the logo every x minutes
|
|
899
937
|
const intervalInMinutes = 5;
|
|
900
|
-
|
|
938
|
+
setLicenseTimeout(ctx, () => {
|
|
901
939
|
if (ctx.domElement.parentNode)
|
|
902
|
-
|
|
940
|
+
__HBEW(ctx);
|
|
903
941
|
}, 1000 * 60 * intervalInMinutes)
|
|
904
942
|
}, removeDelay);
|
|
905
943
|
}
|
|
@@ -910,7 +948,7 @@ function iJbqLNi(ctx: IContext) {
|
|
|
910
948
|
const base64Logo = "data:image/webp;base64,UklGRrABAABXRUJQVlA4WAoAAAAQAAAAHwAAHwAAQUxQSKEAAAARN6CmbSM4WR7vdARON11EBDq3fLiNbVtVzpMCPlKAEzsx0Y/x+Ovuv4dn0EFE/ydAvz6YggXzgh5sVgXM/zOC/4sii7qgGvB5N7hmuQYwkvazWAu1JPW41FXSHq6pnaQWvqYH18Fc0j1hO/BFTtIeSBlJi5w6qIIO7IOrwhFsB2Yxukif0FTRLpXswHR8MxbslKe9VZsn/Ub5C7YFOpqSTABWUDgg6AAAAFAGAJ0BKiAAIAA+7VyoTqmkpCI3+qgBMB2JbACdMt69DwMIQBLhkTO6XwY00UEDK6cNIDnuNibPf0EgAP7Y1myuiQHLDsF/0h5unrGh6WAbv7aegg2ZMd3uRKfT/3SJztcaujYfTvMXspfCTmYcoO6a+vhC3ss4M8uM58t4siiu59I4aOl59e9Sr6xoxYlHf2v+NnBNpJYeJf8jABQAId/PXuBkLEFkiCucgSGEcfhvajql/j3reCGl0M5/9gQWy7ayNPs+wlvIxFnNfSlfuND4CZOCyxOHhRqOmHN4ULHo3tCSrUNvgAA=";
|
|
911
949
|
|
|
912
950
|
let lastLogTime = 0;
|
|
913
|
-
async function
|
|
951
|
+
async function _$Obysw(_logo?: string) {
|
|
914
952
|
const now = Date.now();
|
|
915
953
|
if (now - lastLogTime < 2000) return;
|
|
916
954
|
lastLogTime = now;
|
|
@@ -943,7 +981,7 @@ async function __bdHHYrc(_logo?: string) {
|
|
|
943
981
|
}
|
|
944
982
|
|
|
945
983
|
|
|
946
|
-
async function
|
|
984
|
+
async function cVal(context: IContext) {
|
|
947
985
|
// isAllowed() now also covers cross-origin-isolated pages (where beacons can't be sent)
|
|
948
986
|
// and Do Not Track / GPC.
|
|
949
987
|
if (!Telemetry.isAllowed(context)) {
|
|
@@ -957,7 +995,7 @@ async function _SUkw(context: IContext) {
|
|
|
957
995
|
|
|
958
996
|
// current url without query parameters
|
|
959
997
|
const currentUrl = window.location.href.split("?")[0];
|
|
960
|
-
const license =
|
|
998
|
+
const license = __VqzR;
|
|
961
999
|
|
|
962
1000
|
const beaconData = {
|
|
963
1001
|
license,
|
|
@@ -148,13 +148,27 @@ function internalInvokeLifecycleFunctions(ctx: Context, evt: Event) {
|
|
|
148
148
|
}
|
|
149
149
|
}
|
|
150
150
|
|
|
151
|
-
const
|
|
151
|
+
const buffers: Array<Array<RegisteredLifecycleMethod>> = [];
|
|
152
|
+
let invocationDepth = 0;
|
|
152
153
|
const hookContext: LifecycleHookContext = {
|
|
153
154
|
context: null as any as Context
|
|
154
155
|
};
|
|
155
156
|
|
|
156
157
|
function invoke(ctx: Context, methods: Array<RegisteredLifecycleMethod>, invokeOnce: boolean) {
|
|
157
|
-
|
|
158
|
+
// Reuse storage per nesting depth: hooks may synchronously invoke other hooks.
|
|
159
|
+
const bufferArray = buffers[invocationDepth] ?? (buffers[invocationDepth] = []);
|
|
160
|
+
invocationDepth++;
|
|
161
|
+
try {
|
|
162
|
+
invokeWithBuffer(ctx, methods, invokeOnce, bufferArray);
|
|
163
|
+
}
|
|
164
|
+
finally {
|
|
165
|
+
// One-time/unregistered callbacks can capture entire scenes as well.
|
|
166
|
+
bufferArray.length = 0;
|
|
167
|
+
invocationDepth--;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function invokeWithBuffer(ctx: Context, methods: Array<RegisteredLifecycleMethod>, invokeOnce: boolean, bufferArray: Array<RegisteredLifecycleMethod>) {
|
|
158
172
|
for (let i = 0; i < methods.length; i++) {
|
|
159
173
|
bufferArray.push(methods[i]);
|
|
160
174
|
}
|
|
@@ -168,6 +182,7 @@ function invoke(ctx: Context, methods: Array<RegisteredLifecycleMethod>, invokeO
|
|
|
168
182
|
}
|
|
169
183
|
|
|
170
184
|
if (invoke) {
|
|
185
|
+
const previousContext = hookContext.context;
|
|
171
186
|
try {
|
|
172
187
|
hookContext.context = ctx;
|
|
173
188
|
entry.method?.call(hookContext, ctx);
|
|
@@ -175,6 +190,10 @@ function invoke(ctx: Context, methods: Array<RegisteredLifecycleMethod>, invokeO
|
|
|
175
190
|
catch (e) {
|
|
176
191
|
console.error("Error in lifecycle method", e);
|
|
177
192
|
}
|
|
193
|
+
finally {
|
|
194
|
+
// This shared receiver must not keep the last invoked context alive.
|
|
195
|
+
hookContext.context = previousContext;
|
|
196
|
+
}
|
|
178
197
|
}
|
|
179
198
|
|
|
180
199
|
// Remove the method if it's a one time call
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { FileLoader } from "three";
|
|
2
2
|
|
|
3
3
|
import { showBalloonWarning } from "./debug/index.js";
|
|
4
|
-
import {
|
|
4
|
+
import { __PemN } from "./engine_license.js";
|
|
5
5
|
import { delay } from "./engine_utils.js";
|
|
6
6
|
import { md5AsBytes, md5Hex, sha256Base64 } from "./engine_utils_hash.js";
|
|
7
7
|
|
|
@@ -45,7 +45,7 @@ export namespace BlobStorage {
|
|
|
45
45
|
*/
|
|
46
46
|
export function canUpload(info: { filesize: number }) {
|
|
47
47
|
const sizeInMB = info.filesize / 1024 / 1024;
|
|
48
|
-
if (
|
|
48
|
+
if (__PemN()) {
|
|
49
49
|
return sizeInMB < maxSizeInMB;
|
|
50
50
|
}
|
|
51
51
|
return sizeInMB < maxFreeSizeInMB;
|
|
@@ -100,7 +100,7 @@ export namespace BlobStorage {
|
|
|
100
100
|
console.warn(`Your file is too large for uploading (${filesizeInMB.toFixed(1)}MB). Max allowed size is ${maxSizeInMB}MB`);
|
|
101
101
|
return null;
|
|
102
102
|
}
|
|
103
|
-
else if (!
|
|
103
|
+
else if (!__PemN() && filesizeInMB > maxFreeSizeInMB) {
|
|
104
104
|
if (opts?.silent !== true) showBalloonWarning(`File is too large for uploading. Please get a <a href=\"https://needle.tools/pricing\" target=\"_blank\">commercial license</a> to upload files larger than 5MB`);
|
|
105
105
|
console.warn(`Your file is too large for uploading (${filesizeInMB.toFixed(1)}MB). Max size is 5MB for non-commercial users. Please get a commercial license at https://needle.tools/pricing for larger files (up to 50MB)`);
|
|
106
106
|
return null;
|
|
@@ -8,6 +8,7 @@ import { getPeerjsInstance } from "../engine/engine_networking_peer.js";
|
|
|
8
8
|
import { showBalloonMessage } from "./debug/index.js";
|
|
9
9
|
import { Application } from "./engine_application.js";
|
|
10
10
|
import { Context } from "./engine_context.js";
|
|
11
|
+
import { ContextEvent, ContextRegistry } from "./engine_context_registry.js";
|
|
11
12
|
import type { IModel } from "./engine_networking_types.js";
|
|
12
13
|
import { type IComponent, isComponent } from "./engine_types.js";
|
|
13
14
|
import { getParam } from "./engine_utils.js";
|
|
@@ -121,17 +122,31 @@ function applySdpTransform(sdp) {
|
|
|
121
122
|
|
|
122
123
|
export class PeerHandle extends EventDispatcher<any> {
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
// Weak ownership prevents this cache from rooting a context and its scene indefinitely.
|
|
126
|
+
// IDs are scoped to contexts: separate engine elements may use the same stream ID.
|
|
127
|
+
private static readonly instances = new WeakMap<Context, Map<string, PeerHandle>>();
|
|
128
|
+
|
|
129
|
+
static {
|
|
130
|
+
ContextRegistry.registerCallback(ContextEvent.ContextDestroying, ({ context }) => {
|
|
131
|
+
const handles = PeerHandle.instances.get(context as Context);
|
|
132
|
+
if (!handles) return;
|
|
133
|
+
PeerHandle.instances.delete(context as Context);
|
|
134
|
+
for (const handle of handles.values()) handle.dispose();
|
|
135
|
+
handles.clear();
|
|
136
|
+
});
|
|
137
|
+
}
|
|
125
138
|
|
|
126
139
|
static getOrCreate(context: Context, guid: string): PeerHandle {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
140
|
+
let handles = PeerHandle.instances.get(context);
|
|
141
|
+
if (!handles) {
|
|
142
|
+
handles = new Map();
|
|
143
|
+
PeerHandle.instances.set(context, handles);
|
|
144
|
+
}
|
|
145
|
+
let peer = handles.get(guid);
|
|
146
|
+
if (!peer) {
|
|
147
|
+
peer = new PeerHandle(context, guid);
|
|
148
|
+
handles.set(guid, peer);
|
|
149
|
+
}
|
|
135
150
|
return peer;
|
|
136
151
|
}
|
|
137
152
|
|
|
@@ -151,6 +166,7 @@ export class PeerHandle extends EventDispatcher<any> {
|
|
|
151
166
|
}
|
|
152
167
|
|
|
153
168
|
makeCall(peerId: string, stream: MediaStream): CallHandle | undefined {
|
|
169
|
+
if (this._disposed) return undefined;
|
|
154
170
|
if (!stream?.id) {
|
|
155
171
|
if (debug) console.warn("Can not make a call: mediastream has no id or is undefined");
|
|
156
172
|
else console.debug("Can not make a call: mediastream has no id or is undefined");
|
|
@@ -179,10 +195,8 @@ export class PeerHandle extends EventDispatcher<any> {
|
|
|
179
195
|
}
|
|
180
196
|
|
|
181
197
|
closeAll() {
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
for (const call of this._outgoingCalls) {
|
|
198
|
+
// Closing a call synchronously removes it from the original array.
|
|
199
|
+
for (const call of [...this._incomingCalls, ...this._outgoingCalls]) {
|
|
186
200
|
call.close();
|
|
187
201
|
}
|
|
188
202
|
this.updateCalls();
|
|
@@ -253,6 +267,23 @@ export class PeerHandle extends EventDispatcher<any> {
|
|
|
253
267
|
}
|
|
254
268
|
}
|
|
255
269
|
|
|
270
|
+
private _disposed = false;
|
|
271
|
+
private readonly _callIntervals = new Set<ReturnType<typeof setInterval>>();
|
|
272
|
+
|
|
273
|
+
private dispose() {
|
|
274
|
+
if (this._disposed) return;
|
|
275
|
+
this._disposed = true;
|
|
276
|
+
this.disable();
|
|
277
|
+
this.unsubscribePeerEvents();
|
|
278
|
+
for (const interval of this._callIntervals) clearInterval(interval);
|
|
279
|
+
this._callIntervals.clear();
|
|
280
|
+
this.closeAll();
|
|
281
|
+
this._incomingCalls.length = 0;
|
|
282
|
+
this._outgoingCalls.length = 0;
|
|
283
|
+
this._peer?.destroy();
|
|
284
|
+
this._peer = undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
256
287
|
private _enabled: boolean = false;
|
|
257
288
|
private _enabledPeer: boolean = false;
|
|
258
289
|
private onConnectRoomFn: () => void = this.onConnectRoom.bind(this);
|
|
@@ -262,7 +293,7 @@ export class PeerHandle extends EventDispatcher<any> {
|
|
|
262
293
|
// private _connectionPeerIdMap : Map<string, string> = new Map();
|
|
263
294
|
|
|
264
295
|
enable() {
|
|
265
|
-
if (this._enabled) return;
|
|
296
|
+
if (this._disposed || this._enabled) return;
|
|
266
297
|
this._enabled = true;
|
|
267
298
|
this.context.connection.beginListen(RoomEvents.JoinedRoom, this.onConnectRoomFn);
|
|
268
299
|
// this.context.connection.beginListen(RoomEvents.UserJoinedRoom, this.onUserJoinedOrLeftRoomFn);
|
|
@@ -287,13 +318,19 @@ export class PeerHandle extends EventDispatcher<any> {
|
|
|
287
318
|
// };
|
|
288
319
|
|
|
289
320
|
private async setupPeer() {
|
|
290
|
-
if (!this.context.connection.connectionId) return;
|
|
321
|
+
if (this._disposed || !this.context.connection.connectionId) return;
|
|
291
322
|
if (this._enabledPeer) return;
|
|
292
323
|
this._enabledPeer = true;
|
|
293
324
|
if (!this._peer) {
|
|
294
325
|
const peerId = this.getMyPeerId();
|
|
295
|
-
if (peerId)
|
|
296
|
-
|
|
326
|
+
if (peerId) {
|
|
327
|
+
const peer = await getPeerjsInstance(peerId);
|
|
328
|
+
if (this._disposed) {
|
|
329
|
+
peer.destroy();
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
this._peer = peer;
|
|
333
|
+
}
|
|
297
334
|
else console.error("Failed to setup peerjs because we dont have a connection id", this.context.connection.connectionId);
|
|
298
335
|
}
|
|
299
336
|
if (this._enabled)
|
|
@@ -415,6 +452,7 @@ export class PeerHandle extends EventDispatcher<any> {
|
|
|
415
452
|
});
|
|
416
453
|
|
|
417
454
|
call.on("stream", () => {
|
|
455
|
+
if (this._disposed) return;
|
|
418
456
|
if (debug) console.log("Received stream for call", call.metadata);
|
|
419
457
|
// workaround for https://github.com/peers/peerjs/issues/636
|
|
420
458
|
let intervalCounter = 0;
|
|
@@ -424,9 +462,15 @@ export class PeerHandle extends EventDispatcher<any> {
|
|
|
424
462
|
if (debug) console.warn("Close call because stream is not active", call.metadata);
|
|
425
463
|
intervalCounter += 1;
|
|
426
464
|
clearInterval(closeInterval);
|
|
465
|
+
this._callIntervals.delete(closeInterval);
|
|
427
466
|
handle.close();
|
|
428
467
|
}
|
|
429
468
|
}, 2000);
|
|
469
|
+
this._callIntervals.add(closeInterval);
|
|
470
|
+
call.on("close", () => {
|
|
471
|
+
clearInterval(closeInterval);
|
|
472
|
+
this._callIntervals.delete(closeInterval);
|
|
473
|
+
});
|
|
430
474
|
});
|
|
431
475
|
}
|
|
432
476
|
return handle;
|
|
@@ -4,7 +4,7 @@ import { Quaternion, Vector2, Vector3, Vector4 } from "three";
|
|
|
4
4
|
|
|
5
5
|
import { needleLogoOnlySVG } from "./assets/index.js";
|
|
6
6
|
import { isDevEnvironment } from "./debug/debug.js";
|
|
7
|
-
import {
|
|
7
|
+
import { __PemN } from "./engine_license.js";
|
|
8
8
|
import { InternalAttributeUtils } from "./engine_utils_attributes.js";
|
|
9
9
|
import type { NeedleEngineWebComponent } from "./webcomponents/needle-engine.js";
|
|
10
10
|
|
|
@@ -135,7 +135,7 @@ async function internalRenderQRCodeOverlays(canvas: HTMLCanvasElement, args: { s
|
|
|
135
135
|
console.debug("[QR Code] No web component found")
|
|
136
136
|
}
|
|
137
137
|
|
|
138
|
-
const canUseCustomLogo =
|
|
138
|
+
const canUseCustomLogo = __PemN();
|
|
139
139
|
|
|
140
140
|
// Query logo src from needle-engine attribute.
|
|
141
141
|
// For any supported attribute it's possible to use "falsey" values (e.g. "0" or "false" to disable the logo in the QR code)
|