lecodes-cli 0.19.0 → 0.20.0

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 (270) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +1069 -396
  3. package/package.json +14 -13
  4. package/runtime/scene-harness.json +1 -1
  5. package/runtime/sdk-types.json +1 -1
  6. package/runtime/web/assets/Roboto-Variable-DHm9jpd-.woff2 +0 -0
  7. package/runtime/web/assets/__vite-browser-external-BIHI7g3E.js +1 -0
  8. package/runtime/web/assets/basis-C64VHDVD.js +1 -0
  9. package/runtime/web/assets/basis_transcoder-VXdx5NbI.wasm +0 -0
  10. package/runtime/web/assets/createViewerLite-BvHobVhp.js +852 -0
  11. package/runtime/web/assets/draco-BiISTFcR.js +118 -0
  12. package/runtime/web/assets/draco_decoder-DsQ12WqX.wasm +0 -0
  13. package/runtime/web/assets/index-CfNCCBjN.js +2 -0
  14. package/runtime/web/assets/mapViewImpl-B2JcES8l.js +810 -0
  15. package/runtime/web/assets/maplibre-gl-worker-CJfwIrte.js +8 -0
  16. package/runtime/web/assets/neutral_ibl128-D-lzdkLN.ktx +0 -0
  17. package/runtime/web/assets/worker-Caf-yYEI.js +2 -0
  18. package/runtime/web/embed.html +32 -0
  19. package/runtime/web/embed.js +171 -0
  20. package/src/api.ts +302 -0
  21. package/src/browserAuth.ts +87 -0
  22. package/src/cmgenTool.ts +104 -0
  23. package/src/commands/app.ts +896 -0
  24. package/src/commands/appAndroid.ts +603 -0
  25. package/src/commands/appDesktop.ts +339 -0
  26. package/src/commands/appDesktopMac.ts +470 -0
  27. package/src/commands/appIcon.ts +187 -0
  28. package/src/commands/appShared.ts +542 -0
  29. package/src/commands/appTemplates.ts +907 -0
  30. package/src/commands/appTemplatesAndroid.ts +599 -0
  31. package/src/commands/appTemplatesGradlew.ts +9 -0
  32. package/src/commands/assets.ts +28 -0
  33. package/src/commands/clone.ts +60 -0
  34. package/src/commands/compile.ts +253 -0
  35. package/src/commands/create.ts +59 -0
  36. package/src/commands/design.ts +863 -0
  37. package/src/commands/designTemplates.ts +11 -0
  38. package/src/commands/desktop.ts +127 -0
  39. package/src/commands/dev.ts +236 -0
  40. package/src/commands/diff.ts +59 -0
  41. package/src/commands/init.ts +191 -0
  42. package/src/commands/install.ts +198 -0
  43. package/src/commands/lightmap.ts +290 -0
  44. package/src/commands/link.ts +147 -0
  45. package/src/commands/login.ts +24 -0
  46. package/src/commands/navmesh.ts +225 -0
  47. package/src/commands/pn.ts +305 -0
  48. package/src/commands/projectTemplates.ts +130 -0
  49. package/src/commands/pull.ts +109 -0
  50. package/src/commands/push.ts +173 -0
  51. package/src/commands/render.ts +416 -0
  52. package/src/commands/scene.ts +148 -0
  53. package/src/commands/shaders.ts +157 -0
  54. package/src/commands/status.ts +29 -0
  55. package/src/commands/test.ts +371 -0
  56. package/src/commands/thumbs.ts +178 -0
  57. package/src/commands/types.ts +87 -0
  58. package/src/commands/update.ts +190 -0
  59. package/src/compile/assetIcons.ts +214 -0
  60. package/src/compile/collect.ts +46 -0
  61. package/src/compile/collectLocal.ts +37 -0
  62. package/src/compile/designCompile.ts +104 -0
  63. package/src/compile/fonts.ts +156 -0
  64. package/src/compile/headlessBundle.ts +129 -0
  65. package/src/compile/nativeStack.ts +21 -0
  66. package/src/compile/projectCompile.ts +109 -0
  67. package/src/compile/sceneCompile.ts +113 -0
  68. package/src/compile/screenEntry.ts +127 -0
  69. package/src/compile/shaders.ts +245 -0
  70. package/src/config.ts +42 -0
  71. package/src/designMeta.ts +35 -0
  72. package/src/desktopRenderer.ts +545 -0
  73. package/src/desktopScript.ts +276 -0
  74. package/src/dev/clientTemplates.ts +160 -0
  75. package/src/dev/devServer.ts +333 -0
  76. package/src/dev/webRunner.ts +22 -0
  77. package/src/dev/wsServer.ts +144 -0
  78. package/src/distRoot.ts +20 -0
  79. package/src/ignore.ts +163 -0
  80. package/src/index.ts +494 -0
  81. package/src/lecodes-3d-editor.d.ts +41 -0
  82. package/src/lecodes-assets.d.ts +7 -0
  83. package/src/lecodes-design.d.ts +191 -0
  84. package/src/lecodes-renderer.d.ts +131 -0
  85. package/src/localFiles.ts +144 -0
  86. package/src/manifest.ts +46 -0
  87. package/src/matcTool.ts +137 -0
  88. package/src/peers.ts +40 -0
  89. package/src/project.ts +61 -0
  90. package/src/projectEnv.ts +94 -0
  91. package/src/qrcode-terminal.d.ts +9 -0
  92. package/src/releases.ts +125 -0
  93. package/src/serverDiff.ts +78 -0
  94. package/src/textDiff.ts +103 -0
  95. package/src/types.ts +0 -0
  96. package/src/util.ts +146 -0
  97. package/runtime/sdk/animate/animate.ts +0 -238
  98. package/runtime/sdk/animate/bezier.ts +0 -138
  99. package/runtime/sdk/animate/easings.ts +0 -126
  100. package/runtime/sdk/canvas/Canvas.ts +0 -305
  101. package/runtime/sdk/compile/aspectMacro.ts +0 -86
  102. package/runtime/sdk/compile/assetIconMacro.ts +0 -384
  103. package/runtime/sdk/compile/assetMacro.ts +0 -146
  104. package/runtime/sdk/compile/assetName.ts +0 -50
  105. package/runtime/sdk/compile/bundler.ts +0 -287
  106. package/runtime/sdk/compile/compileProject.ts +0 -129
  107. package/runtime/sdk/compile/detectEntry.ts +0 -128
  108. package/runtime/sdk/compile/fontMacro.ts +0 -459
  109. package/runtime/sdk/compile/fontRegistry.ts +0 -78
  110. package/runtime/sdk/compile/header.ts +0 -67
  111. package/runtime/sdk/compile/index.ts +0 -100
  112. package/runtime/sdk/compile/libraryImports.ts +0 -52
  113. package/runtime/sdk/compile/liteMaterial.ts +0 -247
  114. package/runtime/sdk/compile/sceneEditor.ts +0 -88
  115. package/runtime/sdk/compile/serverSplit.ts +0 -233
  116. package/runtime/sdk/compile/serverTypes.ts +0 -227
  117. package/runtime/sdk/compile/sfnt.ts +0 -98
  118. package/runtime/sdk/compile/shaderSchema.ts +0 -202
  119. package/runtime/sdk/compile/shaderTargets.ts +0 -81
  120. package/runtime/sdk/compile/sourcemap.ts +0 -25
  121. package/runtime/sdk/core/Aspect.ts +0 -568
  122. package/runtime/sdk/core/InspectorUI.ts +0 -212
  123. package/runtime/sdk/core/StateMachine.ts +0 -308
  124. package/runtime/sdk/core/color.ts +0 -66
  125. package/runtime/sdk/core/compWrite.ts +0 -42
  126. package/runtime/sdk/core/events.ts +0 -38
  127. package/runtime/sdk/core/fields.ts +0 -120
  128. package/runtime/sdk/core/registry.ts +0 -23
  129. package/runtime/sdk/core/signals.ts +0 -277
  130. package/runtime/sdk/core/time.ts +0 -84
  131. package/runtime/sdk/g2/Camera2D.ts +0 -40
  132. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  133. package/runtime/sdk/g2/Node2D.ts +0 -267
  134. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  135. package/runtime/sdk/g2/Physics2D.ts +0 -346
  136. package/runtime/sdk/g2/Scene2D.ts +0 -209
  137. package/runtime/sdk/g2/Shape2D.ts +0 -259
  138. package/runtime/sdk/g2/Sprite.ts +0 -89
  139. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  140. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  141. package/runtime/sdk/g2/Texture2D.ts +0 -47
  142. package/runtime/sdk/g2/Tilemap.ts +0 -41
  143. package/runtime/sdk/g2/Tileset.ts +0 -71
  144. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  145. package/runtime/sdk/g2/autotile.ts +0 -433
  146. package/runtime/sdk/g2/cells.ts +0 -91
  147. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  148. package/runtime/sdk/g2/groups2d.ts +0 -106
  149. package/runtime/sdk/g2/loop.ts +0 -50
  150. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  151. package/runtime/sdk/g2/touch.ts +0 -83
  152. package/runtime/sdk/gl/Camera.ts +0 -160
  153. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  154. package/runtime/sdk/gl/CharacterController.ts +0 -238
  155. package/runtime/sdk/gl/Gearbox.ts +0 -212
  156. package/runtime/sdk/gl/Geometry.ts +0 -279
  157. package/runtime/sdk/gl/IK.ts +0 -193
  158. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  159. package/runtime/sdk/gl/Light.ts +0 -107
  160. package/runtime/sdk/gl/Lightmap.ts +0 -256
  161. package/runtime/sdk/gl/Material.ts +0 -260
  162. package/runtime/sdk/gl/Mesh.ts +0 -83
  163. package/runtime/sdk/gl/Model.ts +0 -124
  164. package/runtime/sdk/gl/NavAgent.ts +0 -337
  165. package/runtime/sdk/gl/NavMesh.ts +0 -397
  166. package/runtime/sdk/gl/Node.ts +0 -350
  167. package/runtime/sdk/gl/Noise.ts +0 -30
  168. package/runtime/sdk/gl/Particles.ts +0 -676
  169. package/runtime/sdk/gl/Physics.ts +0 -222
  170. package/runtime/sdk/gl/Plane.ts +0 -53
  171. package/runtime/sdk/gl/Ragdoll.ts +0 -270
  172. package/runtime/sdk/gl/Ray.ts +0 -16
  173. package/runtime/sdk/gl/Scene.ts +0 -486
  174. package/runtime/sdk/gl/Shape.ts +0 -409
  175. package/runtime/sdk/gl/Terrain.ts +0 -1092
  176. package/runtime/sdk/gl/Texture.ts +0 -63
  177. package/runtime/sdk/gl/Trigger.ts +0 -45
  178. package/runtime/sdk/gl/Vehicle.ts +0 -473
  179. package/runtime/sdk/gl/Wheel.ts +0 -240
  180. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  181. package/runtime/sdk/gl/animation/Animator.ts +0 -120
  182. package/runtime/sdk/gl/animation/Layer.ts +0 -31
  183. package/runtime/sdk/gl/animation/Loop.ts +0 -30
  184. package/runtime/sdk/gl/animation/Playback.ts +0 -44
  185. package/runtime/sdk/gl/animation/core.ts +0 -336
  186. package/runtime/sdk/gl/controls.ts +0 -95
  187. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  188. package/runtime/sdk/gl/scenarios.ts +0 -291
  189. package/runtime/sdk/gl/state.ts +0 -6
  190. package/runtime/sdk/gl/terrainMesh.ts +0 -219
  191. package/runtime/sdk/gl/touch.ts +0 -68
  192. package/runtime/sdk/inject.ts +0 -197
  193. package/runtime/sdk/math/Mathf.ts +0 -118
  194. package/runtime/sdk/math/mat4.ts +0 -278
  195. package/runtime/sdk/math/quat.ts +0 -232
  196. package/runtime/sdk/math/vec.ts +0 -255
  197. package/runtime/sdk/net/codec.ts +0 -119
  198. package/runtime/sdk/net/core.ts +0 -384
  199. package/runtime/sdk/net/index.ts +0 -181
  200. package/runtime/sdk/net/replication.ts +0 -622
  201. package/runtime/sdk/plugins/camera.ts +0 -81
  202. package/runtime/sdk/plugins/geolocation.ts +0 -123
  203. package/runtime/sdk/plugins/oauth.ts +0 -61
  204. package/runtime/sdk/plugins/permission.ts +0 -7
  205. package/runtime/sdk/plugins/push.ts +0 -132
  206. package/runtime/sdk/plugins/qr.ts +0 -73
  207. package/runtime/sdk/plugins/service.ts +0 -47
  208. package/runtime/sdk/runtime/app.ts +0 -101
  209. package/runtime/sdk/runtime/appEvents.ts +0 -54
  210. package/runtime/sdk/runtime/channel.ts +0 -61
  211. package/runtime/sdk/runtime/clipboard.ts +0 -20
  212. package/runtime/sdk/runtime/datetime.ts +0 -329
  213. package/runtime/sdk/runtime/device.ts +0 -293
  214. package/runtime/sdk/runtime/fetch.ts +0 -77
  215. package/runtime/sdk/runtime/files.ts +0 -23
  216. package/runtime/sdk/runtime/input.ts +0 -175
  217. package/runtime/sdk/runtime/media.ts +0 -111
  218. package/runtime/sdk/runtime/misc.ts +0 -16
  219. package/runtime/sdk/runtime/net.ts +0 -36
  220. package/runtime/sdk/runtime/rpc.ts +0 -218
  221. package/runtime/sdk/runtime/service.ts +0 -83
  222. package/runtime/sdk/runtime/share.ts +0 -9
  223. package/runtime/sdk/runtime/storage.ts +0 -13
  224. package/runtime/sdk/runtime/touch.ts +0 -76
  225. package/runtime/sdk/scene/defineScene.ts +0 -1353
  226. package/runtime/sdk/scene/editorPlugins.ts +0 -110
  227. package/runtime/sdk/scene/gizmos.ts +0 -148
  228. package/runtime/sdk/scene/grammar.ts +0 -120
  229. package/runtime/sdk/scene/material.ts +0 -188
  230. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  231. package/runtime/sdk/server/auth/global.ts +0 -80
  232. package/runtime/sdk/server/auth/host.ts +0 -318
  233. package/runtime/sdk/server/auth/models.ts +0 -83
  234. package/runtime/sdk/server/auth/types.ts +0 -50
  235. package/runtime/sdk/server/channel.ts +0 -56
  236. package/runtime/sdk/server/context.ts +0 -36
  237. package/runtime/sdk/server/db/defineDb.ts +0 -237
  238. package/runtime/sdk/server/db/fields.ts +0 -132
  239. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  240. package/runtime/sdk/server/db/index.ts +0 -7
  241. package/runtime/sdk/server/db/marci/query.ts +0 -412
  242. package/runtime/sdk/server/db/types.ts +0 -202
  243. package/runtime/sdk/server/errors.ts +0 -12
  244. package/runtime/sdk/server/host.ts +0 -74
  245. package/runtime/sdk/server/inject.ts +0 -13
  246. package/runtime/sdk/server/runtime.ts +0 -133
  247. package/runtime/sdk/server/validate.ts +0 -87
  248. package/runtime/sdk/ui/NativeView.ts +0 -142
  249. package/runtime/sdk/ui/UI.ts +0 -39
  250. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  251. package/runtime/sdk/ui/UIButton.ts +0 -101
  252. package/runtime/sdk/ui/UIContainer.ts +0 -60
  253. package/runtime/sdk/ui/UIImage.ts +0 -83
  254. package/runtime/sdk/ui/UIInput.ts +0 -185
  255. package/runtime/sdk/ui/UIModal.ts +0 -139
  256. package/runtime/sdk/ui/UINode.ts +0 -830
  257. package/runtime/sdk/ui/UIPager.ts +0 -362
  258. package/runtime/sdk/ui/UIPopover.ts +0 -100
  259. package/runtime/sdk/ui/UIScreen.ts +0 -123
  260. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  261. package/runtime/sdk/ui/UISpacer.ts +0 -14
  262. package/runtime/sdk/ui/UITabs.ts +0 -236
  263. package/runtime/sdk/ui/UIText.ts +0 -51
  264. package/runtime/sdk/ui/UIVideo.ts +0 -88
  265. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  266. package/runtime/sdk/ui/UIWidget.ts +0 -127
  267. package/runtime/sdk/ui/fonts.ts +0 -13
  268. package/runtime/sdk/ui/presentable.ts +0 -117
  269. package/runtime/sdk/ui/router.ts +0 -132
  270. package/runtime/sdk/ui/theme.ts +0 -84
