lecodes-sdk 2.4.1 → 2.4.3
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/dist/types/core/Aspect.d.ts +34 -5
- package/dist/types/g2/OneWay2D.d.ts +0 -1
- package/dist/types/g2/Physics2D.d.ts +8 -6
- package/dist/types/g2/Scene2D.d.ts +1 -1
- package/dist/types/g2/Shape2D.d.ts +43 -15
- package/dist/types/gl/Model.d.ts +3 -2
- package/dist/types/gl/Scene.d.ts +1 -1
- package/dist/types/gl/animation/DynamicBone.d.ts +99 -35
- package/dist/types/gl/animation/IK.d.ts +0 -1
- package/dist/types/gl/nav/NavAgent.d.ts +3 -2
- package/dist/types/gl/physics/Physics.d.ts +4 -5
- package/dist/types/gl/physics/Shape.d.ts +40 -15
- package/dist/types/gl/vehicle/Vehicle.d.ts +54 -35
- package/dist/types/gl/vehicle/Wheel.d.ts +31 -10
- package/dist/types/plugins/gen/ar/contract.d.ts +152 -0
- package/dist/types/plugins/gen/ar/sdk/ar.d.ts +47 -21
- package/dist/types/plugins/gen/ar/sdk/ar.gen.d.ts +17 -3
- package/dist/types/plugins/gen/audio-recorder/contract.d.ts +64 -0
- package/dist/types/plugins/gen/browser/contract.d.ts +108 -0
- package/dist/types/plugins/gen/browser/sdk/browser.d.ts +2 -1
- package/dist/types/plugins/gen/camera/contract.d.ts +27 -0
- package/dist/types/plugins/gen/geolocation/contract.d.ts +52 -0
- package/dist/types/plugins/gen/map/contract.d.ts +144 -0
- package/dist/types/plugins/gen/push/contract.d.ts +83 -0
- package/dist/types/plugins/gen/qr-scanner/contract.d.ts +16 -0
- package/dist/types/plugins/gen/speech/contract.d.ts +67 -0
- package/dist/types/plugins/gen/webrtc/contract.d.ts +342 -0
- package/dist/types/version.d.ts +1 -1
- package/dist/types.json +1 -1
- package/package.json +1 -1
- package/src/animate/tween/Timeline.ts +175 -175
- package/src/bridges/gl.d.ts +5 -3
- package/src/compile/compileProject.ts +45 -10
- package/src/compile/index.ts +7 -0
- package/src/compile/webHalf.ts +255 -0
- package/src/core/Aspect.ts +118 -35
- package/src/g2/OneWay2D.ts +0 -2
- package/src/g2/Physics2D.ts +8 -16
- package/src/g2/Scene2D.ts +1 -1
- package/src/g2/Shape2D.ts +51 -22
- package/src/gl/CameraPlace.ts +52 -52
- package/src/gl/Light.ts +257 -257
- package/src/gl/Mesh.ts +120 -120
- package/src/gl/Model.ts +3 -2
- package/src/gl/Scene.ts +1 -1
- package/src/gl/animation/DynamicBone.ts +549 -486
- package/src/gl/animation/IK.ts +0 -2
- package/src/gl/nav/NavAgent.ts +14 -12
- package/src/gl/physics/Physics.ts +4 -5
- package/src/gl/physics/Shape.ts +49 -23
- package/src/gl/scenarios.ts +291 -291
- package/src/gl/vehicle/Vehicle.ts +102 -58
- package/src/gl/vehicle/Wheel.ts +45 -11
- package/src/plugins/gen/ar/contract.d.ts +35 -1
- package/src/plugins/gen/ar/sdk/ar.gen.ts +25 -6
- package/src/plugins/gen/ar/sdk/ar.ts +102 -35
- package/src/plugins/gen/browser/sdk/browser.ts +11 -1
- package/src/version.ts +1 -1
|
@@ -16,14 +16,14 @@
|
|
|
16
16
|
// horizontal plane the session reports, so the halves know no root.
|
|
17
17
|
|
|
18
18
|
import {
|
|
19
|
-
Geometry, Mat4, Material, Mesh, Node, Presentable, Quat, Scene, System, Vec3, _ensurePermission, _navEpoch, _toFile,
|
|
19
|
+
Geometry, Mat4, Material, Mesh, Node, Presentable, Quat, Router, Scene, System, Vec3, _ensurePermission, _navEpoch, _toFile,
|
|
20
20
|
type ClickEvent, type DismissOptions, type FetchResponse, type File, type NodeEvents, type PresentOptions,
|
|
21
21
|
type QuatLike, type SceneOptions, type TouchStartEvent, type Vec3Like,
|
|
22
22
|
} from "../../../../plugin"
|
|
23
23
|
import { ARBase, supports } from "./ar.gen"
|
|
24
24
|
import { attachControls, type ControlsHandle, type ControlsOptions } from "./controls"
|
|
25
25
|
import type {
|
|
26
|
-
ARErrorCode, ARMode, HitTarget, ImageTarget, PlaneChange, PlaneDetection, PlaneType, Pose, StartOptions,
|
|
26
|
+
ARErrorCode, ARMode, HitTarget, ImageTarget, PlaneChange, PlaneDetection, PlaneType, Pose, QRTarget, StartOptions,
|
|
27
27
|
TrackingReason, TrackingState, WorldAlignment,
|
|
28
28
|
} from "../contract"
|
|
29
29
|
|
|
@@ -60,12 +60,12 @@ export type ARError = { code: ARErrorCode, message: string }
|
|
|
60
60
|
export type ARPlaneEvent = { plane: ARPlane, change: PlaneChange, mergedInto: ARPlane | null }
|
|
61
61
|
|
|
62
62
|
export type ARSceneOptions = SceneOptions & {
|
|
63
|
-
/** "world" (default): planes, hit tests, anchors, image targets. "images": image
|
|
64
|
-
* lighter on iOS; no planes, no hit test, no `root`. */
|
|
63
|
+
/** "world" (default): planes, hit tests, anchors, image and QR targets. "images": image and QR
|
|
64
|
+
* targets only — lighter on iOS; no planes, no hit test, no `root`. */
|
|
65
65
|
mode?: ARMode
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
-
export type AROpenOptions = PresentOptions & Omit<StartOptions, "mode" | "images"> & {
|
|
68
|
+
export type AROpenOptions = PresentOptions & Omit<StartOptions, "mode" | "images" | "codes"> & {
|
|
69
69
|
/** The scene's ambient (its `environmentIntensity`) follows the estimated light; turns
|
|
70
70
|
* `lightEstimation` on. */
|
|
71
71
|
followLight?: boolean
|
|
@@ -76,6 +76,13 @@ export type ARSceneEvents = {
|
|
|
76
76
|
plane: (event: ARPlaneEvent) => void
|
|
77
77
|
light: (estimate: ARLightEstimate) => void
|
|
78
78
|
error: (error: ARError) => void
|
|
79
|
+
/** The session ended on its own — the user left it (the back gesture of an immersive web session)
|
|
80
|
+
* or the system took it — and the scene leaves: popped when it is the router's top, else closed
|
|
81
|
+
* (`onClose` is where an app opens what comes next, as after `scene.close()`). Not an error. Sent
|
|
82
|
+
* before the scene leaves. (A `Router.pop()` of an AR scene by the app does NOT end its session:
|
|
83
|
+
* a covered scene comes back with it — on the web an immersive session then stays on; `close()`
|
|
84
|
+
* ends it.) */
|
|
85
|
+
end: (reason: "user" | "system") => void
|
|
79
86
|
}
|
|
80
87
|
|
|
81
88
|
export type AnchorEvents = NodeEvents & {
|
|
@@ -97,9 +104,10 @@ const wirePose = (p: ARPoseLike): Pose => {
|
|
|
97
104
|
}
|
|
98
105
|
|
|
99
106
|
/**
|
|
100
|
-
* A node the AR session drives: it follows a place in the room (`scene.anchor(pose)`, `scene.root`)
|
|
101
|
-
*
|
|
102
|
-
* holds it now; `track` / `untrack` fire when
|
|
107
|
+
* A node the AR session drives: it follows a place in the room (`scene.anchor(pose)`, `scene.root`),
|
|
108
|
+
* an image (`scene.createAnchor(image)`) or a QR code (`scene.createQRAnchor(text, size)`). Put
|
|
109
|
+
* content under it. `tracked` says whether the session holds it now; `track` / `untrack` fire when
|
|
110
|
+
* that changes.
|
|
103
111
|
*/
|
|
104
112
|
export class Anchor extends Node {
|
|
105
113
|
/** @internal */
|
|
@@ -134,13 +142,16 @@ export class Anchor extends Node {
|
|
|
134
142
|
}
|
|
135
143
|
|
|
136
144
|
/**
|
|
137
|
-
* An anchor that follows an image (`scene.createAnchor(image, physicalWidth)`)
|
|
138
|
-
*
|
|
139
|
-
*
|
|
145
|
+
* An anchor that follows an image (`scene.createAnchor(image, physicalWidth)`) or a printed QR code
|
|
146
|
+
* (`scene.createQRAnchor(text, size)`, `code` its text): while tracked it sits on it — a rigid pose
|
|
147
|
+
* in metres, no scale: its centre, Y out of it, the image in X (its width; a code's top edge) and Z
|
|
148
|
+
* (towards its bottom). How big it is, is `extent`.
|
|
140
149
|
*/
|
|
141
150
|
export class ImageAnchor extends Anchor {
|
|
142
151
|
/** @internal */
|
|
143
152
|
_extent: [number, number]
|
|
153
|
+
/** @internal The text of the QR code it follows; null for an image's. */
|
|
154
|
+
_code: string | null = null
|
|
144
155
|
|
|
145
156
|
constructor(physicalWidth: number) {
|
|
146
157
|
super()
|
|
@@ -149,8 +160,12 @@ export class ImageAnchor extends Anchor {
|
|
|
149
160
|
|
|
150
161
|
/** [width, height] in metres, along the anchor's X and Z: the session's estimate of the printed
|
|
151
162
|
* image, refined while it tracks it. Before the image is first found: [physicalWidth, 0] — the
|
|
152
|
-
* height comes with the image's proportions, which the session reads, at the first `track`.
|
|
163
|
+
* height comes with the image's proportions, which the session reads, at the first `track`. A QR
|
|
164
|
+
* code's is [size, size] from the start (a code is square). */
|
|
153
165
|
get extent(): [number, number] { return [this._extent[0], this._extent[1]] }
|
|
166
|
+
|
|
167
|
+
/** The text of the QR code the anchor follows (`createQRAnchor`); null for an image's. */
|
|
168
|
+
get code(): string | null { return this._code }
|
|
154
169
|
}
|
|
155
170
|
|
|
156
171
|
/** A surface the session found: a floor, a table, a wall. Its node sits at the plane's centre with Y
|
|
@@ -265,11 +280,15 @@ export class ARScene extends Scene {
|
|
|
265
280
|
private _shown: { promise: Promise<void>, resolve: () => void, reject: (error: Error) => void, epoch: number } | null = null
|
|
266
281
|
/** The open's start while it is out (`_begin`). `closed`: close() ended its session meanwhile — the
|
|
267
282
|
* half refuses a start its close cut short ("failed"), which is the close's doing: the open was
|
|
268
|
-
* superseded, not failed. `images`: the image targets changed meanwhile, which the
|
|
269
|
-
* carry (it read them when it was sent) — the set goes to the session once it runs. */
|
|
270
|
-
private _startOut: { session: ARBase, closed: boolean, images: boolean } | null = null
|
|
271
|
-
/** A `setImages` is due at the end of this turn (`
|
|
272
|
-
private
|
|
283
|
+
* superseded, not failed. `images` / `codes`: the image / QR targets changed meanwhile, which the
|
|
284
|
+
* start did not carry (it read them when it was sent) — the set goes to the session once it runs. */
|
|
285
|
+
private _startOut: { session: ARBase, closed: boolean, images: boolean, codes: boolean } | null = null
|
|
286
|
+
/** A `setImages` / `setCodes` is due at the end of this turn (`_setTargets`). */
|
|
287
|
+
private readonly _due = { images: false, codes: false }
|
|
288
|
+
/** The QR targets (`createQRAnchor`). */
|
|
289
|
+
private readonly _codes: { anchor: ImageAnchor, text: string, size: number }[] = []
|
|
290
|
+
/** This host's half is older than the QR targets: said once. */
|
|
291
|
+
private _codesWarned = false
|
|
273
292
|
private _options: AROpenOptions = {}
|
|
274
293
|
private _root: Anchor | null = null
|
|
275
294
|
/** The first horizontal plane's pose: where `root` is pinned. */
|
|
@@ -335,7 +354,34 @@ export class ARScene extends Scene {
|
|
|
335
354
|
this.add(anchor)
|
|
336
355
|
const id = (source as unknown as { _h: Handle })._h.id
|
|
337
356
|
this._images.push({ anchor, image: _toFile({ systemId: id, name: "image", size: 0 }), width: physicalWidth, source })
|
|
338
|
-
this.
|
|
357
|
+
this._setTargets("images")
|
|
358
|
+
return anchor
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
/** An anchor that follows a printed QR code whose text is `text`: the session finds the symbol in
|
|
362
|
+
* the camera image and keeps the anchor on it, as on an image — a rigid pose in metres (its centre,
|
|
363
|
+
* Y out of the symbol, X along its top edge, Z towards its bottom), `anchor.extent` = [size, size].
|
|
364
|
+
* `size` = the printed side of the symbol in metres (its modules, no quiet zone): how far it is
|
|
365
|
+
* comes from it. Hidden while not tracked. Before `open()`, or any time: on a running scene the set
|
|
366
|
+
* of codes is updated in place. The web finds codes first (WebXR); on a host whose `ar` plugin does
|
|
367
|
+
* not track them the anchor is never tracked, and the console says so. `text` null or "" = ANY
|
|
368
|
+
* code: the first symbol decoded (a test, a plaque whose text the app does not know). */
|
|
369
|
+
createQRAnchor(text: string | null, size: number): ImageAnchor {
|
|
370
|
+
const anchor = new ImageAnchor(size)
|
|
371
|
+
anchor._code = text ?? ""
|
|
372
|
+
anchor._extent = [size, size]
|
|
373
|
+
anchor._scene = this
|
|
374
|
+
anchor._hideUntracked = true
|
|
375
|
+
anchor.visible = false
|
|
376
|
+
this.add(anchor)
|
|
377
|
+
// a half older than the codes (contract version 2) has no setCodes, and its start reads no codes
|
|
378
|
+
if (ARScene.isSupported && !supports("setCodes")) {
|
|
379
|
+
if (!this._codesWarned) console.warn('ARScene: this host\'s "ar" plugin tracks no QR codes (it is older than contract version 2) — their anchors are never tracked')
|
|
380
|
+
this._codesWarned = true
|
|
381
|
+
return anchor
|
|
382
|
+
}
|
|
383
|
+
this._codes.push({ anchor, text: text ?? "", size })
|
|
384
|
+
this._setTargets("codes")
|
|
339
385
|
return anchor
|
|
340
386
|
}
|
|
341
387
|
|
|
@@ -486,6 +532,7 @@ export class ARScene extends Scene {
|
|
|
486
532
|
const options: StartOptions = { ...rest, mode: this.mode }
|
|
487
533
|
if (followLight) options.lightEstimation = true
|
|
488
534
|
if (this._images.length) options.images = this._targets()
|
|
535
|
+
if (this._codes.length) options.codes = this._codeTargets()
|
|
489
536
|
return options
|
|
490
537
|
}
|
|
491
538
|
|
|
@@ -493,6 +540,10 @@ export class ARScene extends Scene {
|
|
|
493
540
|
return this._images.map(t => ({ image: t.image, physicalWidth: t.width, entity: t.anchor }))
|
|
494
541
|
}
|
|
495
542
|
|
|
543
|
+
private _codeTargets(): QRTarget[] {
|
|
544
|
+
return this._codes.map(t => ({ text: t.text, size: t.size, entity: t.anchor }))
|
|
545
|
+
}
|
|
546
|
+
|
|
496
547
|
private async _begin(): Promise<void> {
|
|
497
548
|
const epoch = _navEpoch()
|
|
498
549
|
const superseded = (why = "another destination was opened") => new Error(`ARScene.open() superseded: ${why} while preparing`)
|
|
@@ -524,9 +575,19 @@ export class ARScene extends Scene {
|
|
|
524
575
|
this._drop(session)
|
|
525
576
|
this._emit("error", { code: p.code, message: p.message })
|
|
526
577
|
})
|
|
578
|
+
session.on("ended", p => {
|
|
579
|
+
if (this._session !== session || !p) return
|
|
580
|
+
// over, not by close(): the scene goes with it — the user's back gesture, as the router's:
|
|
581
|
+
// popped when it is the router's top (the page under it shows), else closed (what it replaced
|
|
582
|
+
// comes back through its onClose — `open()` took that destination's place)
|
|
583
|
+
this._drop(session)
|
|
584
|
+
this._emit("end", p.reason)
|
|
585
|
+
if (Router.current === this) Router.pop()
|
|
586
|
+
else this.close()
|
|
587
|
+
})
|
|
527
588
|
this._session = session
|
|
528
589
|
this._baseIntensity = this.environmentIntensity
|
|
529
|
-
const out = { session, closed: false, images: false }
|
|
590
|
+
const out = { session, closed: false, images: false, codes: false }
|
|
530
591
|
this._startOut = out
|
|
531
592
|
try {
|
|
532
593
|
await session.start(this._startOptions())
|
|
@@ -545,8 +606,9 @@ export class ARScene extends Scene {
|
|
|
545
606
|
throw superseded()
|
|
546
607
|
}
|
|
547
608
|
this._pinAll()
|
|
548
|
-
// the image targets changed while the start was out: the running session takes the set now
|
|
549
|
-
if (out.images) this.
|
|
609
|
+
// the image / QR targets changed while the start was out: the running session takes the set now
|
|
610
|
+
if (out.images) this._setTargets("images")
|
|
611
|
+
if (out.codes) this._setTargets("codes")
|
|
550
612
|
}
|
|
551
613
|
|
|
552
614
|
/** The session is over (closed, failed): every anchor untracked, the planes gone. */
|
|
@@ -571,32 +633,34 @@ export class ARScene extends Scene {
|
|
|
571
633
|
this._planes.clear()
|
|
572
634
|
for (const anchor of this._pinned) anchor._setTracked(false)
|
|
573
635
|
for (const image of this._images) image.anchor._setTracked(false)
|
|
636
|
+
for (const code of this._codes) code.anchor._setTracked(false)
|
|
574
637
|
this._root?._setTracked(false)
|
|
575
638
|
if (this._options.followLight) this.environmentIntensity = this._baseIntensity
|
|
576
639
|
}
|
|
577
640
|
|
|
578
|
-
/** The image targets changed (one added or removed): a running session takes the
|
|
579
|
-
* (`setImages` — the tracking, the planes and the anchors stay). Sent
|
|
580
|
-
* targets added in a row are one set. While the open's start is out
|
|
581
|
-
* carried the set it read when it was sent); before a session there is
|
|
582
|
-
* carries the set. Nothing waits for it: a set the half refuses is a
|
|
583
|
-
* whose
|
|
584
|
-
private
|
|
641
|
+
/** The image targets or the QR targets changed (one added or removed): a running session takes the
|
|
642
|
+
* whole set in place (`setImages` / `setCodes` — the tracking, the planes and the anchors stay). Sent
|
|
643
|
+
* at the end of the turn: several targets added in a row are one set. While the open's start is out
|
|
644
|
+
* it waits for it (the start carried the set it read when it was sent); before a session there is
|
|
645
|
+
* nothing to tell — the start carries the set. Nothing waits for it: a set the half refuses is a
|
|
646
|
+
* console line, and an anchor whose target the session did not take never tracks. */
|
|
647
|
+
private _setTargets(kind: "images" | "codes"): void {
|
|
585
648
|
const session = this._session
|
|
586
649
|
if (!session) return
|
|
587
650
|
const out = this._startOut
|
|
588
|
-
if (out && out.session === session) { out
|
|
589
|
-
if (this.
|
|
590
|
-
this.
|
|
651
|
+
if (out && out.session === session) { out[kind] = true; return }
|
|
652
|
+
if (this._due[kind]) return
|
|
653
|
+
this._due[kind] = true
|
|
591
654
|
void Promise.resolve().then(() => {
|
|
592
|
-
this.
|
|
655
|
+
this._due[kind] = false
|
|
593
656
|
// closed meanwhile: a call on the closed channel would open a new session (the next open's start
|
|
594
657
|
// carries the set)
|
|
595
658
|
if (this._session !== session) return
|
|
596
|
-
session.setImages(this._targets()).
|
|
659
|
+
const sent = kind === "images" ? session.setImages(this._targets()) : session.setCodes(this._codeTargets())
|
|
660
|
+
sent.catch((e: unknown) => {
|
|
597
661
|
// a close while it was out: the half's refusal is the close's, nothing failed
|
|
598
662
|
if (this._session !== session) return
|
|
599
|
-
console.warn(`ARScene: the session did not take the image targets (${e instanceof Error ? e.message : String(e)}) — their anchors are not tracked`)
|
|
663
|
+
console.warn(`ARScene: the session did not take the ${kind === "images" ? "image targets" : "QR codes"} (${e instanceof Error ? e.message : String(e)}) — their anchors are not tracked`)
|
|
600
664
|
})
|
|
601
665
|
})
|
|
602
666
|
}
|
|
@@ -643,10 +707,13 @@ export class ARScene extends Scene {
|
|
|
643
707
|
if (anchor === this._root) this._root = null
|
|
644
708
|
const image = this._images.findIndex(t => t.anchor === anchor)
|
|
645
709
|
if (image >= 0) this._images.splice(image, 1)
|
|
710
|
+
const code = this._codes.findIndex(t => t.anchor === anchor)
|
|
711
|
+
if (code >= 0) this._codes.splice(code, 1)
|
|
646
712
|
this._wire("release", s => s.release(anchor)).catch(() => {})
|
|
647
713
|
anchor._setTracked(false)
|
|
648
714
|
this.remove(anchor)
|
|
649
|
-
if (image >= 0) this.
|
|
715
|
+
if (image >= 0) this._setTargets("images")
|
|
716
|
+
if (code >= 0) this._setTargets("codes")
|
|
650
717
|
}
|
|
651
718
|
|
|
652
719
|
private _onPlane(p: { change: PlaneChange, id: number, type: PlaneType, pose: Pose, extent: number[], polygon: number[], mergedInto?: number }): void {
|
|
@@ -8,6 +8,9 @@
|
|
|
8
8
|
// const back = await Browser.open(checkoutUrl, { returnUrl: [paid, failed], embedded: true })
|
|
9
9
|
// a checkout in an embedded page,
|
|
10
10
|
// resolved with the URL it reached
|
|
11
|
+
// (navigated to in the page, or
|
|
12
|
+
// opened by a bank's app through
|
|
13
|
+
// the app's verified link)
|
|
11
14
|
//
|
|
12
15
|
// It resolves with the return URL reached, or null when the user left. `returnUrl` says what ENDS
|
|
13
16
|
// the page; `embedded` says WHERE it runs — the system sheet (shared logins, passkeys, Apple Pay;
|
|
@@ -38,7 +41,8 @@ export interface BrowserOpenOptions {
|
|
|
38
41
|
/** What ends the page: a URL prefix, or several (a checkout's success and failure URLs). The
|
|
39
42
|
* promise resolves with the URL reached. In the sheet every prefix must share one scheme — the
|
|
40
43
|
* platform intercepts one; a custom scheme needs app.json `urlScheme`, an https one the app's
|
|
41
|
-
* verified links.
|
|
44
|
+
* verified links (app.json `appLinks`). Embedded, the view intercepts the navigation itself,
|
|
45
|
+
* and a verified https return from another app (a bank's) ends the page too. */
|
|
42
46
|
returnUrl?: string | string[]
|
|
43
47
|
/** Run the page in an embedded WebView under a close bar instead of the system sheet. The
|
|
44
48
|
* WebView intercepts any return URL itself, https included. */
|
|
@@ -157,10 +161,16 @@ const openEmbedded = (url: string, options: BrowserOpenOptions): Promise<string
|
|
|
157
161
|
if (routed) Router.pop()
|
|
158
162
|
else { screen.close(); previous?.open() }
|
|
159
163
|
}
|
|
164
|
+
// Two ways the return URL arrives: the page navigates to it inside the view (`stop`), or
|
|
165
|
+
// another app does — a bank's app the checkout handed off to sends the person back through the
|
|
166
|
+
// app's verified link (app.json "appLinks"), which reaches the app as its "url" event while the
|
|
167
|
+
// page still shows the bank's form. Both end the flow; `done` makes the second a no-op.
|
|
160
168
|
let done = false
|
|
169
|
+
const off = stops.length ? _onAppUrl(back => { if (matching(back, stops)) finish(back) }) : () => {}
|
|
161
170
|
const finish = (back: string | null): void => {
|
|
162
171
|
if (done) return
|
|
163
172
|
done = true
|
|
173
|
+
off()
|
|
164
174
|
leave()
|
|
165
175
|
resolve(back)
|
|
166
176
|
}
|
package/src/version.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// same manifest). Its MAJOR is the bundle ↔ runtime contract: a bundle's `// sdk:` header line
|
|
5
5
|
// and a host's embedded version must agree on the major for the host to run the bundle; minor
|
|
6
6
|
// and patch never gate anything (features are detected, never versioned).
|
|
7
|
-
export const SDK_VERSION = "2.4.
|
|
7
|
+
export const SDK_VERSION = "2.4.3"
|
|
8
8
|
|
|
9
9
|
/** The major of a semver string, or null when it is not one. */
|
|
10
10
|
export const sdkMajor = (version: string | null | undefined): number | null => {
|