lecodes-sdk 2.0.4 → 2.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +104 -76
  2. package/dist/global.d.ts +2 -5
  3. package/dist/host.d.ts +3 -0
  4. package/dist/types/inject.d.ts +3 -3
  5. package/dist/types/runtime/device.d.ts +7 -0
  6. package/dist/types/runtime/rpc.d.ts +11 -17
  7. package/dist/types/runtime/wire.d.ts +53 -0
  8. package/dist/types/server/auth/api.d.ts +42 -0
  9. package/dist/types/server/auth/appConfig.d.ts +1 -5
  10. package/dist/types/server/auth/models.d.ts +119 -70
  11. package/dist/types/server/auth/types.d.ts +19 -43
  12. package/dist/types/server/channel.d.ts +57 -19
  13. package/dist/types/server/context.d.ts +2 -2
  14. package/dist/types/server/db/defineDb.d.ts +10 -0
  15. package/dist/types/server/db/index.d.ts +1 -1
  16. package/dist/types/server/db/types.d.ts +76 -6
  17. package/dist/types/server/inject.d.ts +0 -1
  18. package/dist/types/ui/UINode.d.ts +19 -5
  19. package/dist/types/ui/UIScreen.d.ts +1 -0
  20. package/dist/types/ui/UITabs.d.ts +8 -6
  21. package/dist/types/ui/theme.d.ts +48 -13
  22. package/dist/types/version.d.ts +1 -1
  23. package/dist/types.json +1 -1
  24. package/package.json +3 -2
  25. package/prompts/README.md +1 -1
  26. package/prompts/design.md +19 -19
  27. package/prompts/dist/2d-game.md +45 -31
  28. package/prompts/dist/3d-app.md +45 -31
  29. package/prompts/dist/ar-app.md +45 -31
  30. package/prompts/dist/design.md +25 -24
  31. package/prompts/dist/ui-app.md +45 -31
  32. package/prompts/ui-design.md +6 -5
  33. package/prompts/ui.md +25 -22
  34. package/src/bridges/device.d.ts +9 -0
  35. package/src/bridges/tree.d.ts +5 -0
  36. package/src/chisel.ts +1 -1
  37. package/src/compile/bundler.ts +6 -0
  38. package/src/compile/compileProject.ts +3 -1
  39. package/src/compile/index.ts +3 -1
  40. package/src/compile/serverSplit.ts +58 -11
  41. package/src/compile/serverTypes.ts +189 -8
  42. package/src/host.d.ts +3 -0
  43. package/src/inject.ts +6 -6
  44. package/src/runtime/device.ts +12 -0
  45. package/src/runtime/rpc.ts +101 -40
  46. package/src/runtime/wire.ts +35 -0
  47. package/src/server/auth/api.ts +94 -0
  48. package/src/server/auth/appConfig.ts +2 -3
  49. package/src/server/auth/host.ts +244 -174
  50. package/src/server/auth/models.ts +45 -62
  51. package/src/server/auth/types.ts +19 -34
  52. package/src/server/channel.ts +97 -29
  53. package/src/server/channelHub.ts +153 -0
  54. package/src/server/context.ts +2 -2
  55. package/src/server/db/defineDb.ts +96 -36
  56. package/src/server/db/index.ts +1 -1
  57. package/src/server/db/types.ts +76 -8
  58. package/src/server/host.ts +25 -10
  59. package/src/server/inject.ts +2 -2
  60. package/src/server/runtime.ts +34 -12
  61. package/src/ui/UINode.ts +22 -5
  62. package/src/ui/UIScreen.ts +5 -0
  63. package/src/ui/UITabs.ts +19 -17
  64. package/src/ui/styleColor.ts +10 -1
  65. package/src/ui/theme.ts +96 -41
  66. package/src/version.ts +1 -1
  67. package/tests/helpers/fakeTree.ts +1 -0
  68. package/dist/types/plugins/oauth.d.ts +0 -25
  69. package/dist/types/server/auth/global.d.ts +0 -56
  70. package/src/plugins/oauth.ts +0 -61
  71. package/src/server/auth/global.ts +0 -80
  72. package/tests/helpers/memoryMarci.ts +0 -124
package/README.md CHANGED
@@ -1,52 +1,76 @@
1
- # sdk — the unified, import-free scripting API
1
+ # lecodes-sdk — the import-free LeCodes API
2
2
 