@@ -1,237 +0,0 @@
1
- /**
2
- * `model({...})` + `defineDb({ User, Post })` — the schema registry and the typed db handle.
3
- *
4
- * Runtime responsibilities: validate relation names, resolve `t.many()` back-fields, emit the `.marci`
5
- * schema text (`db.$schema`), and turn collection calls into lazy `Op`s over a `MarciTransport`
6
- * (`marcidb-embedded` handle, HTTP client, or a test fake). The transport is either passed to
7
- * `defineDb(models, { transport })` or provided by the host through `setDbTransport()` — user code
8
- * never sees a connection string.
9
- */
10
-
11
- import type { Field, FieldDef, ScalarKind } from "./fields"
12
- import { AUTH_SESSION_MODEL, AUTH_USER_MODEL, authSessionModel, isAuthModel, sessionUserField } from "../auth/models"
13
- import { createQueryLayer, type FieldDesc, type MarciOp, type MarciTransport, type ModelsMeta } from "./marci/query"
14
- import type { AuthBinding, Db, Fields, Model, Schema, ValidateRefs } from "./types"
15
-
16
- export const model = <F extends Fields>(fields: F): Model<F> => ({ fields, __model: true })
17
-
18
- // ───────────────────────────── transport ─────────────────────────────
19
-
20
- // A GLOBAL seam (not a module variable): the compiled server bundle carries its own copy of this
21
- // module, so the host's `setDbTransport` and the bundle's `defineDb` only meet through globalThis.
22
- const seam = globalThis as unknown as { __lecodesDbTransport?: MarciTransport, __lecodesDbs?: Db<any>[] }
23
- /** Host hook: the runner/dev-server installs the project's transport before loading user code. */
24
- export const setDbTransport = (transport: MarciTransport | null) => {
25
- if (transport) seam.__lecodesDbTransport = transport
26
- else delete seam.__lecodesDbTransport
27
- }
28
- /**
29
- * Every `defineDb` call registers its handle here so the host finds the schema whether or not the
30
- * project exports `db` (`const db = defineDb(...)` used only inside endpoints is the common case).
31
- * The host reads and clears the list after evaluating a bundle (`loadServerModules`).
32
- */
33
- export const takeRegisteredDbs = (): Db<any>[] => {
34
- const dbs = seam.__lecodesDbs ?? []
35
- seam.__lecodesDbs = []
36
- return dbs
37
- }
38
-
39
- const opOf = (o: unknown): MarciOp => {
40
- const op = (o as { __op?: MarciOp })?.__op
41
- if (!op) throw new Error("db.$transaction: every item must be a db.<model>.<method>(...) call")
42
- return op
43
- }
44
-
45
- /** A reference to a previous operation's result inside `$transaction` (resolved server-side): `ref("0.id")`. */
46
- export const ref = (path: string): any => ({ $ref: path })
47
-
48
- // ───────────────────────────── metadata ─────────────────────────────
49
-
50
- type ModelMeta = {
51
- name: string
52
- fields: Record<string, FieldDef>
53
- idKeys: string[]
54
- /** id is a single uuid key we generate on insert. */
55
- uuidId: boolean
56
- }
57
-
58
- const isRel = (d: FieldDef) => d.kind === "one" || d.kind === "many" || d.kind === "list"
59
-
60
- const buildMeta = (models: Record<string, Model>): Record<string, ModelMeta> => {
61
- const metas: Record<string, ModelMeta> = {}
62
- for (const [name, m] of Object.entries(models)) {
63
- if (!/^[A-Z][A-Za-z0-9]*$/.test(name)) throw new Error(`defineDb: model name "${name}" must be PascalCase`)
64
- const fields: Record<string, FieldDef> = {}
65
- for (const [key, f] of Object.entries(m.fields as Fields)) fields[key] = { ...(f as Field<any, any>).def }
66
- if (fields.id && !fields.id.isId) fields.id.isId = true
67
- const idKeys = Object.keys(fields).filter(k => fields[k].isId)
68
- const uuidId = idKeys.length === 1 && idKeys[0] === "id" && fields.id.scalar === "uuid"
69
- metas[name] = { name, fields, idKeys, uuidId }
70
- }
71
- // relations: targets must exist; `many` needs a back field
72
- for (const meta of Object.values(metas)) {
73
- for (const [key, d] of Object.entries(meta.fields)) {
74
- if (!isRel(d)) continue
75
- const target = metas[d.model!]
76
- if (!target) throw new Error(`defineDb: ${meta.name}.${key} references unknown model "${d.model}"`)
77
- if (d.kind !== "many") continue
78
- if (d.back) {
79
- const back = target.fields[d.back]
80
- if (!back || back.kind !== "one" || back.model !== meta.name)
81
- throw new Error(`defineDb: ${meta.name}.${key}: "${d.model}.${d.back}" is not a t.one("${meta.name}") field`)
82
- continue
83
- }
84
- const candidates = Object.entries(target.fields).filter(([, f]) => f.kind === "one" && f.model === meta.name).map(([k]) => k)
85
- if (candidates.length !== 1)
86
- throw new Error(
87
- `defineDb: ${meta.name}.${key}: cannot infer the back field on ${d.model} ` +
88
- (candidates.length ? `(candidates: ${candidates.join(", ")}) — pass it: t.many("${d.model}", "<field>")`
89
- : `— add a t.one("${meta.name}") field to ${d.model}`))
90
- d.back = candidates[0]
91
- }
92
- }
93
- return metas
94
- }
95
-
96
- // ───────────────────────────── .marci emitter ─────────────────────────────
97
-
98
- const SCALAR: Record<Exclude<ScalarKind, "">, string> = {
99
- string: "String", int: "Int", float: "Double", bool: "Bool", date: "DateTime", uuid: "Byte[16]",
100
- }
101
- const cap = (s: string) => s.charAt(0).toUpperCase() + s.slice(1)
102
- const ON_DELETE = { cascade: "Cascade", setNull: "SetNull", restrict: "Restrict" } as const
103
-
104
- const literal = (v: unknown, d: FieldDef): string => {
105
- if (d.scalar === "date" && v === "now") return "now()"
106
- if (Array.isArray(v)) return `[${v.map(x => literal(x, { ...d, array: false })).join(", ")}]`
107
- if (typeof v === "string") return d.kind === "enum" ? v : JSON.stringify(v)
108
- return String(v)
109
- }
110
-
111
- const emitField = (owner: string, key: string, d: FieldDef, extra: string[]): string => {
112
- let type: string
113
- const attrs: string[] = []
114
- switch (d.kind) {
115
- case "scalar":
116
- type = d.vector ? `Float[${d.vector.size}]` : SCALAR[d.scalar as Exclude<ScalarKind, "">]
117
- if (d.scalar === "uuid") attrs.push("@format(uuid)")
118
- break
119
- case "json": type = "Json"; break
120
- case "enum": {
121
- const name = `${owner}${cap(key)}`
122
- extra.push(`enum ${name} {\n${d.values!.map(v => ` ${v}`).join("\n")}\n}`)
123
- type = name; break
124
- }
125
- case "struct": {
126
- const name = `${owner}${cap(key)}`
127
- const inner: string[] = []
128
- for (const [k, f] of Object.entries(d.fields!)) inner.push(emitField(name, k, (f as Field<any, any>).def, extra))
129
- extra.push(`struct ${name} {\n${inner.join("\n")}\n}`)
130
- type = name; break
131
- }
132
- case "one": type = d.model!; break
133
- case "many": type = `${d.model}[]`; attrs.push(`@bind(${d.model}.${d.back})`); break
134
- case "list": type = `${d.model}[]`; attrs.push("@list"); break
135
- }
136
- if (d.array && d.kind !== "many" && d.kind !== "list") type += "[]"
137
- if (d.optional) type += "?"
138
- if (d.isId) attrs.unshift("@id")
139
- if (d.unique) attrs.push("@unique")
140
- if (d.index) attrs.push("@index")
141
- if (d.hasDefault) attrs.push(`@default(${literal(d.default, d)})`)
142
- if (d.onDelete) attrs.push(`@onDelete(${ON_DELETE[d.onDelete]})`)
143
- if (d.fulltext) attrs.push(`@fulltext(${d.fulltext})`)
144
- if (d.vector) attrs.push(`@vector(${d.vector.metric})`)
145
- return ` ${key.padEnd(12)} ${type}${attrs.length ? " " + attrs.join(" ") : ""}`
146
- }
147
-
148
- export const toMarci = (metas: Record<string, ModelMeta>): string => {
149
- const out: string[] = []
150
- const extra: string[] = []
151
- for (const meta of Object.values(metas)) {
152
- const lines = Object.entries(meta.fields).map(([k, d]) => emitField(meta.name, k, d, extra))
153
- out.push(`model ${meta.name} {\n${lines.join("\n")}\n}`)
154
- }
155
- return [...out, ...extra].join("\n\n") + "\n"
156
- }
157
-
158
- // ───────────────────────────── queries ─────────────────────────────
159
-
160
- /**
161
- * The model metadata marcidb's query layer reads (`MODELS` in a generated client): field descriptors per
162
- * model — `key` / `body` scalars, `one` / `many` relations with their target. Structs are pseudo-models
163
- * (`User.info`) so an empty nested select fills with the struct's own scalars, as marcidb does.
164
- */
165
- const modelsMeta = (metas: Record<string, ModelMeta>): ModelsMeta => {
166
- const out: Record<string, FieldDesc[]> = {}
167
- const describe = (name: string, fields: Record<string, FieldDef>, implicitId: boolean) => {
168
- const descs: FieldDesc[] = implicitId ? [{ n: "id", k: "key" }] : []
169
- for (const [key, d] of Object.entries(fields)) {
170
- if (isRel(d)) descs.push({ n: key, k: d.kind === "one" ? "one" : "many", m: d.model! })
171
- else if (d.kind === "struct") {
172
- const sub = `${name}.${key}`
173
- const inner: Record<string, FieldDef> = {}
174
- for (const [ik, f] of Object.entries(d.fields!)) inner[ik] = (f as Field<any, any>).def
175
- describe(sub, inner, false)
176
- descs.push({ n: key, k: "one", m: sub })
177
- }
178
- else descs.push({ n: key, k: d.isId ? "key" : "body" })
179
- }
180
- out[name] = descs
181
- }
182
- for (const meta of Object.values(metas)) describe(meta.name, meta.fields, meta.idKeys.length === 0)
183
- return out
184
- }
185
-
186
- const uncap = (s: string) => s.charAt(0).toLowerCase() + s.slice(1)
187
-
188
- export type DefineDbOptions = { transport?: MarciTransport }
189
-
190
- /**
191
- * The schema registry: model name = key (`db.user` ← `User`). Relation strings are checked against the
192
- * keys both at the type level (`ValidateRefs`) and at runtime.
193
- */
194
- /**
195
- * The auth models (docs/backend-plan.md §3.4): `auth.model.user()` must be registered as `User`,
196
- * `auth.model.session()` as `Session`; `Session` is added when the project doesn't declare it (a device
197
- * session exists before any login), and gets its `user` column exactly when a `User` model exists.
198
- */
199
- const withAuthModels = (models: Record<string, Model>): { models: Record<string, Model>, auth: AuthBinding } => {
200
- for (const [name, m] of Object.entries(models)) {
201
- if (isAuthModel(m, "user") && name !== AUTH_USER_MODEL) throw new Error(`defineDb: auth.model.user() must be registered as "${AUTH_USER_MODEL}" (got "${name}")`)
202
- if (isAuthModel(m, "session") && name !== AUTH_SESSION_MODEL) throw new Error(`defineDb: auth.model.session() must be registered as "${AUTH_SESSION_MODEL}" (got "${name}")`)
203
- }
204
- // A plain `model()` named User is just a model (no auth binding); only auth.model.user() binds.
205
- const user = isAuthModel(models[AUTH_USER_MODEL], "user") ? models[AUTH_USER_MODEL] : null
206
- let session = models[AUTH_SESSION_MODEL]
207
- if (session && !isAuthModel(session, "session")) throw new Error(`defineDb: "${AUTH_SESSION_MODEL}" is reserved for auth.model.session()`)
208
- if (!session) session = authSessionModel()
209
- if (user && !("user" in session.fields)) session = { ...session, fields: { ...session.fields, user: sessionUserField() } }
210
- return { models: { ...models, [AUTH_SESSION_MODEL]: session }, auth: { user: user ? AUTH_USER_MODEL : null, session: AUTH_SESSION_MODEL } }
211
- }
212
-
213
- export const defineDb = <const S extends Schema>(declared: S & ValidateRefs<S>, options: DefineDbOptions = {}): Db<S> => {
214
- const { models, auth } = withAuthModels(declared as Record<string, Model>)
215
- const metas = buildMeta(models)
216
- const transport = () => {
217
- const t = options.transport ?? seam.__lecodesDbTransport
218
- if (!t) throw new Error("db: no transport — the host must call setDbTransport() before user code runs")
219
- return t
220
- }
221
- // marcidb's own query builder over our metadata; the transport is the only host-specific piece
222
- const { collection } = createQueryLayer({
223
- models: modelsMeta(metas),
224
- run: op => transport().exec(op),
225
- // a single uuid key is generated client-side so `insert` needs no id (the .marci says @format(uuid))
226
- prepareInsert: (name, data) => (metas[name].uuidId && data?.id == null ? { ...data, id: crypto.randomUUID() } : data),
227
- })
228
- const db: Record<string, unknown> = {
229
- $schema: toMarci(metas),
230
- $models: models,
231
- $auth: auth,
232
- $transaction: async (ops: unknown[]) => transport().batch(ops.map(opOf)),
233
- }
234
- for (const name of Object.keys(metas)) db[uncap(name)] = collection(name)
235
- ;(seam.__lecodesDbs ??= []).push(db as Db<any>)
236
- return db as Db<S>
237
- }
@@ -1,132 +0,0 @@
1
- /**
2
- * `t` — field builders for `model({...})`. Each builder returns an immutable `Field` carrying a runtime
3
- * `FieldDef` (what `.marci` is emitted from) and two phantom type parameters: the TS value type and a
4
- * `FieldMeta` record (kind / optional / default / …) that `defineDb`'s type layer projects into row,
5
- * insert, update, select and where types. Chained modifiers return a NEW field (builders are never
6
- * mutated), so `const s = t.string(); { a: s, b: s.optional() }` behaves as expected.
7
- */
8
-
9
- import type { JsonValue } from "./marci/query"
10
-
11
- export type ScalarKind = "string" | "int" | "float" | "bool" | "date" | "uuid" | ""
12
- export type FieldKind = "scalar" | "json" | "enum" | "struct" | "one" | "many" | "list"
13
- export type OnDelete = "cascade" | "setNull" | "restrict"
14
-
15
- /** Phantom metadata — the type-level twin of `FieldDef`. */
16
- export type FieldMeta = {
17
- kind: FieldKind
18
- scalar: ScalarKind
19
- optional: boolean
20
- hasDefault: boolean
21
- array: boolean
22
- isId: boolean
23
- /** Related model name for one/many/list; "" otherwise. */
24
- model: string
25
- }
26
-
27
- type DefaultMeta = { kind: "scalar", scalar: "", optional: false, hasDefault: false, array: false, isId: false, model: "" }
28
- export type Meta<O extends Partial<FieldMeta>> = { [P in keyof FieldMeta]: P extends keyof O ? O[P] : DefaultMeta[P] }
29
- type SetMeta<M extends FieldMeta, K extends keyof FieldMeta, V> = { [P in keyof FieldMeta]: P extends K ? V : M[P] }
30
-
31
- /** Runtime field definition — everything the `.marci` emitter and the runtime need. */
32
- export type FieldDef = {
33
- kind: FieldKind
34
- scalar?: ScalarKind
35
- optional: boolean
36
- unique: boolean
37
- index: boolean
38
- hasDefault: boolean
39
- default?: unknown
40
- array: boolean
41
- isId: boolean
42
- onDelete?: OnDelete
43
- fulltext?: "multi" | "english" | "russian"
44
- vector?: { size: number, metric: "cosine" | "euclidean" }
45
- /** enum */
46
- values?: readonly string[]
47
- /** struct */
48
- fields?: Record<string, Field<any, any>>
49
- /** one / many / list */
50
- model?: string
51
- /** many: the field on the related model that holds the reference (`@bind`); resolved by defineDb when omitted. */
52
- back?: string
53
- }
54
-
55
- type DefaultOf<T, M extends FieldMeta> =
56
- M["scalar"] extends "date" ? "now" | number : M["array"] extends true ? T[] : T
57
-
58
- export interface Field<T, M extends FieldMeta = FieldMeta> {
59
- /** phantom (type-level only — never set at runtime) */ readonly __t?: T
60
- /** phantom (type-level only — never set at runtime) */ readonly __m?: M
61
- readonly def: FieldDef
62
- /** Nullable column (`String?`). */
63
- optional(): Field<T, SetMeta<M, "optional", true>>
64
- /** `@unique` (on a relation: one-to-one). */
65
- unique(): Field<T, M>
66
- /** `@index` — used by `$where`, `$order`, aggregations. */
67
- index(): Field<T, M>
68
- /** `@default(...)`; `"now"` on `t.date()` → `now()`. Makes the field optional on insert. */
69
- default(value: DefaultOf<T, M>): Field<T, SetMeta<M, "hasDefault", true>>
70
- /** Primitive list (`String[]`). */
71
- array(): Field<T, SetMeta<M, "array", true>>
72
- /** `@id` — primary key (several → composite). A field literally named `id` is the key implicitly. */
73
- id(): Field<T, SetMeta<M, "isId", true>>
74
- /** Referential policy for `t.one()` (defaults: required → restrict, optional → setNull). */
75
- onDelete(policy: OnDelete): Field<T, M>
76
- /** `@fulltext(...)` module index on a string field; query with `$search`. */
77
- fulltext(lang?: "multi" | "english" | "russian"): Field<T, M>
78
- /** `Float[n] @vector(metric)` — nearest-neighbour index; query with `$near`. */
79
- vector(size: number, metric?: "cosine" | "euclidean"): Field<T, M>
80
- }
81
-
82
- const create = <T, M extends FieldMeta>(def: FieldDef): Field<T, M> => {
83
- const next = (patch: Partial<FieldDef>) => create<any, any>({ ...def, ...patch })
84
- return {
85
- def,
86
- optional: () => next({ optional: true }),
87
- unique: () => next({ unique: true }),
88
- index: () => next({ index: true }),
89
- default: (value: unknown) => next({ hasDefault: true, default: value }),
90
- array: () => next({ array: true }),
91
- id: () => next({ isId: true }),
92
- onDelete: (policy) => next({ onDelete: policy }),
93
- fulltext: (lang = "multi") => next({ fulltext: lang }),
94
- vector: (size, metric = "cosine") => next({ vector: { size, metric } }),
95
- } as Field<T, M>
96
- }
97
-
98
- const base = (kind: FieldKind, extra: Partial<FieldDef> = {}): FieldDef => ({
99
- kind, optional: false, unique: false, index: false, hasDefault: false, array: false, isId: false, ...extra,
100
- })
101
-
102
- const scalar = <T, K extends ScalarKind>(scalar: K) =>
103
- create<T, Meta<{ kind: "scalar", scalar: K }>>(base("scalar", { scalar }))
104
-
105
- export const t = {
106
- string: () => scalar<string, "string">("string"),
107
- /** 64-bit signed integer. */
108
- int: () => scalar<number, "int">("int"),
109
- /** 64-bit float. */
110
- float: () => scalar<number, "float">("float"),
111
- bool: () => scalar<boolean, "bool">("bool"),
112
- /** Epoch milliseconds — reads and writes as `number`. */
113
- date: () => scalar<number, "date">("date"),
114
- /** UUID string (16 bytes in storage). On an `id` field the runtime generates it on insert. */
115
- uuid: () => scalar<string, "uuid">("uuid"),
116
- /** Any JSON value; `J` is TS-only (marcidb stores it schemaless). Filter by dot-path in `$where`. */
117
- json: <J = JsonValue>() => create<J, Meta<{ kind: "json" }>>(base("json")),
118
- /** String enum: `t.enum("draft", "published")`. */
119
- enum: <const V extends readonly [string, ...string[]]>(...values: V) =>
120
- create<V[number], Meta<{ kind: "enum" }>>(base("enum", { values })),
121
- /** Owned nested object (marcidb `struct`): created/updated with the parent, no id of its own. */
122
- struct: <F extends Record<string, Field<any, any>>>(fields: F) =>
123
- create<F, Meta<{ kind: "struct" }>>(base("struct", { fields })),
124
- /** Reference to one row of `model` (by id). Required unless `.optional()`. */
125
- one: <N extends string>(model: N) => create<never, Meta<{ kind: "one", model: N }>>(base("one", { model })),
126
- /** Reverse side of a `t.one()` on `model` (the field is inferred when unambiguous). Read-only list. */
127
- many: <N extends string>(model: N, backField?: string) =>
128
- create<never, Meta<{ kind: "many", model: N }>>(base("many", { model, back: backField })),
129
- /** Ordered relation list stored inline (`@list`) — hand-arranged collections, duplicates allowed. */
130
- list: <N extends string>(model: N) => create<never, Meta<{ kind: "list", model: N }>>(base("list", { model })),
131
- }
132
- export type T = typeof t
@@ -1,93 +0,0 @@
1
- /**
2
- * `MarciTransport` over marcidb-server's HTTP API (docs/HTTP-API.md in the marci-db repo) — the
3
- * transport the runner installs in every project worker (`setDbTransport(marciHttpTransport(url))`).
4
- * Host-side code (Bun/Node): uses the platform `fetch`, never the SDK bridge. Mirrors marcidb's own
5
- * `httpTransport` (crates/marcidb-ts/src/prefix.js) minus the binary read fast path.
6
- *
7
- * `baseUrl` = `http://host:port/<db>`; an optional bearer token (marcidb-server `MARCI_TOKEN`) is sent
8
- * on every request. Pass `fetch` explicitly when the global one might be monkey-patched by untrusted
9
- * code running in the same realm (the runner captures it before evaluating a project bundle).
10
- */
11
-
12
- import type { MarciOp, MarciTransport } from "./marci/query"
13
-
14
- /** A failed marcidb request: `status` is the HTTP status (0 = network failure), `message` its body. */
15
- export class MarciHttpError extends Error {
16
- readonly status: number
17
- constructor(status: number, message: string) {
18
- super(message)
19
- this.name = "MarciHttpError"
20
- this.status = status
21
- }
22
- }
23
-
24
- export type MarciHttpOptions = {
25
- token?: string | null
26
- fetch?: typeof fetch
27
- }
28
-
29
- /** `update/:id` and `delete/:id` take the key in the path: a bare value, or `k=v&k2=v2` for composite keys
30
- * (nested ids flatten with dots — `{ chat: { id: 1 } }` → `chat.id=1`). */
31
- export const encodeMarciId = (id: unknown): string => {
32
- if (typeof id === "object" && id !== null) {
33
- const parts: string[] = []
34
- const walk = (obj: Record<string, unknown>, prefix: string) => {
35
- for (const [k, v] of Object.entries(obj)) {
36
- const key = prefix ? `${prefix}.${k}` : k
37
- if (typeof v === "object" && v !== null) walk(v as Record<string, unknown>, key)
38
- else parts.push(`${key}=${encodeURIComponent(String(v))}`)
39
- }
40
- }
41
- walk(id as Record<string, unknown>, "")
42
- return parts.join("&")
43
- }
44
- return encodeURIComponent(String(id))
45
- }
46
-
47
- /** Low-level request helper (also used by the runner's admin calls: `$sync`, `DELETE /:db`). */
48
- export const marciRequest = async (
49
- url: string,
50
- init: { method?: string, body?: unknown, rawBody?: string, token?: string | null, fetch?: typeof fetch } = {},
51
- ): Promise<any> => {
52
- const f = init.fetch ?? fetch
53
- const headers: Record<string, string> = {}
54
- if (init.token) headers.authorization = `Bearer ${init.token}`
55
- let body: string | undefined
56
- if (init.rawBody !== undefined) { body = init.rawBody; headers["content-type"] = "text/plain" }
57
- else if (init.body !== undefined) { body = JSON.stringify(init.body); headers["content-type"] = "application/json" }
58
- let res: Response
59
- try {
60
- res = await f(url, { method: init.method ?? "POST", headers, body })
61
- } catch (e) {
62
- throw new MarciHttpError(0, `marcidb unreachable: ${(e as Error)?.message ?? e}`)
63
- }
64
- const text = await res.text()
65
- if (!res.ok) throw new MarciHttpError(res.status, text || `HTTP ${res.status}`)
66
- if (!text) return undefined
67
- try { return JSON.parse(text) } catch { return text }
68
- }
69
-
70
- export const marciHttpTransport = (baseUrl: string, options: MarciHttpOptions = {}): MarciTransport => {
71
- const url = baseUrl.replace(/\/+$/, "")
72
- const call = (path: string, body?: unknown) => marciRequest(`${url}/${path}`, { body, token: options.token, fetch: options.fetch })
73
- return {
74
- exec(op: MarciOp) {
75
- switch (op.action) {
76
- case "findMany": return call(`${op.model}/findMany`, op.query ?? {})
77
- case "findFirst": return call(`${op.model}/findFirst`, op.query ?? {})
78
- case "insert": return call(`${op.model}/insert`, op.data)
79
- case "update": return call(`${op.model}/update/${encodeMarciId(op.id)}`, op.data).then(() => null)
80
- // no id in the path — the filter and the payload travel together in the body
81
- case "updateMany": return call(`${op.model}/updateMany`, { ...(op.query ?? {}), data: op.data })
82
- case "delete": return call(`${op.model}/delete/${encodeMarciId(op.id)}`).then(() => null)
83
- case "deleteMany": return call(`${op.model}/deleteMany`, op.query ?? {})
84
- case "count": return call(`${op.model}/count`, op.query ?? {})
85
- case "aggregate": return call(`${op.model}/aggregate`, op.query ?? {})
86
- default: return Promise.reject(new Error(`marcidb: unknown action '${op.action}'`))
87
- }
88
- },
89
- batch(ops: MarciOp[]) {
90
- return call("$transaction", ops)
91
- },
92
- }
93
- }
@@ -1,7 +0,0 @@
1
- // Server-side db layer (phase 0 prototype of docs/backend-plan.md §3.1). Not yet part of inject.ts.
2
- export { t } from "./fields"
3
- export type { Field, FieldDef, FieldMeta } from "./fields"
4
- export { model, defineDb, ref, setDbTransport, toMarci } from "./defineDb"
5
- export type { DefineDbOptions } from "./defineDb"
6
- export type { Db, Model, Schema, Row, Id, Insert, Update, Select, Where, Query, QueryObject, Result, Collection, ScalarSelect, Types } from "./types"
7
- export type { Op, MarciOp, MarciTransport, JsonValue, Sub } from "./marci/query"