lecodes-sdk 2.2.11 → 2.2.14
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/global.d.ts +13 -0
- package/dist/host.d.ts +8 -2
- package/dist/types/core/Aspect.d.ts +5 -0
- package/dist/types/g2/Physics2D.d.ts +3 -2
- package/dist/types/g2/Scene2D.d.ts +6 -0
- package/dist/types/gl/Scene.d.ts +6 -0
- package/dist/types/gl/Texture.d.ts +2 -7
- package/dist/types/gl/animation/DynamicBone.d.ts +3 -0
- package/dist/types/gl/animation/Locomotion.d.ts +3 -0
- package/dist/types/gl/physics/Physics.d.ts +2 -1
- package/dist/types/gl/physics/Ragdoll.d.ts +4 -0
- package/dist/types/gl/vehicle/Vehicle.d.ts +4 -0
- package/dist/types/inject.d.ts +4 -3
- package/dist/types/plugin.d.ts +3 -0
- package/dist/types/plugins/gen/push/sdk/push.d.ts +10 -3
- package/dist/types/plugins/gen/push/sdk/push.gen.d.ts +11 -3
- package/dist/types/runtime/app.d.ts +4 -2
- package/dist/types/runtime/fetch.d.ts +3 -0
- package/dist/types/runtime/pushSeam.d.ts +23 -0
- package/dist/types/runtime/rpc.d.ts +15 -1
- package/dist/types/runtime/storage.d.ts +6 -1
- package/dist/types/server/auth/models.d.ts +13 -1
- package/dist/types/server/auth/types.d.ts +2 -0
- package/dist/types/server/db/marci/query.d.ts +8 -2
- package/dist/types/server/inject.d.ts +1 -0
- package/dist/types/server/pushChannel.d.ts +79 -0
- package/dist/types/ui/UIButton.d.ts +2 -2
- package/dist/types/ui/UIContainer.d.ts +4 -2
- package/dist/types/ui/UINode.d.ts +2 -2
- package/dist/types/ui/UIScreen.d.ts +2 -2
- package/dist/types/ui/UIVideo.d.ts +3 -2
- package/dist/types/ui/router.d.ts +1 -1
- package/dist/types/version.d.ts +1 -1
- package/dist/types.json +1 -1
- package/package.json +2 -5
- package/src/compile/assetIconMacro.ts +2 -1
- package/src/compile/assetMacro.ts +2 -1
- package/src/compile/compileProject.ts +28 -4
- package/src/compile/header.ts +17 -0
- package/src/compile/index.ts +2 -0
- package/src/compile/serverSplit.ts +29 -19
- package/src/compile/serverTypes.ts +1 -1
- package/src/core/Aspect.ts +2 -1
- package/src/g2/Physics2D.ts +6 -3
- package/src/g2/Scene2D.ts +9 -1
- package/src/gl/Scene.ts +9 -1
- package/src/gl/Texture.ts +2 -1
- package/src/gl/animation/DynamicBone.ts +6 -2
- package/src/gl/animation/Locomotion.ts +11 -1
- package/src/gl/physics/Physics.ts +3 -2
- package/src/gl/physics/Ragdoll.ts +8 -3
- package/src/gl/vehicle/Vehicle.ts +7 -2
- package/src/host.d.ts +8 -2
- package/src/inject.ts +4 -3
- package/src/plugin.ts +3 -0
- package/src/plugins/gen/push/contract.d.ts +24 -2
- package/src/plugins/gen/push/sdk/push.gen.ts +17 -5
- package/src/plugins/gen/push/sdk/push.ts +159 -55
- package/src/runtime/app.ts +4 -2
- package/src/runtime/fetch.ts +6 -0
- package/src/runtime/pushSeam.ts +22 -0
- package/src/runtime/rpc.ts +35 -1
- package/src/runtime/storage.ts +14 -4
- package/src/server/auth/host.ts +40 -9
- package/src/server/auth/models.ts +5 -1
- package/src/server/auth/types.ts +2 -0
- package/src/server/db/marci/query.ts +8 -2
- package/src/server/host.ts +6 -0
- package/src/server/inject.ts +3 -2
- package/src/server/push/host.ts +143 -0
- package/src/server/pushChannel.ts +121 -0
- package/src/server/runtime.ts +20 -8
- package/src/ui/UIButton.ts +2 -2
- package/src/ui/UIContainer.ts +4 -2
- package/src/ui/UIInput.ts +2 -2
- package/src/ui/UINode.ts +2 -2
- package/src/ui/UIScreen.ts +2 -2
- package/src/ui/UIVideo.ts +3 -2
- package/src/ui/router.ts +4 -3
- package/src/version.ts +1 -1
- package/prompts/2d.md +0 -391
- package/prompts/3d-scene-files.md +0 -113
- package/prompts/3d-scene.md +0 -122
- package/prompts/3d.md +0 -299
- package/prompts/README.md +0 -142
- package/prompts/ar.md +0 -142
- package/prompts/canvas.md +0 -28
- package/prompts/compose.ts +0 -46
- package/prompts/concept.md +0 -49
- package/prompts/core-design.md +0 -98
- package/prompts/core.md +0 -294
- package/prompts/design.md +0 -218
- package/prompts/dist/2d-game.md +0 -1567
- package/prompts/dist/3d-app.md +0 -1712
- package/prompts/dist/ar-app.md +0 -1585
- package/prompts/dist/concept.md +0 -49
- package/prompts/dist/design.md +0 -521
- package/prompts/dist/ui-app.md +0 -1175
- package/prompts/index.md +0 -39
- package/prompts/intake-prompt.md +0 -33
- package/prompts/namer-prompt.md +0 -12
- package/prompts/router-prompt.md +0 -50
- package/prompts/select.ts +0 -126
- package/prompts/ui-design.md +0 -203
- package/prompts/ui.md +0 -813
package/package.json
CHANGED
|
@@ -12,7 +12,6 @@
|
|
|
12
12
|
"./inject": "./src/inject.ts",
|
|
13
13
|
"./plugin": "./src/plugin.ts",
|
|
14
14
|
"./server": "./src/server/host.ts",
|
|
15
|
-
"./prompts/select": "./prompts/select.ts",
|
|
16
15
|
"./global.d.ts": "./dist/global.d.ts",
|
|
17
16
|
"./host.d.ts": "./dist/host.d.ts",
|
|
18
17
|
"./editor.d.ts": "./dist/editor.d.ts",
|
|
@@ -30,12 +29,11 @@
|
|
|
30
29
|
"test": "bun test --randomize",
|
|
31
30
|
"typecheck": "tsc --noEmit",
|
|
32
31
|
"docs:check": "node docs/check.mjs",
|
|
33
|
-
"prompts:build": "bun prompts/compose.ts",
|
|
34
32
|
"sync:marcidb": "bun scripts/sync-marcidb.ts",
|
|
35
33
|
"prepublishOnly": "bun run build"
|
|
36
34
|
},
|
|
37
35
|
"dependencies": {
|
|
38
|
-
"@letary/chisel": "^0.
|
|
36
|
+
"@letary/chisel": "^0.15.0"
|
|
39
37
|
},
|
|
40
38
|
"devDependencies": {
|
|
41
39
|
"@types/bun": "^1.3.14",
|
|
@@ -45,11 +43,10 @@
|
|
|
45
43
|
"marcidb-embedded": "^0.15.0",
|
|
46
44
|
"sharp": "^0.35.2"
|
|
47
45
|
},
|
|
48
|
-
"version": "2.2.
|
|
46
|
+
"version": "2.2.14",
|
|
49
47
|
"files": [
|
|
50
48
|
"src",
|
|
51
49
|
"dist",
|
|
52
|
-
"prompts",
|
|
53
50
|
"tests/helpers",
|
|
54
51
|
"README.md"
|
|
55
52
|
],
|
|
@@ -244,7 +244,8 @@ const iconExpr = (svg: string, call: IconCall): string => {
|
|
|
244
244
|
return call.tint ? `Object.assign(${src}, { tintColor: (${call.tint}) })` : src
|
|
245
245
|
}
|
|
246
246
|
|
|
247
|
-
|
|
247
|
+
/** Text into a template literal: backslash, backtick and `${` escaped. */
|
|
248
|
+
export const escapeTemplate = (s: string) => s.replace(/\\/g, "\\\\").replace(/`/g, "\\`").replace(/\$\{/g, "\\${")
|
|
248
249
|
|
|
249
250
|
// ---- the macro pass -----------------------------------------------------------------------------
|
|
250
251
|
|
|
@@ -92,7 +92,8 @@ export type AssetMacroRef = { path: string, index: number, length: number, line:
|
|
|
92
92
|
* image, a sound, a video, a model, a font, raw bytes) plus `.svg` (an SvgSource, what UIImage
|
|
93
93
|
* takes). Not `.json`, whose asset is its parsed data — a `.json` path in a string stays a string. */
|
|
94
94
|
const BARE_PATH_EXT = /\.(png|jpe?g|webp|gif|avif|bmp|svg|mp3|wav|ogg|m4a|aac|flac|mp4|webm|mov|m4v|glb|gltf|ktx2|hdr|exr|ttf|otf|woff2?|bin|bake|filamat)$/i
|
|
95
|
-
|
|
95
|
+
/** A module the bundler parses as code (and .json, which is data it understands): never a text asset. */
|
|
96
|
+
export const CODE_EXT = /\.([tj]sx?|mjs|cjs|json)$/i
|
|
96
97
|
// What precedes an import specifier: the literal is a module path, never a resource reference.
|
|
97
98
|
const IMPORT_BEFORE = /(?:\bfrom|\bimport|\bimport\s*\(|\brequire\s*\()\s*$/
|
|
98
99
|
|
|
@@ -8,6 +8,8 @@ import { detectEntry } from "./detectEntry"
|
|
|
8
8
|
import { buildHeader } from "./header"
|
|
9
9
|
import { inlineSourceMapComment, offsetSourceMap } from "./sourcemap"
|
|
10
10
|
import { ClientPublishError, clientPublishes, stubServerEntries } from "./serverSplit"
|
|
11
|
+
import { CODE_EXT } from "./assetMacro"
|
|
12
|
+
import { escapeTemplate } from "./assetIconMacro"
|
|
11
13
|
|
|
12
14
|
export type CompileFileType = "text" | "resource" | "shader"
|
|
13
15
|
|
|
@@ -145,12 +147,20 @@ export const entriesToFiles = (entries: CompileEntry[], opts: { publicUrl: strin
|
|
|
145
147
|
files[e.path] = `export default ""` // local-only shader with no compiled artifact yet
|
|
146
148
|
}
|
|
147
149
|
} else if (e.path.toLowerCase().endsWith(".svg")) {
|
|
148
|
-
|
|
150
|
+
// The file's text sits in a template literal: a backtick, `${` or a backslash in it (an
|
|
151
|
+
// editor's `<!-- exported by \`tool\` -->` comment) would otherwise end the literal and fail
|
|
152
|
+
// the compile with a chisel parse error that names nothing a person can act on.
|
|
153
|
+
files[e.path] = `export default SvgSource(\`${escapeTemplate(e.text ?? "")}\`)`
|
|
149
154
|
} else if (e.path.toLowerCase().endsWith(".json")) {
|
|
150
155
|
// A .json project file is DATA, not code: `import data from "./x.json"` — and `asset("./x.json")`,
|
|
151
156
|
// which desugars to that import — yields the parsed value. The bundler would otherwise try to
|
|
152
157
|
// parse the file as JS. JSON is an expression, so a default export is the whole translation.
|
|
153
158
|
files[e.path] = `export default ${e.text?.trim() || "null"}`
|
|
159
|
+
} else if (!CODE_EXT.test(e.path)) {
|
|
160
|
+
// Any other text file (.txt, .csv, .md, .css, .html, .xml, .yml …) is its TEXT: `asset("./levels.csv")`
|
|
161
|
+
// yields the string. Handing the raw text to the bundler as a module parsed as JS failed at
|
|
162
|
+
// run time instead (`__asset_0 is not defined`), after a compile that said nothing.
|
|
163
|
+
files[e.path] = `export default ${JSON.stringify(e.text ?? "")}`
|
|
154
164
|
} else {
|
|
155
165
|
files[e.path] = e.text ?? ""
|
|
156
166
|
}
|
|
@@ -158,13 +168,27 @@ export const entriesToFiles = (entries: CompileEntry[], opts: { publicUrl: strin
|
|
|
158
168
|
return files
|
|
159
169
|
}
|
|
160
170
|
|
|
161
|
-
/**
|
|
171
|
+
/** app.json's `"plugins"` in either shape — `{ "camera": "*" }` (id → version spec) or
|
|
172
|
+
* `["camera"]` (ids, every version `*`) — as id → spec. Anything else is no plugin. The list
|
|
173
|
+
* form is what CLAUDE.md and most hands write first; reading it as "no plugins" dropped the
|
|
174
|
+
* plugin from a web build and a shell with one warning nobody tied to the shape. */
|
|
175
|
+
export const pluginSpecsOf = (plugins: unknown): Record<string, string> => {
|
|
176
|
+
if (Array.isArray(plugins)) return Object.fromEntries(plugins.filter((p): p is string => typeof p === "string").map((id) => [id, "*"]))
|
|
177
|
+
if (plugins && typeof plugins === "object") {
|
|
178
|
+
return Object.fromEntries(Object.entries(plugins as Record<string, unknown>).map(([id, v]) => [id, typeof v === "string" ? v : "*"]))
|
|
179
|
+
}
|
|
180
|
+
return {}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/** The ids of app.json's `"plugins"` (either shape), in its order. */
|
|
184
|
+
export const pluginIdsOf = (plugins: unknown): string[] => Object.keys(pluginSpecsOf(plugins))
|
|
185
|
+
|
|
186
|
+
/** The ids app.json lists under `"plugins"`, in its order. */
|
|
162
187
|
export const pluginsOfAppJson = (entries: CompileEntry[]): string[] => {
|
|
163
188
|
const app = entries.find(e => e.path.replace(/^[./\\]+/, "") === "app.json")
|
|
164
189
|
if (!app?.text) return []
|
|
165
190
|
try {
|
|
166
|
-
|
|
167
|
-
return plugins && typeof plugins === "object" && !Array.isArray(plugins) ? Object.keys(plugins) : []
|
|
191
|
+
return pluginIdsOf((JSON.parse(app.text) as { plugins?: unknown }).plugins)
|
|
168
192
|
} catch {
|
|
169
193
|
return [] // a broken app.json is reported by whoever reads it as a config
|
|
170
194
|
}
|
package/src/compile/header.ts
CHANGED
|
@@ -28,6 +28,18 @@ export type HeaderOptions = {
|
|
|
28
28
|
plugins?: string[]
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
/** The optional engines of the game stack, by the bundled bridge calls that mean "in use": a body,
|
|
32
|
+
* a character, a vehicle or a ragdoll created, a ray cast, a sound loaded or played, the net
|
|
33
|
+
* bridge named, a navmesh call. Not the calls every 3D bundle carries (Node's body pose setters,
|
|
34
|
+
* Scene's audio listener). A `Physics.supported` probe pulls the Physics class whole, so it
|
|
35
|
+
* counts as use — the full variant is the safe side of that line. */
|
|
36
|
+
export const ENGINE_SIGNALS: readonly (readonly [name: "physics" | "net" | "nav" | "audio", re: RegExp])[] = [
|
|
37
|
+
["physics", /_creator\.(?:physicsCreateBody|physicsConfigure|physicsRaycast|characterCreate|vehicleCreate|ragdollCreate)\(/],
|
|
38
|
+
["net", /\b_creatorNet\b/], // the net core takes the bridge into a local once (core.ts bridge()): its calls never name it
|
|
39
|
+
["nav", /_creatorNav\.\w+\(/],
|
|
40
|
+
["audio", /_creatorAudio\.(?:loadClip|play)\(/], // a sound loaded or played; Scene's listener setters ride in every 3D bundle
|
|
41
|
+
]
|
|
42
|
+
|
|
31
43
|
export const buildHeader = (name: string, code: string, opts: HeaderOptions = {}): string => {
|
|
32
44
|
let header = `// sdk: ${SDK_VERSION}\n`
|
|
33
45
|
header += `// name: ${name.slice(0, 20)}\n`
|
|
@@ -51,6 +63,11 @@ export const buildHeader = (name: string, code: string, opts: HeaderOptions = {}
|
|
|
51
63
|
// bundle, and nothing 2D works without a Scene2D anyway.
|
|
52
64
|
if (code.includes("_creator2d.createScene")) header += `// 2d: enable\n`
|
|
53
65
|
|
|
66
|
+
// The game stack a shell's SDK variant must carry (the `3d` variant has no Jolt, net, nav or
|
|
67
|
+
// game audio; `2d` / `core` no net or nav): the CLI's resolveVariant reads these lines, so a 3D
|
|
68
|
+
// game with physics no longer gets a variant where Physics.configure throws on the device.
|
|
69
|
+
for (const [name, re] of ENGINE_SIGNALS) if (re.test(code)) header += `// ${name}: enable\n`
|
|
70
|
+
|
|
54
71
|
if (usesAR(code, opts.plugins)) header += `// ar: enable\n`
|
|
55
72
|
|
|
56
73
|
// Short, so with the engine lines: a host reads it before it registers anything.
|
package/src/compile/index.ts
CHANGED
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* The client/server split of a project (docs/backend-plan.md §4).
|
|
3
3
|
*
|
|
4
4
|
* `*.server.ts` files never reach the client bundle: `compileProject` swaps each one for a STUB
|
|
5
|
-
* module whose exports are RPC/channel proxies (`__rpc`, `__channel`, `__serverOnly` — client SDK
|
|
5
|
+
* module whose exports are RPC/channel proxies (`__rpc`, `__channel`, `__pushChannel`, `__serverOnly` — client SDK
|
|
6
6
|
* globals, src/runtime/rpc.ts), so `import { listNotes } from "./notes.server"` in client code keeps
|
|
7
7
|
* its types and calls the server. `compileServerBundle` bundles the same files with the SERVER inject
|
|
8
8
|
* behind a synthetic entry that registers every module on `globalThis.__lecodesServer` (chisel keeps
|
|
9
9
|
* side effects, not entry exports) — the runner reads that (src/server/runtime.ts).
|
|
10
10
|
*
|
|
11
11
|
* Export kinds are found by a static scan (no TS program needed): a function export is an
|
|
12
|
-
* endpoint, a `channel<…>()…` export is a channel, anything else is server-only.
|
|
12
|
+
* endpoint, a `channel<…>()…` export is a channel, a `pushChannel<…>()` export a push channel, anything else is server-only.
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
import type { AppConfig } from "../server/auth/appConfig"
|
|
@@ -29,7 +29,7 @@ export const serverModuleId = (path: string) => path.split("\\").join("/").repla
|
|
|
29
29
|
* a helper: a function that is not async is used by server files alone, and the app's import of
|
|
30
30
|
* it is refused at compile time (serverTypes.ts), never a call that silently holds a promise.
|
|
31
31
|
*/
|
|
32
|
-
export type ServerExportKind = "fn" | "channel" | "other"
|
|
32
|
+
export type ServerExportKind = "fn" | "channel" | "push" | "other"
|
|
33
33
|
export type ServerExport = { name: string, kind: ServerExportKind }
|
|
34
34
|
|
|
35
35
|
// ───────────────────────────── static export scan ─────────────────────────────
|
|
@@ -66,6 +66,7 @@ const IDENT = "[A-Za-z_$][\\w$]*"
|
|
|
66
66
|
const classifyInit = (init: string): ServerExportKind => {
|
|
67
67
|
const s = init.trimStart()
|
|
68
68
|
if (/^channel\s*[<(]/.test(s)) return "channel"
|
|
69
|
+
if (/^pushChannel\s*[<(]/.test(s)) return "push"
|
|
69
70
|
if (/^async\s+function\b/.test(s)) return "fn"
|
|
70
71
|
if (/^async\b/.test(s)) return "fn" // async (…) => / async x =>
|
|
71
72
|
return "other" // a sync function, a value: the server's own
|
|
@@ -113,38 +114,43 @@ export const scanServerExports = (source: string, path = "<server module>"): Ser
|
|
|
113
114
|
// ───────────────────────────── client stubs ─────────────────────────────
|
|
114
115
|
|
|
115
116
|
/** The client-side replacement of a `*.server.ts` module. `serverUrl` is baked in (draft/prod are
|
|
116
|
-
* separate compiles); "" means "no server deployed" — calls fail with a clear error.
|
|
117
|
-
|
|
117
|
+
* separate compiles); "" means "no server deployed" — calls fail with a clear error. `push` = the project
|
|
118
|
+
* has push channels (in this module or another): the stub opens by installing the push runtime, so the
|
|
119
|
+
* transport registers the device when the server asks, whether or not the app hears a channel itself. */
|
|
120
|
+
export const serverStubSource = (path: string, exports: ServerExport[], serverUrl: string, push = exports.some(e => e.kind === "push")): string => {
|
|
118
121
|
const mod = serverModuleId(path)
|
|
119
122
|
const url = JSON.stringify(serverUrl)
|
|
120
123
|
const lines = exports.map(({ name, kind }) => {
|
|
121
124
|
const id = JSON.stringify(`${mod}#${name}`)
|
|
122
125
|
if (kind === "fn") return `export const ${name} = __rpc(${url}, ${id})`
|
|
123
126
|
if (kind === "channel") return `export const ${name} = __channel(${url}, ${id})`
|
|
127
|
+
if (kind === "push") return `export const ${name} = __pushChannel(${url}, ${id})`
|
|
124
128
|
return `export const ${name} = __serverOnly(${id})`
|
|
125
129
|
})
|
|
130
|
+
if (push) lines.unshift("__pushEnable()")
|
|
126
131
|
return `// client stub of ${mod} — generated by the lecodes compiler\n${lines.join("\n")}\n`
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
// ───────────────────────────── a channel in the app ─────────────────────────────
|
|
130
135
|
|
|
131
136
|
/** A channel export has ONE type on both sides, so `publish` type-checks in the app — where the
|
|
132
|
-
* export is the subscribing proxy. Said at compile time, with what to do instead. */
|
|
137
|
+
* export is the subscribing proxy; a push channel's `send` likewise. Said at compile time, with what to do instead. */
|
|
133
138
|
export class ClientPublishError extends Error {
|
|
134
139
|
readonly problems: string[]
|
|
135
140
|
constructor(problems: string[]) {
|
|
136
|
-
super(`a channel goes from the server to the app — the app
|
|
141
|
+
super(`a channel goes from the server to the app — the app listens; to send, call a server function that publishes (a push channel: that sends):\n ${problems.join("\n ")}`)
|
|
137
142
|
this.name = "ClientPublishError"
|
|
138
143
|
this.problems = problems
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
/** `<channel>.publish(` in the project's client files, for every channel imported from a `*.server.ts`. */
|
|
147
|
+
/** `<channel>.publish(` and `<pushChannel>.send(` in the project's client files, for every channel imported from a `*.server.ts`. */
|
|
143
148
|
export const clientPublishes = (entries: CompileEntry[]): string[] => {
|
|
144
|
-
|
|
149
|
+
/** module → export name → the server's verb */
|
|
150
|
+
const channels = new Map<string, Map<string, string>>()
|
|
145
151
|
for (const [path, exports] of Object.entries(scanServerModules(entries))) {
|
|
146
|
-
const names = exports.filter(e => e.kind === "channel").map(e => e.name)
|
|
147
|
-
if (names.
|
|
152
|
+
const names = new Map(exports.filter(e => e.kind === "channel" || e.kind === "push").map(e => [e.name, e.kind === "push" ? "send" : "publish"]))
|
|
153
|
+
if (names.size) channels.set(path.replace(/\.[tj]s$/, ""), names)
|
|
148
154
|
}
|
|
149
155
|
if (channels.size === 0) return []
|
|
150
156
|
const out: string[] = []
|
|
@@ -152,21 +158,22 @@ export const clientPublishes = (entries: CompileEntry[]): string[] => {
|
|
|
152
158
|
if (e.type !== "text" || isServerPath(e.path) || !/\.[tj]sx?$/.test(e.path)) continue
|
|
153
159
|
const src = e.text ?? ""
|
|
154
160
|
const dir = normalizePath(e.path).replace(/\/[^/]*$/, "")
|
|
155
|
-
const locals: string[] = []
|
|
161
|
+
const locals: [string, string][] = []
|
|
156
162
|
for (const m of src.matchAll(/import\s*\{([^}]*)\}\s*from\s*["']([^"']+)["']/g)) {
|
|
157
163
|
if (!m[2].startsWith(".")) continue
|
|
158
164
|
const names = channels.get(normalizePath(`${dir}/${m[2]}`).replace(/\.[tj]s$/, ""))
|
|
159
165
|
if (!names) continue
|
|
160
166
|
for (const part of m[1].split(",")) {
|
|
161
167
|
const [imported, local] = part.split(/\s+as\s+/).map(s => s.trim())
|
|
162
|
-
|
|
168
|
+
const verb = imported ? names.get(imported) : undefined
|
|
169
|
+
if (verb) locals.push([local ?? imported, verb])
|
|
163
170
|
}
|
|
164
171
|
}
|
|
165
172
|
if (!locals.length) continue
|
|
166
173
|
const code = blankCommentsAndStrings(src)
|
|
167
|
-
for (const local of locals) {
|
|
168
|
-
for (const m of code.matchAll(new RegExp(`(?<![\\w$.])${local.replace(/\$/g, "\\$")}\\s*\\.\\s
|
|
169
|
-
out.push(`${serverModuleId(e.path)}:${code.slice(0, m.index).split("\n").length}: ${local}
|
|
174
|
+
for (const [local, verb] of locals) {
|
|
175
|
+
for (const m of code.matchAll(new RegExp(`(?<![\\w$.])${local.replace(/\$/g, "\\$")}\\s*\\.\\s*${verb}\\s*\\(`, "g"))) {
|
|
176
|
+
out.push(`${serverModuleId(e.path)}:${code.slice(0, m.index).split("\n").length}: ${local}.${verb}(…)`)
|
|
170
177
|
}
|
|
171
178
|
}
|
|
172
179
|
}
|
|
@@ -228,10 +235,13 @@ export const scanServerModules = (entries: CompileEntry[]): Record<string, Serve
|
|
|
228
235
|
}
|
|
229
236
|
|
|
230
237
|
/** Replace each `*.server.ts` entry with its client stub (returns a new entries array). */
|
|
231
|
-
export const stubServerEntries = (entries: CompileEntry[], serverUrl: string): CompileEntry[] =>
|
|
232
|
-
|
|
233
|
-
|
|
238
|
+
export const stubServerEntries = (entries: CompileEntry[], serverUrl: string): CompileEntry[] => {
|
|
239
|
+
const modules = scanServerModules(entries)
|
|
240
|
+
const push = Object.values(modules).some(exports => exports.some(e => e.kind === "push"))
|
|
241
|
+
return entries.map(e => e.type === "text" && isServerPath(e.path)
|
|
242
|
+
? { ...e, text: serverStubSource(e.path, modules[normalizePath(e.path)] ?? [], serverUrl, push) }
|
|
234
243
|
: e)
|
|
244
|
+
}
|
|
235
245
|
|
|
236
246
|
export type CompileServerOptions = {
|
|
237
247
|
entries: CompileEntry[]
|
|
@@ -237,7 +237,7 @@ export const deriveEndpointParams = async (entries: CompileEntry[], modules: Rec
|
|
|
237
237
|
if (!modSym) continue
|
|
238
238
|
const exported = new Map(checker.getExportsOfModule(modSym).map(s => [s.name, s]))
|
|
239
239
|
for (const ex of exports) {
|
|
240
|
-
if (ex.kind === "other") continue
|
|
240
|
+
if (ex.kind === "other" || ex.kind === "push") continue
|
|
241
241
|
const id = `${serverModuleId(path)}#${ex.name}`
|
|
242
242
|
let sym = exported.get(ex.name)
|
|
243
243
|
if (!sym) { warnings.push(`${id}: export not found by the checker — unvalidated`); continue }
|
package/src/core/Aspect.ts
CHANGED
|
@@ -403,7 +403,8 @@ export abstract class AspectHost<E extends EventMap = EventMap> extends Emitter<
|
|
|
403
403
|
}
|
|
404
404
|
|
|
405
405
|
// ---- systems: aspects of a scene --------------------------------------------
|
|
406
|
-
/**
|
|
406
|
+
/** What a scene needs to carry systems: Scene and Scene2D implement it over the helpers below. Public
|
|
407
|
+
* (never stripped from the public types: the `System` type names it). */
|
|
407
408
|
export type SystemHost = { readonly _systems: Map<Function, Aspect<any, any, any>> }
|
|
408
409
|
|
|
409
410
|
/** @internal `scene.system(Ctor, opts)`. */
|
package/src/g2/Physics2D.ts
CHANGED
|
@@ -231,9 +231,12 @@ export class Physics2D extends Aspect<"physics", Node2D> {
|
|
|
231
231
|
}
|
|
232
232
|
private static _gravity: [number, number] = [ 0, -980 ]
|
|
233
233
|
|
|
234
|
-
/** Whether this
|
|
235
|
-
*
|
|
236
|
-
|
|
234
|
+
/** Whether this host has 2D physics (a 2D engine built with CREATOR_2D_PHYSICS). False, never a
|
|
235
|
+
* throw, where there is no 2D engine at all (Web Lite, a 3d / core engine variant). It reports
|
|
236
|
+
* the build, not the world — there is nothing else to guard, since the world creates itself. */
|
|
237
|
+
static get supported(): boolean {
|
|
238
|
+
return typeof _creator2d !== "undefined" && !!_creator2d && typeof _creator2d.physicsHasSupport === "function" && _creator2d.physicsHasSupport()
|
|
239
|
+
}
|
|
237
240
|
|
|
238
241
|
/** Render interpolation of body transforms between the fixed 60 Hz steps (global; default on). Turn
|
|
239
242
|
* off to save per-frame transform writes when you have many moving bodies / don't need >60 Hz
|
package/src/g2/Scene2D.ts
CHANGED
|
@@ -62,6 +62,12 @@ export class Scene2D implements Presentable {
|
|
|
62
62
|
private static _active: Scene2D | null = null
|
|
63
63
|
static get active(): Scene2D | null { return Scene2D._active }
|
|
64
64
|
|
|
65
|
+
/** Whether this host has the 2D engine. False on Web Lite (UI and 3D only) and in a 3d / core
|
|
66
|
+
* engine variant of an app built with `lecodes app`; there `new Scene2D()` throws. */
|
|
67
|
+
static get supported(): boolean {
|
|
68
|
+
return typeof _creator2d !== "undefined" && !!_creator2d && typeof _creator2d.createScene === "function"
|
|
69
|
+
}
|
|
70
|
+
|
|
65
71
|
/** @internal — scene-level pointer listeners (the catch-all; target = hit node or null). */
|
|
66
72
|
readonly _clickListeners: Array<(ev: ClickEvent<Node2D | null>) => void> = []
|
|
67
73
|
readonly _touchStartListeners: Array<(ev: TouchStartEvent<Node2D | null>) => void> = []
|
|
@@ -70,6 +76,7 @@ export class Scene2D implements Presentable {
|
|
|
70
76
|
private readonly _filter?: 'nearest' | 'linear'
|
|
71
77
|
|
|
72
78
|
constructor(options: Scene2DOptions = {}) {
|
|
79
|
+
if (!Scene2D.supported) throw new Error("Scene2D: this host has no 2D engine (Web Lite runs UI and 3D apps; a 3d / core engine variant has no 2D) — check Scene2D.supported before creating one")
|
|
73
80
|
installRenderSyncedFrames()
|
|
74
81
|
this.id = _creator2d.createScene()
|
|
75
82
|
this.camera = new Camera2D(this.id)
|
|
@@ -121,7 +128,8 @@ export class Scene2D implements Presentable {
|
|
|
121
128
|
}
|
|
122
129
|
|
|
123
130
|
// ---- systems: aspects of the scene (core/Aspect.ts `System<'x', Scene2D>`) ----
|
|
124
|
-
/**
|
|
131
|
+
/** class → instance, the authoritative store (named accessors mirror this). Kept in the public
|
|
132
|
+
* types: it is what makes a Scene2D a `SystemHost` there. */
|
|
125
133
|
readonly _systems = new Map<Function, Aspect<any, any, any>>()
|
|
126
134
|
|
|
127
135
|
/** Attach (and configure) a system, or reconfigure it if already present. Returns the scene typed
|
package/src/gl/Scene.ts
CHANGED
|
@@ -206,7 +206,14 @@ export class Scene implements Presentable {
|
|
|
206
206
|
private static _active: Scene | null = null
|
|
207
207
|
static get active(): Scene | null { return Scene._active }
|
|
208
208
|
|
|
209
|
+
/** Whether this host has the 3D engine. False where the bundle was marked `gl: disable` (no scene
|
|
210
|
+
* created anywhere in it) and in a 2d / core engine variant; there `new Scene()` throws. */
|
|
211
|
+
static get supported(): boolean {
|
|
212
|
+
return typeof _creator !== "undefined" && !!_creator && typeof _creator.createScene === "function"
|
|
213
|
+
}
|
|
214
|
+
|
|
209
215
|
constructor(options: SceneOptions = {}) {
|
|
216
|
+
if (!Scene.supported) throw new Error("Scene: this host has no 3D engine (a 2d / core engine variant, or a bundle that creates no scene) — check Scene.supported before creating one")
|
|
210
217
|
installRenderSyncedFrames()
|
|
211
218
|
const parent = (options as any)._parent as Scene | undefined
|
|
212
219
|
if (parent) {
|
|
@@ -366,7 +373,8 @@ export class Scene implements Presentable {
|
|
|
366
373
|
}
|
|
367
374
|
|
|
368
375
|
// ---- systems: aspects of the scene (core/Aspect.ts `System`) ----
|
|
369
|
-
/**
|
|
376
|
+
/** class → instance, the authoritative store (named accessors mirror this). Kept in the public
|
|
377
|
+
* types: it is what makes a Scene a `SystemHost` there. */
|
|
370
378
|
readonly _systems = new Map<Function, Aspect<any, any, any>>()
|
|
371
379
|
|
|
372
380
|
/** Attach (and configure) a system, or reconfigure it if already present. Returns the scene typed
|
package/src/gl/Texture.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// A 3D GPU texture, decoded natively by creator-gl. Load from a URL or an already-fetched response.
|
|
2
2
|
|
|
3
3
|
import { fetch, type FetchResponse, type File } from "../runtime/fetch"
|
|
4
|
+
import type { Canvas } from "../canvas/Canvas"
|
|
4
5
|
|
|
5
6
|
/** `Texture.load` options. */
|
|
6
7
|
export type TextureLoadOptions = {
|
|
@@ -57,7 +58,7 @@ export class Texture {
|
|
|
57
58
|
// Wrap a Canvas's baked surface as a 3D texture (RGBA8, already rasterized → synchronous, no decode).
|
|
58
59
|
// The id is cached on the canvas (opaque _tex3dId), so backing several materials shares one texture and
|
|
59
60
|
// canvas.update() re-uploads it. Structural type avoids a Canvas import (keeps 3D free of 2D-canvas code).
|
|
60
|
-
static fromCanvas(canvas:
|
|
61
|
+
static fromCanvas(canvas: Canvas): Texture {
|
|
61
62
|
if (!canvas._tex3dId) {
|
|
62
63
|
const surfaceId = canvas._bakeSurface()
|
|
63
64
|
canvas._tex3dId = _creator.createTextureFromCanvas(surfaceId)
|
|
@@ -54,8 +54,8 @@ const FLOOR_NONE = 0, FLOOR_PLANE = 1, FLOOR_PROBE = 2
|
|
|
54
54
|
|
|
55
55
|
let warned = false
|
|
56
56
|
const supported = (): boolean => {
|
|
57
|
-
if (
|
|
58
|
-
if (!warned) { warned = true; console.warn("DynamicBone
|
|
57
|
+
if (DynamicBone.supported) return true
|
|
58
|
+
if (!warned) { warned = true; console.warn("DynamicBone: this host has no dynamic bones (DynamicBone.supported is false) — the animation shows as is") }
|
|
59
59
|
return false
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -134,6 +134,10 @@ export class DynamicBoneCollider extends Aspect<"dynamicBoneCollider", Node> {
|
|
|
134
134
|
export class DynamicBone extends Aspect<"dynamicBone", Node> {
|
|
135
135
|
static readonly aspect = "dynamicBone"
|
|
136
136
|
|
|
137
|
+
/** Whether this host simulates dynamic bones. False, never a throw, on Web Lite and in headless
|
|
138
|
+
* tests; there the aspect attaches, warns once, and `chain` stays empty. */
|
|
139
|
+
static get supported(): boolean { return typeof _creator !== "undefined" && !!_creator && typeof _creator.dynamicBoneCreate === "function" }
|
|
140
|
+
|
|
137
141
|
/** Only these bones (by name) join the chain — the root always does, a bone left out takes its
|
|
138
142
|
* subtree with it. Default: the root's whole subtree. */
|
|
139
143
|
bones?: string[]
|
|
@@ -225,9 +225,15 @@ export type LocomotionEvents = {
|
|
|
225
225
|
state: (state: LocomotionState) => void
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
let locoWarned = false
|
|
229
|
+
|
|
228
230
|
export class Locomotion extends Aspect<"loco", Node, LocomotionEvents> {
|
|
229
231
|
static readonly aspect = "loco"
|
|
230
232
|
|
|
233
|
+
/** Whether this host runs the locomotion selector. False, never a throw, on Web Lite and in
|
|
234
|
+
* headless tests; there the aspect attaches, warns once, and the body stays `'idle'`. */
|
|
235
|
+
static get supported(): boolean { return typeof _creator !== "undefined" && !!_creator && typeof _creator.locoCreate === "function" }
|
|
236
|
+
|
|
231
237
|
/** The animator whose clips are shown. Default: the first one on this node or under it (a Model
|
|
232
238
|
* child), which is the usual character — a capsule node with the model inside. */
|
|
233
239
|
animator?: Animator
|
|
@@ -281,7 +287,11 @@ export class Locomotion extends Aspect<"loco", Node, LocomotionEvents> {
|
|
|
281
287
|
* walk, a slowed-down character). */
|
|
282
288
|
rebuild(): void {
|
|
283
289
|
const anim = this.anim
|
|
284
|
-
if (!
|
|
290
|
+
if (!Locomotion.supported) {
|
|
291
|
+
if (!locoWarned) { locoWarned = true; console.warn("Locomotion: this host has no locomotion engine (Locomotion.supported is false) — the character stays idle") }
|
|
292
|
+
return
|
|
293
|
+
}
|
|
294
|
+
if (!anim) return
|
|
285
295
|
if (!this._loco) {
|
|
286
296
|
const first = this.set.idle ?? (this.set.gaits ?? [])[0] // binding a clip is what creates the native animator
|
|
287
297
|
if (first !== undefined) anim._slot(name(first))
|
|
@@ -209,9 +209,10 @@ export class Physics extends Aspect<"physics", Node> implements CompWriter {
|
|
|
209
209
|
_creator.physicsConfigure(cx(g), cy(g), cz(g), config.maxBodies ?? 0)
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
/** Whether this
|
|
212
|
+
/** Whether this host has 3D physics (an engine built with CREATOR_GL_PHYSICS). False, never a
|
|
213
|
+
* throw, where there is no 3D engine at all. */
|
|
213
214
|
static get supported(): boolean {
|
|
214
|
-
return !!_creator.physicsHasSupport && _creator.physicsHasSupport()
|
|
215
|
+
return typeof _creator !== "undefined" && !!_creator && typeof _creator.physicsHasSupport === "function" && _creator.physicsHasSupport()
|
|
215
216
|
}
|
|
216
217
|
|
|
217
218
|
/** Closest pickable body hit by the ray from `origin` along `dir` (up to `maxDist`), or null. */
|
|
@@ -45,7 +45,7 @@ import type { FieldMeta } from "../../core/fields"
|
|
|
45
45
|
import { Mathf } from "../../math/Mathf"
|
|
46
46
|
import { cx, cy, cz, Vec3, type Vec3Like } from "../../math/vec"
|
|
47
47
|
import { Node } from "../Node"
|
|
48
|
-
import { DEFAULT_FRICTION } from "./Physics"
|
|
48
|
+
import { DEFAULT_FRICTION, Physics } from "./Physics"
|
|
49
49
|
|
|
50
50
|
export interface RagdollHinge {
|
|
51
51
|
/** The hinge axis in the MODEL's space (its rest pose: +Z forward, +Y up, +X the model's left in
|
|
@@ -169,6 +169,11 @@ export const humanoidParts = (find: (role: string) => string | null): RagdollPar
|
|
|
169
169
|
export class Ragdoll extends Aspect<"ragdoll", Node> {
|
|
170
170
|
static readonly aspect = "ragdoll"
|
|
171
171
|
|
|
172
|
+
/** Whether this host can drop a body: 3D physics with ragdoll parts. False, never a throw,
|
|
173
|
+
* without physics or on an older host. A host that has it can still refuse the parts
|
|
174
|
+
* (`activate()` returns false). */
|
|
175
|
+
static get supported(): boolean { return Physics.supported && typeof _creator.ragdollCreate === "function" }
|
|
176
|
+
|
|
172
177
|
/** The parts: `'humanoid'` (default) finds the standard bones by name; a list places bodies on any
|
|
173
178
|
* bones — parents before children. */
|
|
174
179
|
parts: RagdollPart[] | "humanoid" = "humanoid"
|
|
@@ -243,9 +248,9 @@ export class Ragdoll extends Aspect<"ragdoll", Node> {
|
|
|
243
248
|
private _release: { left: number; blend: number } | null = null
|
|
244
249
|
|
|
245
250
|
onAttach(): void {
|
|
246
|
-
if (!
|
|
251
|
+
if (!Physics.supported) return
|
|
247
252
|
if (!_creator.ragdollCreate) {
|
|
248
|
-
console.warn("Ragdoll
|
|
253
|
+
console.warn("Ragdoll: this host has no ragdoll parts (Ragdoll.supported is false) — the body will not fall")
|
|
249
254
|
return
|
|
250
255
|
}
|
|
251
256
|
const find = (role: string): string | null => {
|
|
@@ -194,6 +194,11 @@ const clamp = (v: number, lo: number, hi: number): number => (v < lo ? lo : v >
|
|
|
194
194
|
export class Vehicle extends Aspect<"vehicle", Node> {
|
|
195
195
|
static readonly aspect = "vehicle"
|
|
196
196
|
|
|
197
|
+
/** Whether this host can drive a car: 3D physics with the vehicle model. False, never a throw,
|
|
198
|
+
* without physics or on an older host. A host that has it can still refuse a car's settings
|
|
199
|
+
* (`id` stays 0, with a warning). */
|
|
200
|
+
static get supported(): boolean { return Physics.supported && typeof _creator.vehicleCreate === "function" }
|
|
201
|
+
|
|
197
202
|
/** Chassis mass in kg (default 1200). Wheels are massless in this model. */
|
|
198
203
|
mass = 1200
|
|
199
204
|
/** Centre of mass height in chassis space (metres, negative = lower). Left out, it drops to halfway
|
|
@@ -274,9 +279,9 @@ export class Vehicle extends Aspect<"vehicle", Node> {
|
|
|
274
279
|
private _retunePending = false
|
|
275
280
|
|
|
276
281
|
onAttach(): void {
|
|
277
|
-
if (!
|
|
282
|
+
if (!Physics.supported) return
|
|
278
283
|
if (!_creator.vehicleCreate) {
|
|
279
|
-
console.warn("Vehicle
|
|
284
|
+
console.warn("Vehicle: this host has no vehicle model (Vehicle.supported is false) — the car will not move")
|
|
280
285
|
return
|
|
281
286
|
}
|
|
282
287
|
const shape = this.node.get(Shape)
|
package/src/host.d.ts
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
import type { SvgSource } from "./runtime/misc"
|
|
7
7
|
|
|
8
8
|
declare global {
|
|
9
|
-
|
|
10
|
-
|
|
9
|
+
/** As in the browser, but the delay is REQUIRED (the native runtime throws without it) and no
|
|
10
|
+
* extra argument reaches the handler. Timers tick with the frame on every native host: one
|
|
11
|
+
* frame is the resolution. */
|
|
12
|
+
function setTimeout(handler: () => void, timeout: number): number
|
|
13
|
+
function setInterval(handler: () => void, timeout: number): number
|
|
11
14
|
function clearTimeout(id: number): void
|
|
12
15
|
function clearInterval(id: number): void
|
|
13
16
|
/** Per-frame loop, driven by the host (viewer RAF / desktop runTick). `dt` is **seconds** since
|
|
@@ -38,6 +41,9 @@ declare global {
|
|
|
38
41
|
/** Compile-time macro: `asset('./logo.svg')` yields the file as an SVG image source — for
|
|
39
42
|
* `UIImage(...)` and `bgImage`. */
|
|
40
43
|
function asset(path: `${string}.svg`): SvgSource
|
|
44
|
+
/** Compile-time macro: `asset('./levels.csv')` yields a text file's TEXT (.txt, .csv, .md, .css,
|
|
45
|
+
* .html, .xml, .yml — it ships inside the bundle). Parse it yourself. */
|
|
46
|
+
function asset(path: `${string}.${"txt" | "csv" | "md" | "css" | "html" | "xml" | "yml" | "yaml"}`): string
|
|
41
47
|
/** Compile-time macro: `asset('./hero.png')` is desugared by the bundler into the module import
|
|
42
48
|
* for that resource. The file must exist — a path that resolves to nothing fails the compile
|
|
43
49
|
* (`asset not found: ./hero.png (main.ts:3)`); there is no runtime fallback. Calls inside
|
package/src/inject.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
// ---- math ----
|
|
11
11
|
export { Mathf } from "./math/Mathf"
|
|
12
|
-
export { Vec2, Vec3 } from "./math/vec"
|
|
12
|
+
export { Vec2, Vec3, type Vec2Like, type Vec3Like } from "./math/vec"
|
|
13
13
|
export { Quat } from "./math/quat"
|
|
14
14
|
export { Mat4 } from "./math/mat4"
|
|
15
15
|
export { Color } from "./core/color"
|
|
@@ -43,10 +43,11 @@ export { WebSocket } from "./runtime/net"
|
|
|
43
43
|
export { Net, NetPlayer, Replicated, NetEntity, type NetMessage, type NetRole, type NetStatus, type NetEvents, type NetLaunch, type NetInput, type NetKind, type NetTransform, type NetCorrection } from "./net"
|
|
44
44
|
// App-backend transport: the compiler-generated stubs of `*.server.ts` modules call these; user code
|
|
45
45
|
// only ever imports its server functions (docs/backend-plan.md §4).
|
|
46
|
-
export { __rpc, __channel, __serverOnly, RpcError } from "./runtime/rpc"
|
|
46
|
+
export { __rpc, __channel, __pushChannel, __serverOnly, RpcError } from "./runtime/rpc"
|
|
47
47
|
// What a screen asks of the server, as reactive state: a request a screen follows, an action with its own busy / error.
|
|
48
48
|
export { resource, action, dataOf, failureOf, type Loaded, type Failure, type Resource, type Action } from "./runtime/resource"
|
|
49
49
|
export type { ChannelSubscription } from "./server/channel"
|
|
50
|
+
export type { PushChannel, PushMessage, PushNotification, PushTarget } from "./server/pushChannel"
|
|
50
51
|
export type { StoredFile, StoredImage } from "./server/files/models"
|
|
51
52
|
export { AudioPlayer, VideoPlayer } from "./runtime/media"
|
|
52
53
|
// Game audio (docs/audio-plan.md): decoded clips, a voice pool, buses with effects; 3D through the
|
|
@@ -162,7 +163,7 @@ export {
|
|
|
162
163
|
type MapPaddingValues, type MarkerItem, type MarkerTap, type MarkerLayer, type MarkerLayerOptions,
|
|
163
164
|
type LineLayer, type LineLayerOptions, type UserLocationOptions,
|
|
164
165
|
} from "./plugins/gen/map/sdk/map"
|
|
165
|
-
export { Push, type PushPayload, type PushStatus, type PushRegisterOptions, type PushEvent } from "./plugins/gen/push/sdk/push"
|
|
166
|
+
export { Push, __pushEnable, type PushPayload, type PushStatus, type PushRegisterOptions, type PushEvent } from "./plugins/gen/push/sdk/push"
|
|
166
167
|
export { Browser, WebView, type BrowserOpenOptions, type WebViewParams, type WebViewEvents } from "./plugins/gen/browser/sdk/browser"
|
|
167
168
|
export {
|
|
168
169
|
RTCPeerConnection, MediaStream, MediaStreamTrack, RTCDataChannel, RTCRtpSender, RTCRtpReceiver, RTCRtpTransceiver,
|
package/src/plugin.ts
CHANGED
|
@@ -44,6 +44,9 @@ export { Quat, type QuatLike } from "./math/quat"
|
|
|
44
44
|
export { Mat4 } from "./math/mat4"
|
|
45
45
|
export type { ClickEvent, TouchStartEvent } from "./runtime/touch"
|
|
46
46
|
export type { DismissOptions } from "./ui/presentable"
|
|
47
|
+
/** The seam the push plugin's wrapper installs its runtime on (src/runtime/pushSeam.ts): what the app's transport
|
|
48
|
+
* reads the device's address from, registers through, and a push channel's handlers are kept by. */
|
|
49
|
+
export { _pushRuntime, type PushRuntime } from "./runtime/pushSeam"
|
|
47
50
|
/** @internal The navigation epoch: a destination that prepares before it presents (ARScene) reads it
|
|
48
51
|
* before its awaits and gives up when another navigation moved it meanwhile. */
|
|
49
52
|
export { _navEpoch }
|