3
- One coherent, **import-free** syntax for le.codes projects across **2D** (creator-2d), **3D**
4
- (creator-gl), **UI** (creator-ui), and the platform runtime. The whole library is delivered through
5
- esbuild's `inject()`, so user code uses globals (`Scene`, `Sprite`, `Mesh`, `UIScreen`, `setLoop`, …)
6
- with **no imports**; tree-shaking drops whatever a project doesn't use.
3
+ The TypeScript API LeCodes apps are written against: **UI**, **2D**, **3D** and the platform
4
+ runtime (network, storage, input, audio, device, plugins) in one surface of **globals** — user code
5
+ writes `UIScreen`, `Scene`, `Sprite`, `fetch`, … with **no imports**. The `lecodes` CLI compiles a
6
+ project through this package into one `app.js`; the bundler (chisel) keeps only the methods a
7
+ project reaches, so a UI app carries no 3D code and a 2D game no UI kit. The same bundle runs on
8
+ the LeCodes hosts: desktop, iOS, Android and the web player.
7
9
 
8
- Replaces the `worker` package. See [PLAN.md](./PLAN.md) for the full design and build log.
10
+ **API reference:** [docs/index.md](./docs/index.md) — every global, example-first; how to build
11
+ a whole thing out of it is the [cookbook](./docs/cookbook/README.md). `bun run docs:check` verifies
12
+ that every global has exactly one page and that the links resolve.
9
13
 
10
- **API reference:** the complete, curated docs live in [docs/](./docs/index.md) — every global, every
11
- method, example-first. Run `bun run docs:check` to verify coverage + links.
14
+ ```ts
15
+ // UI — no imports
16
+ let taps = 0
17
+ const label = UIText('Taps: 0').style({ color: '#fff', fontSize: 22 })
18
+
19
+ UIScreen(
20
+ label,
21
+ UIButton(UIText('Tap me').style({ color: '#fff' }))
22
+ .style({ bgColor: '#4f7cff', borderRadius: 12, p: 16, $pressed: { opacity: 0.8 } })
23
+ .onClick(() => { label.text = `Taps: ${++taps}` }),
24
+ )
25
+ .style({ bgColor: '#1e2230', alignItems: 'center', justifyContent: 'center', gap: 16 })
26
+ .open()
27
+ ```
12
28
 
13
29
  ```ts
14
- // 2D — no imports
15
- const scene = new Scene2D({ background: '#10131a' })
16
- const player = new Sprite({ texture: await Texture2D.load(asset('hero.png')), anchor: [0.5, 1] })
17
- .clips({ size: [32, 48], idle: [0, 1, 2, 3], walk: [4, 5, 6, 7] })
18
- player.play('idle')
19
- scene.add(player); scene.open()
20
- scene.camera.follow(player, { smooth: 0.15 })
21
- setLoop(dt => { if (Input.key('ArrowRight')) { player.x += 120 * dt; player.play('walk') } })
22
-
23
- // 3D — no imports
24
- const world = new Scene({ bloom: true })
25
- const ball = Mesh.sphere({ radius: 0.5, material: Material.lit({ color: '#e33' }), position: [0, 5, 0] })
26
- .physics({ motion: 'dynamic', shape: { type: 'sphere', radius: 0.5 } })
27
- world.add(ball, Light.sun({ castShadows: true }))
28
- world.camera.position = [0, 6, 12]; world.camera.lookAt([0, 0, 0])
29
- world.open()
30
- setLoop(() => { if (Input.key('Space')) ball.body.applyImpulse([0, 6, 0]) })
30
+ // 3D — no imports; behaviour is an Aspect attached to a node
31
+ Physics.configure({ gravity: [0, -9.81, 0] })
32
+
33
+ class Turn extends Aspect<'turn'> {
34
+ speed = 90 // degrees per second
35
+ private angle = 0
36
+ protected update(dt: number) { // every frame; dt = game seconds
37
+ this.angle += this.speed * dt
38
+ this.node.eulerAngles = [0, this.angle, 0]
39
+ }
40
+ }
41
+
42
+ const scene = new Scene({ skybox: '#10131a' })
43
+ const mat = Material.lit({ color: '#e33' })
44
+ const ground = Mesh.box({ material: mat, scale: [10, 0.2, 10] })
45
+ .aspect(Shape, {}) // a box collider fitted to the mesh
46
+ .aspect(Physics, { motion: 'static' })
47
+ const ball = Mesh.sphere({ material: mat, position: [0, 5, 0] })
48
+ .aspect(Shape, {})
49
+ .aspect(Physics, { mass: 1 }) // dynamic: the physics step moves it
50
+ const cube = Mesh.box({ material: mat, position: [2, 1, 0] })
51
+ .aspect(Turn, { speed: 180 }) // cube.turn is the attached aspect
52
+ scene.add(ground, ball, cube, Light.sun())
53
+ scene.camera.position = [0, 6, 12]
54
+ scene.camera.lookAt([0, 0, 0])
55
+ scene.open()
31
56
  ```
32
57
 
33
58
  ## Fluent math (`Vec2` / `Vec3` / `Quat` / `Mat4`)
34
59
 
35
- Per-frame math reads like the formula — no imports, no out-params. Vectors are *mutable structs with
36
- pure methods*: fields are settable (`v.x = 3`, `v.set(…)`), but every method returns a NEW value and
37
- never mutates its source. They're iterable and accept raw tuples anywhere, so `[0, 1, 0]` works wherever
38
- a vector does.
60
+ Per-frame math reads like the formula — no out-params. Vectors are *mutable structs with pure
61
+ methods*: fields are settable (`v.x = 3`, `v.set(…)`), but every method returns a NEW value and
62
+ never mutates its source. They are iterable and accept raw tuples anywhere, so `[0, 1, 0]` works
63
+ wherever a vector does.
39
64
 
40
65
  ```ts
41
- const dir = target.position.sub(self.position).normalize()
66
+ const dir = target.position.sub(self.position).normalize()
42
67
  self.position = self.position.add(dir.scale(speed * dt))
43
68
  if (a.distanceTo(b) < 2) explode()
44
69
  node.quaternion = node.quaternion.mul(Quat.fromAxisAngle(Vec3.up, turn))
45
70
 
46
- // the node API returns fresh copies (value semantics, like Unity):
47
- node.x = 3 // loud single-axis nudge
48
- const p = node.position; p.y = 5; node.position = p // local-mutate, then assign back
49
- // node.position.x = 3 ← a no-op (mutates a discarded copy); use node.x instead
71
+ // node getters return fresh copies (value semantics, like Unity):
72
+ node.position.x = 3 // compiled to a single-axis write — the direct spelling works
73
+ const p = node.position; p.y = 5; node.position = p // a stored copy: mutate it, assign it back
50
74
 
51
75
  // Mat4 keeps the metal exposed (public .m) for low-level surgery:
52
76
  let m = node.matrix.translate(pivot).rotateY(yaw * dt).translate(pivot.negate())
@@ -54,64 +78,68 @@ m.m[12] += vx * dt
54
78
  node.matrix = m
55
79
  ```
56
80
 
57
- `Quat`/`Mat4` cover the rest: `Quat.fromEuler/lookRotation/slerp`, `Mat4.compose/decompose/lookAt/
58
- perspective`. All of it is fuzz-tested against gl-matrix (dev-only oracle) in `tests/math/oracle.test.ts`.
81
+ `Quat` / `Mat4` cover the rest: `Quat.fromEuler / lookRotation / slerp`, `Mat4.compose / decompose /
82
+ lookAt / perspective`. All of it is fuzz-tested against gl-matrix (a dev-only oracle) in
83
+ `tests/math/oracle.test.ts`.
59
84
 
60
85
  ## Design rules
61
86
 
62
- - **No ECS / no components.** Built-in behaviors are chainable node methods (`.physics()`,
63
- `.collider()`, `.clips()/.play()`); reuse is a factory function. Per-frame logic is `setLoop(dt => …)`.
64
- - **Few globals.** Variants are static factory methods (`Mesh.box()`, `Material.lit()`,
65
- `Texture2D.load()`), not free functions.
66
- - **Pure re-skin.** Calls the existing `_creator` / `_creator2d` / `_creatorTree` / `_creatorUtils`
67
- host bridges — no native engine changes.
87
+ - **Globals only.** `src/inject.ts` is the one file that exports public names; a project's imports
88
+ are relative paths to its own files and assets. Variants are static factories (`Mesh.box()`,
89
+ `Material.lit()`, `Texture2D.load()`), not free functions.
90
+ - **UI elements are handles** to the runtime's tree (docs/tree.md at the repo root): factories take
91
+ content, appearance goes through chained `.style()`, states are `$name` class blocks.
92
+ - **Behaviour is an `Aspect`** on a node and a **`System`** on a scene; the phases are methods
93
+ (`updateBefore` / `update` / `updateFixed`) — no hand-written game loops.
94
+ - **Native resources are owned handles**: a node, texture, material or player is freed once nothing
95
+ references it and nothing native owns it; `destroy()` frees a node early.
96
+ - **Hosts differ, the API does not**: an optional host capability is feature-detected
97
+ (`Physics.supported`, a plugin's `isSupported`), never versioned.
68
98
 
69
99
  ## Layout
70
100
 
71
101
  ```
72
102
  src/
73
- inject.ts # THE aggregation point — the only file that exports public globals
74
- bridges.d.ts # ambient host ABI (_creator*, declared, never bundled)
75
- host.d.ts # ambient host runtime globals (setLoop, console, DEG2RAD, asset)
76
- core/ # handle registry, color, event Emitter
77
- math/ # Mathf (scalar) + fluent Vec2/Vec3 (vec), Quat (quat), Mat4 (mat4) — no gl-matrix shipped
78
- runtime/ # fetch, storage, device, input, media, net, files, touch, misc
79
- g2/ # 2D: Scene2D, Node2D, Sprite, Tilemap, Texture2D, Camera2D
80
- gl/ # 3D: Scene, ARScene, Node, Mesh, Light, Material, Geometry, Texture,
81
- # Camera, Ray, Plane, Noise, Particles
82
- ui/ # UIScreen/Row/Column/Text/Button/… + Router + registerFont
83
- animate/ # animate, cubicBezier, easings
84
- plugins/ # optional, host-provided capabilities (QRScanner, CameraView) — gated on host support
85
- compile/ # the user-project compiler (bundler + header + compileProject) — backend/CLI use it
103
+ inject.ts THE aggregation point — the only file that exports public globals
104
+ host.d.ts host-provided globals (setTimeout, setLoop, console, asset, font, DEG2RAD, EDITOR)
105
+ bridges/ the host contract: one .d.ts per bridge (_creator*, host tables) — never bundled
106
+ chisel.ts the chisel vocabulary: every SDK name the bundler's passes key on
107
+ version.ts SDK_VERSION — its major is the bundle ↔ runtime contract
108
+ core/ Aspect / System, signals, events, Time, StateMachine, color, owned-handle pins
109
+ math/ Mathf, Vec2 / Vec3, Quat, Mat4 (no gl-matrix shipped)
110
+ canvas/ Canvas / Bitmap — vector drawing over the AnyCanvas core
111
+ animate/ animate(), Timeline, easings — the keyframe tween core
112
+ runtime/ fetch, storage, files, device, input, media, touch, app, share, date, rpc
113
+ audio/ game audio: Sound, Voice, Bus, audio
114
+ net/ multiplayer: Net, replication
115
+ ui/ UIScreen / UIRow / UIText / … + Router, UIPager, widgets, theme, transitions
116
+ g2/ 2D: Scene2D, Node2D, Sprite, Tilemap, tilesets, Box2D physics
117
+ gl/ 3D: Scene, Node, Mesh, Model, Material, Light, physics/, vehicle/, animation/, nav/, terrain/
118
+ scene/ scenes as data (defineScene), materials, Level, editor plugins
119
+ plugins/ OAuth, Service + the first-party plugins vendored under plugins/gen
120
+ server/ the host side of app backends (`lecodes-sdk/server`)
121
+ compile/ the project compiler (bundler, entry detection, macros) — `lecodes-sdk/compile`
122
+ docs/ the API reference + cookbook
123
+ prompts/ codegen prompts for the platform's in-editor assistant (`bun run prompts:build`)
124
+ tests/<area>/ bun tests, one folder per src area; tests/helpers = the fake host (`lecodes-sdk/testing`)
86
125
  ```
87
126
 
88
127
  ## Build & test
89
128
 
90
129
  ```sh
91
- bun run build # → dist/inject.js (runtime), dist/global.d.ts + host.d.ts + types.json (editor types)
92
- bun test # compile/parity + math/color + 2D + 3D-runtime + UI tests
130
+ bun run build # dist/global.d.ts + host.d.ts + editor.d.ts + types.json — the editor types
131
+ bun test # every suite; `bun test tests/<area>` for one area
93
132
  bun run typecheck # tsc --noEmit
133
+ bun run docs:check # every global documented exactly once, sources + links resolve
94
134
  ```
95
135
 
96
- Tests live in `tests/<area>/`, one folder per `src/<area>` they exercise; [tests/README.md](tests/README.md)
97
- has the layout, the shared stubs and the one-process rules.
98
-
99
- `tests/headless/` holds live WebGL2 render harnesses (manual; need chrome-headless-shell). The 2D
100
- harness is green: a project compiled through the real `compileProject` renders correct pixels.
136
+ [tests/README.md](tests/README.md) has the test layout, the shared stubs and the one-process rules.
101
137
 
102
138
  ## How it integrates
103
139
 
104
- The backend (`backend/src/utils/compileProject.ts`) and CLI (`lecodes-cli`) compile **every** user
105
- project through this SDK (`sdk/compile`) — the legacy `worker` path was removed from user-project
106
- compilation. A bare `import` from a runtime module (`creator`/`ui`/`animate`/`utils`/`2d`) is no longer
107
- special-cased; it's just an unresolved import, so the build fails loudly and points at the file. The
108
- `worker` package still exists for internal tooling (scene-viewer / model-preview), not for user code.
109
-
110
- ## Deferred (API present, native pending)
111
-
112
- - 2D physics / collider triggers (`.collider()` is a no-op stub; `Node2D.add()` hierarchy).
113
- - Live 2D↔3D engine switching is wired in the viewer host (two canvases) but pending a real dual
114
- project to verify.
115
-
116
- `ARScene.addControls(target, options?)` ports the AR object-placement gesture recipe (one-finger drag,
117
- two-finger pinch-scale + twist-rotate) onto the fluent math; returns a `{ remove() }` handle.
140
+ The CLI (`lecodes compile | render | test | dev`) and the build service compile every project
141
+ through `lecodes-sdk/compile`: entry detection (`main.ts`, else the import-graph root), the
142
+ `asset()` / `font()` / `assetIcon()` macros, then chisel over the SDK **source** — the bundle is
143
+ built from `src/`, not from a prebuilt runtime. Every bundle opens with `// sdk: <version>`; a host
144
+ refuses a bundle whose major differs from its runtime's (the SDK and the runtimes are one version,
145
+ `bun run set-version` at the repo root). Plugins import the SDK through `lecodes-sdk/plugin`.
package/dist/global.d.ts CHANGED
@@ -71,7 +71,6 @@ declare global {
71
71
  const Node: typeof SDK.Node
72
72
  const Node2D: typeof SDK.Node2D
73
73
  const Noise: typeof SDK.Noise
74
- const OAuth: typeof SDK.OAuth
75
74
  const OneWay2D: typeof SDK.OneWay2D
76
75
  const Particles: typeof SDK.Particles
77
76
  const Physics: typeof SDK.Physics
@@ -246,10 +245,10 @@ declare global {
246
245
  type CameraView = SDK.CameraView
247
246
  type Canvas = SDK.Canvas
248
247
  type CellsData = SDK.CellsData
248
+ type ChannelSubscription = SDK.ChannelSubscription
249
249
  type CharacterController = SDK.CharacterController
250
250
  type CharacterController2D = SDK.CharacterController2D
251
251
  type ClickEvent<T = any> = SDK.ClickEvent<T>
252
- type ClientChannel<E extends Record<string, unknown> = Record<string, unknown>> = SDK.ClientChannel<E>
253
252
  type ClipAlign = SDK.ClipAlign
254
253
  type ClipCycle = SDK.ClipCycle
255
254
  type ClipDef = SDK.ClipDef
@@ -404,8 +403,6 @@ declare global {
404
403
  type Node = SDK.Node
405
404
  type Node2D = SDK.Node2D
406
405
  type Noise = SDK.Noise
407
- type OAuthCredential = SDK.OAuthCredential
408
- type OAuthProviderName = SDK.OAuthProviderName
409
406
  type OneWay2D = SDK.OneWay2D
410
407
  type OrientationLock = SDK.OrientationLock
411
408
  type Particles = SDK.Particles
@@ -497,6 +494,7 @@ declare global {
497
494
  type Texture2D = SDK.Texture2D
498
495
  type TextureLoadOptions = SDK.TextureLoadOptions
499
496
  type ThemeAccessors<T> = SDK.ThemeAccessors<T>
497
+ type ThemeRoles = SDK.ThemeRoles
500
498
  type ThemeValues = SDK.ThemeValues
501
499
  type TileBlock = SDK.TileBlock
502
500
  type TileMaterial = SDK.TileMaterial
@@ -575,7 +573,6 @@ declare global {
575
573
  type With<N, A extends Aspect<any, any, any>> = SDK.With<N, A>
576
574
  // ---- server globals (available in *.server.ts; using them in client code is a build error) ----
577
575
  const ApiError: typeof SERVER.ApiError
578
- const auth: typeof SERVER.auth
579
576
  const channel: typeof SERVER.channel
580
577
  const defineDb: typeof SERVER.defineDb
581
578
  const model: typeof SERVER.model
package/dist/host.d.ts CHANGED
@@ -31,6 +31,9 @@ declare global {
31
31
  /** Compile-time macro: `asset('./data.json')` yields the file's PARSED data (a .json file is
32
32
  * data, not code — it ships inside the bundle). See the `string` overload for everything else. */
33
33
  function asset(path: `${string}.json`): any
34
+ /** Compile-time macro: `asset('./logo.svg')` yields the file as an SVG image source — for
35
+ * `UIImage(...)` and `bgImage`. */
36
+ function asset(path: `${string}.svg`): { readonly svg: string, tintColor: string | null }
34
37
  /** Compile-time macro: `asset('./hero.png')` is desugared by the bundler into the module import
35
38
  * for that resource. The file must exist — a path that resolves to nothing fails the compile
36
39
  * (`asset not found: ./hero.png (main.ts:3)`); there is no runtime fallback. Calls inside
@@ -21,7 +21,8 @@ export { device, type HapticStyle, type MotionOptions } from "./runtime/device";
21
21
  export { Input, InputChannel, type InputKeyEvent, type InputGamepadEvent, type InputEventName, type GamepadState, type GamepadAxisName } from "./runtime/input";
22
22
  export { WebSocket } from "./runtime/net";
23
23
  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";
24
- export { __rpc, __channel, __serverOnly, RpcError, type ClientChannel } from "./runtime/rpc";
24
+ export { __rpc, __channel, __serverOnly, RpcError } from "./runtime/rpc";
25
+ export type { ChannelSubscription } from "./server/channel";
25
26
  export { AudioPlayer, VideoPlayer } from "./runtime/media";
26
27
  export { Sound, type SoundOptions } from "./audio/Sound";
27
28
  export { Voice, type VoiceEvents } from "./audio/Voice";
@@ -57,7 +58,7 @@ export { UIScreen, type UIScreenStyle, UIRow, UIColumn, UIBox, type UIContainerS
57
58
  export { Presentable } from "./ui/UI";
58
59
  export type { DismissOptions, PresentOptions, Transition, TransitionName, TransitionPose, TransitionSpec } from "./ui/UI";
59
60
  export type { UINode, UINodeChild } from "./ui/UINode";
60
- export { theme, type ThemeValues, type ThemeAccessors } from "./ui/theme";
61
+ export { theme, type ThemeValues, type ThemeAccessors, type ThemeRoles } from "./ui/theme";
61
62
  export { animate, type AnimateOptions, type AnimateValue, type AnimateOut } from "./animate/tween/animateValue";
62
63
  export { cubicBezier } from "./animate/bezier";
63
64
  export { Timeline, type TimelineOptions, type TimelineAddOptions, type TimelinePosition } from "./animate/tween/Timeline";
@@ -70,7 +71,6 @@ export { CameraView, type CameraFacing } from "./plugins/gen/camera/sdk/camera";
70
71
  export { Geolocation, type GeoPosition, type GeoOptions, type GeoWatch } from "./plugins/gen/geolocation/sdk/geolocation";
71
72
  export { MapView, type MapOptions, type MapStyle, type MapStyleName, type MapCamera, type MapTap, type LngLat, type CameraMove, type FitOptions, type MapPaddingValues, type MarkerItem, type MarkerTap, type MarkerLayer, type MarkerLayerOptions, type LineLayer, type LineLayerOptions, type UserLocationOptions, } from "./plugins/gen/map/sdk/map";
72
73
  export { Push, type PushPayload, type PushStatus, type PushRegisterOptions, type PushEvent } from "./plugins/gen/push/sdk/push";
73
- export { OAuth, type OAuthCredential, type OAuthProviderName } from "./plugins/oauth";
74
74
  export { Service } from "./plugins/service";
75
75
  export { defineScene, use, ref, make, SceneHandle } from "./scene/defineScene";
76
76
  export { defineMaterial, MaterialHandle } from "./scene/material";
@@ -56,6 +56,13 @@ export declare const device: {
56
56
  * feels native on each platform; see {@link HapticStyle}. Host-gated: a silent no-op where there's
57
57
  * no haptic hardware (iPad, older iPhones, web, headless). */
58
58
  vibrate(style?: HapticStyle): void;
59
+ /** The host's performance overlay: one line over everything with the frames per second the
60
+ * display actually got, how evenly they came (`66 fps · 73% even` is judder, `60 fps · 100% even`
61
+ * is smooth), and the GPU's and the main thread's milliseconds per frame. The host measures and
62
+ * draws it itself about once a second — nothing of it runs in the app's JS, so it does not change
63
+ * what it measures. It belongs to the host, not to the project: it stays on when another project
64
+ * is opened. Reads `false`, and writing does nothing, on hosts without one (web, headless). */
65
+ statsOverlay: boolean;
59
66
  /** Device-orientation sensor (gyro + accelerometer, fused) for tilt/steering and magic-window /
60
67
  * 360° panoramas. Poll `attitude` / `gravity` inside setLoop; they return the freshest fused
61
68
  * sample, so the sensor rate need not match your frame rate. Host-gated: a no-op with no sensor. */
@@ -10,15 +10,13 @@
10
10
  * Wire (implemented by the runner):
11
11
  * POST <serverUrl>/api/<id> body {"args":[…]}, `authorization: Bearer <session>` when known
12
12
  * → 200 {"ok":true,"result":…,"session"?:"…"} | {"ok":false,"status":n,"message":"…","session"?:"…"}
13
- * WS <serverUrl>/ws frames: → {t:"hello",session} {t:"sub",id,ch,topic} {t:"unsub",id}
14
- * ← {t:"session",session} {t:"ok",id} {t:"err",id,status,message} {t:"ev",ch,topic,event,data}
15
- * (`ev` is addressed by channel + topic, not by subscription id, so the server can fan one publish out
16
- * to every socket of a topic natively; the client delivers it to each matching subscription.)
13
+ * WS <serverUrl>/ws the channel socket — its frames are ./wire.ts
17
14
  *
18
15
  * The session token is transport-owned (cookie-like): stored under `lecodes.session:<serverUrl>` in
19
16
  * localStorage, sent on every call and on the socket, replaced whenever a response carries `session`.
20
17
  * User code never sees it — "am I logged in" is an endpoint (`me()`), see the plan.
21
18
  */
19
+ import type { ChannelSubscription } from "../server/channel";
22
20
  /** Rejection value of a failed endpoint call: `status` mirrors the server's `ApiError`
23
21
  * (0 = the request itself failed: no network / no server). */
24
22
  export declare class RpcError extends Error {
@@ -29,16 +27,12 @@ export declare class RpcError extends Error {
29
27
  export declare const __rpc: (serverUrl: string, id: string) => (...args: unknown[]) => Promise<any>;
30
28
  /** A server-only export (db, models, helpers) referenced from client code: any use throws. */
31
29
  export declare const __serverOnly: (id: string) => any;
32
- export interface ClientChannel<E extends Record<string, unknown> = Record<string, unknown>> {
33
- /** Subscribe to `topic`; handlers are keyed by event name (+ `reconnect`/`error`). Returns `{ close }`. */
34
- subscribe(topic: string, handlers: {
35
- [K in keyof E]?: (payload: E[K]) => void;
36
- } & {
37
- reconnect?: () => void;
38
- error?: (e: RpcError) => void;
39
- }): {
40
- close(): void;
41
- };
42
- }
43
- /** Build the client proxy of channel `id`. */
44
- export declare const __channel: (serverUrl: string, id: string) => ClientChannel<any>;
30
+ /**
31
+ * Build the client proxy of channel `id` — what a `channel()` export of a `*.server.ts` file is in
32
+ * the app (its type is the server's: src/server/channel.ts). `subscribe([...args,] handler, options?)`:
33
+ * what stands before the handler travels to the channel's `groupBy`.
34
+ */
35
+ export declare const __channel: (serverUrl: string, id: string) => {
36
+ subscribe(...all: unknown[]): ChannelSubscription;
37
+ publish(): never;
38
+ };
@@ -0,0 +1,53 @@
1
+ /**
2
+ * The frames of the channel socket (`<serverUrl>/ws`) — the ONE description both ends are written
3
+ * against: the client transport (./rpc.ts) and the server's hub (../server/channelHub.ts, which the
4
+ * runner and the local backend of `lecodes dev` both run). JSON text frames, one per message.
5
+ *
6
+ * → hello who is on this socket. Sent first, and again whenever the session changes: the server
7
+ * then DROPS the socket's subscriptions (they were granted to someone else) and the
8
+ * client sends them again, under new ids.
9
+ * → sub subscribe to channel `ch`; `args` are what the app passed to `subscribe` before the
10
+ * handler — the channel's `groupBy` reads them.
11
+ * → unsub
12
+ * ← session a token minted while the socket was served (the first contact of a guest): the client
13
+ * stores it; nothing is dropped, the server already has it.
14
+ * ← ok the subscription stands; `group` is where the server put it (null = a channel without
15
+ * groups). The client learns its group only here — it never names one.
16
+ * ← err refused (a hook threw) — the subscription is gone.
17
+ * ← ev one published message, addressed by channel + group so the server sends one frame to
18
+ * every socket of a group; the client hands it to each of its subscriptions there.
19
+ *
20
+ * A group's value is not a secret: its subscriber is told it.
21
+ */
22
+ /** What `groupBy` may answer. `42` and `"42"` are two groups. */
23
+ export type ChannelGroup = string | number;
24
+ export type ClientFrame = {
25
+ t: "hello";
26
+ session: string | null;
27
+ } | {
28
+ t: "sub";
29
+ id: number;
30
+ ch: string;
31
+ args: unknown[];
32
+ } | {
33
+ t: "unsub";
34
+ id: number;
35
+ };
36
+ export type ServerFrame = {
37
+ t: "session";
38
+ session: string;
39
+ } | {
40
+ t: "ok";
41
+ id: number;
42
+ group: ChannelGroup | null;
43
+ } | {
44
+ t: "err";
45
+ id: number;
46
+ status: number;
47
+ message: string;
48
+ } | {
49
+ t: "ev";
50
+ ch: string;
51
+ group: ChannelGroup | null;
52
+ data: unknown;
53
+ };
@@ -0,0 +1,42 @@
1
+ /**
2
+ * `db.auth` — the BUNDLE side of sign-in, what `*.server.ts` code calls on a db made with
3
+ * `defineDb({...}).withAuth({ model: "User" })`:
4
+ *
5
+ * const db = defineDb({ User, Post }).withAuth({ model: "User" })
6
+ *
7
+ * export async function register(login: string, password: string, name: string) {
8
+ * await db.auth.signUpWithPassword(login, password, { name })
9
+ * }
10
+ * export async function me() { return db.auth.user().select({ id: true, name: true }) }
11
+ * export async function removePost(id: number) {
12
+ * await db.auth.requireUser({ role: "admin" }).select({ id: true })
13
+ * …
14
+ * }
15
+ *
16
+ * The platform knows a request's session and the ID of its user (the host resolved the token before
17
+ * the endpoint ran). Everything else about a user is a row of the project's own model, and is READ:
18
+ * `user()` / `requireUser()` are queries over that model — typed by it, run when awaited — narrowed to
19
+ * the user of the request. Sign-in operations forward to the host's `AuthOps` on
20
+ * `globalThis.__lecodesAuth` (a seam: the compiled bundle carries its own SDK copy).
21
+ */
22
+ /** The collections of a db with auth, by role (defineDb.ts). */
23
+ export type AuthCollections = {
24
+ model: string;
25
+ user: any;
26
+ session: any;
27
+ identity: any;
28
+ };
29
+ export declare const createAuthApi: (tables: () => AuthCollections) => {
30
+ user: () => any;
31
+ requireUser: (where?: Record<string, unknown>) => any;
32
+ readonly sessionId: number;
33
+ signUpWithPassword: (login: string, password: string, data?: Record<string, unknown>) => Promise<void>;
34
+ signInWithPassword: (login: string, password: string) => Promise<void>;
35
+ setPassword: (password: string) => Promise<void>;
36
+ sendCode: (email: string) => Promise<void>;
37
+ signInWithCode: (email: string, code: string) => Promise<void>;
38
+ signIn: (userId: number) => Promise<void>;
39
+ signOut: () => Promise<void>;
40
+ readonly sessions: any;
41
+ readonly identities: any;
42
+ };
@@ -1,16 +1,12 @@
1
1
  /**
2
- * The project's `app.json` subset the server runtime reads (name, auth providers).
2
+ * The project's `app.json` subset the server runtime reads: the app's name (the sign-in mail).
3
3
  *
4
4
  * This module is a placeholder the COMPILER REPLACES: `compileServerBundle({ app })` puts a module with the
5
5
  * real values at this same path in chisel's file map (project files win over SDK files), so every SDK module
6
6
  * that imports `app` gets the project's config as a plain dependency — no globals, no evaluation-order games.
7
7
  * Outside a compiled bundle (tests, tooling) `globalThis.__lecodesApp` can stand in.
8
8
  */
9
- export type AuthProvider = "email" | "google" | "apple" | "yandex" | "vk";
10
9
  export type AppConfig = {
11
10
  name?: string;
12
- auth?: {
13
- providers?: AuthProvider[];
14
- };
15
11
  };
16
12
  export declare const app: AppConfig;