lecodes-cli 0.19.0 → 0.19.2

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 (254) hide show
  1. package/dist/index.js +730 -235
  2. package/package.json +14 -13
  3. package/runtime/scene-harness.json +1 -1
  4. package/runtime/sdk-types.json +1 -1
  5. package/src/api.ts +302 -0
  6. package/src/browserAuth.ts +87 -0
  7. package/src/cmgenTool.ts +104 -0
  8. package/src/commands/app.ts +891 -0
  9. package/src/commands/appAndroid.ts +603 -0
  10. package/src/commands/appDesktop.ts +326 -0
  11. package/src/commands/appDesktopMac.ts +470 -0
  12. package/src/commands/appIcon.ts +187 -0
  13. package/src/commands/appShared.ts +448 -0
  14. package/src/commands/appTemplates.ts +883 -0
  15. package/src/commands/appTemplatesAndroid.ts +599 -0
  16. package/src/commands/appTemplatesGradlew.ts +9 -0
  17. package/src/commands/assets.ts +28 -0
  18. package/src/commands/clone.ts +60 -0
  19. package/src/commands/compile.ts +253 -0
  20. package/src/commands/create.ts +59 -0
  21. package/src/commands/design.ts +863 -0
  22. package/src/commands/designTemplates.ts +11 -0
  23. package/src/commands/desktop.ts +122 -0
  24. package/src/commands/dev.ts +214 -0
  25. package/src/commands/diff.ts +59 -0
  26. package/src/commands/init.ts +191 -0
  27. package/src/commands/install.ts +198 -0
  28. package/src/commands/lightmap.ts +290 -0
  29. package/src/commands/link.ts +147 -0
  30. package/src/commands/login.ts +24 -0
  31. package/src/commands/navmesh.ts +225 -0
  32. package/src/commands/pn.ts +305 -0
  33. package/src/commands/projectTemplates.ts +129 -0
  34. package/src/commands/pull.ts +109 -0
  35. package/src/commands/push.ts +173 -0
  36. package/src/commands/render.ts +414 -0
  37. package/src/commands/scene.ts +148 -0
  38. package/src/commands/shaders.ts +157 -0
  39. package/src/commands/status.ts +29 -0
  40. package/src/commands/test.ts +370 -0
  41. package/src/commands/thumbs.ts +178 -0
  42. package/src/commands/types.ts +87 -0
  43. package/src/commands/update.ts +190 -0
  44. package/src/compile/assetIcons.ts +214 -0
  45. package/src/compile/collect.ts +46 -0
  46. package/src/compile/collectLocal.ts +37 -0
  47. package/src/compile/designCompile.ts +104 -0
  48. package/src/compile/fonts.ts +156 -0
  49. package/src/compile/headlessBundle.ts +129 -0
  50. package/src/compile/nativeStack.ts +21 -0
  51. package/src/compile/projectCompile.ts +109 -0
  52. package/src/compile/sceneCompile.ts +113 -0
  53. package/src/compile/screenEntry.ts +127 -0
  54. package/src/compile/shaders.ts +245 -0
  55. package/src/config.ts +42 -0
  56. package/src/designMeta.ts +35 -0
  57. package/src/desktopRenderer.ts +532 -0
  58. package/src/desktopScript.ts +276 -0
  59. package/src/dev/clientTemplates.ts +160 -0
  60. package/src/dev/devServer.ts +292 -0
  61. package/src/dev/wsServer.ts +144 -0
  62. package/src/distRoot.ts +20 -0
  63. package/src/ignore.ts +163 -0
  64. package/src/index.ts +491 -0
  65. package/src/lecodes-3d-editor.d.ts +41 -0
  66. package/src/lecodes-assets.d.ts +7 -0
  67. package/src/lecodes-design.d.ts +191 -0
  68. package/src/lecodes-renderer.d.ts +131 -0
  69. package/src/localFiles.ts +144 -0
  70. package/src/manifest.ts +46 -0
  71. package/src/matcTool.ts +137 -0
  72. package/src/peers.ts +40 -0
  73. package/src/project.ts +61 -0
  74. package/src/projectEnv.ts +94 -0
  75. package/src/qrcode-terminal.d.ts +9 -0
  76. package/src/releases.ts +125 -0
  77. package/src/serverDiff.ts +78 -0
  78. package/src/textDiff.ts +103 -0
  79. package/src/types.ts +0 -0
  80. package/src/util.ts +146 -0
  81. package/runtime/sdk/animate/animate.ts +0 -238
  82. package/runtime/sdk/animate/bezier.ts +0 -138
  83. package/runtime/sdk/animate/easings.ts +0 -126
  84. package/runtime/sdk/canvas/Canvas.ts +0 -305
  85. package/runtime/sdk/compile/aspectMacro.ts +0 -86
  86. package/runtime/sdk/compile/assetIconMacro.ts +0 -384
  87. package/runtime/sdk/compile/assetMacro.ts +0 -146
  88. package/runtime/sdk/compile/assetName.ts +0 -50
  89. package/runtime/sdk/compile/bundler.ts +0 -287
  90. package/runtime/sdk/compile/compileProject.ts +0 -129
  91. package/runtime/sdk/compile/detectEntry.ts +0 -128
  92. package/runtime/sdk/compile/fontMacro.ts +0 -459
  93. package/runtime/sdk/compile/fontRegistry.ts +0 -78
  94. package/runtime/sdk/compile/header.ts +0 -67
  95. package/runtime/sdk/compile/index.ts +0 -100
  96. package/runtime/sdk/compile/libraryImports.ts +0 -52
  97. package/runtime/sdk/compile/liteMaterial.ts +0 -247
  98. package/runtime/sdk/compile/sceneEditor.ts +0 -88
  99. package/runtime/sdk/compile/serverSplit.ts +0 -233
  100. package/runtime/sdk/compile/serverTypes.ts +0 -227
  101. package/runtime/sdk/compile/sfnt.ts +0 -98
  102. package/runtime/sdk/compile/shaderSchema.ts +0 -202
  103. package/runtime/sdk/compile/shaderTargets.ts +0 -81
  104. package/runtime/sdk/compile/sourcemap.ts +0 -25
  105. package/runtime/sdk/core/Aspect.ts +0 -568
  106. package/runtime/sdk/core/InspectorUI.ts +0 -212
  107. package/runtime/sdk/core/StateMachine.ts +0 -308
  108. package/runtime/sdk/core/color.ts +0 -66
  109. package/runtime/sdk/core/compWrite.ts +0 -42
  110. package/runtime/sdk/core/events.ts +0 -38
  111. package/runtime/sdk/core/fields.ts +0 -120
  112. package/runtime/sdk/core/registry.ts +0 -23
  113. package/runtime/sdk/core/signals.ts +0 -277
  114. package/runtime/sdk/core/time.ts +0 -84
  115. package/runtime/sdk/g2/Camera2D.ts +0 -40
  116. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  117. package/runtime/sdk/g2/Node2D.ts +0 -267
  118. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  119. package/runtime/sdk/g2/Physics2D.ts +0 -346
  120. package/runtime/sdk/g2/Scene2D.ts +0 -209
  121. package/runtime/sdk/g2/Shape2D.ts +0 -259
  122. package/runtime/sdk/g2/Sprite.ts +0 -89
  123. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  124. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  125. package/runtime/sdk/g2/Texture2D.ts +0 -47
  126. package/runtime/sdk/g2/Tilemap.ts +0 -41
  127. package/runtime/sdk/g2/Tileset.ts +0 -71
  128. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  129. package/runtime/sdk/g2/autotile.ts +0 -433
  130. package/runtime/sdk/g2/cells.ts +0 -91
  131. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  132. package/runtime/sdk/g2/groups2d.ts +0 -106
  133. package/runtime/sdk/g2/loop.ts +0 -50
  134. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  135. package/runtime/sdk/g2/touch.ts +0 -83
  136. package/runtime/sdk/gl/Camera.ts +0 -160
  137. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  138. package/runtime/sdk/gl/CharacterController.ts +0 -238
  139. package/runtime/sdk/gl/Gearbox.ts +0 -212
  140. package/runtime/sdk/gl/Geometry.ts +0 -279
  141. package/runtime/sdk/gl/IK.ts +0 -193
  142. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  143. package/runtime/sdk/gl/Light.ts +0 -107
  144. package/runtime/sdk/gl/Lightmap.ts +0 -256
  145. package/runtime/sdk/gl/Material.ts +0 -260
  146. package/runtime/sdk/gl/Mesh.ts +0 -83
  147. package/runtime/sdk/gl/Model.ts +0 -124
  148. package/runtime/sdk/gl/NavAgent.ts +0 -337
  149. package/runtime/sdk/gl/NavMesh.ts +0 -397
  150. package/runtime/sdk/gl/Node.ts +0 -350
  151. package/runtime/sdk/gl/Noise.ts +0 -30
  152. package/runtime/sdk/gl/Particles.ts +0 -676
  153. package/runtime/sdk/gl/Physics.ts +0 -222
  154. package/runtime/sdk/gl/Plane.ts +0 -53
  155. package/runtime/sdk/gl/Ragdoll.ts +0 -270
  156. package/runtime/sdk/gl/Ray.ts +0 -16
  157. package/runtime/sdk/gl/Scene.ts +0 -486
  158. package/runtime/sdk/gl/Shape.ts +0 -409
  159. package/runtime/sdk/gl/Terrain.ts +0 -1092
  160. package/runtime/sdk/gl/Texture.ts +0 -63
  161. package/runtime/sdk/gl/Trigger.ts +0 -45
  162. package/runtime/sdk/gl/Vehicle.ts +0 -473
  163. package/runtime/sdk/gl/Wheel.ts +0 -240
  164. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  165. package/runtime/sdk/gl/animation/Animator.ts +0 -120
  166. package/runtime/sdk/gl/animation/Layer.ts +0 -31
  167. package/runtime/sdk/gl/animation/Loop.ts +0 -30
  168. package/runtime/sdk/gl/animation/Playback.ts +0 -44
  169. package/runtime/sdk/gl/animation/core.ts +0 -336
  170. package/runtime/sdk/gl/controls.ts +0 -95
  171. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  172. package/runtime/sdk/gl/scenarios.ts +0 -291
  173. package/runtime/sdk/gl/state.ts +0 -6
  174. package/runtime/sdk/gl/terrainMesh.ts +0 -219
  175. package/runtime/sdk/gl/touch.ts +0 -68
  176. package/runtime/sdk/inject.ts +0 -197
  177. package/runtime/sdk/math/Mathf.ts +0 -118
  178. package/runtime/sdk/math/mat4.ts +0 -278
  179. package/runtime/sdk/math/quat.ts +0 -232
  180. package/runtime/sdk/math/vec.ts +0 -255
  181. package/runtime/sdk/net/codec.ts +0 -119
  182. package/runtime/sdk/net/core.ts +0 -384
  183. package/runtime/sdk/net/index.ts +0 -181
  184. package/runtime/sdk/net/replication.ts +0 -622
  185. package/runtime/sdk/plugins/camera.ts +0 -81
  186. package/runtime/sdk/plugins/geolocation.ts +0 -123
  187. package/runtime/sdk/plugins/oauth.ts +0 -61
  188. package/runtime/sdk/plugins/permission.ts +0 -7
  189. package/runtime/sdk/plugins/push.ts +0 -132
  190. package/runtime/sdk/plugins/qr.ts +0 -73
  191. package/runtime/sdk/plugins/service.ts +0 -47
  192. package/runtime/sdk/runtime/app.ts +0 -101
  193. package/runtime/sdk/runtime/appEvents.ts +0 -54
  194. package/runtime/sdk/runtime/channel.ts +0 -61
  195. package/runtime/sdk/runtime/clipboard.ts +0 -20
  196. package/runtime/sdk/runtime/datetime.ts +0 -329
  197. package/runtime/sdk/runtime/device.ts +0 -293
  198. package/runtime/sdk/runtime/fetch.ts +0 -77
  199. package/runtime/sdk/runtime/files.ts +0 -23
  200. package/runtime/sdk/runtime/input.ts +0 -175
  201. package/runtime/sdk/runtime/media.ts +0 -111
  202. package/runtime/sdk/runtime/misc.ts +0 -16
  203. package/runtime/sdk/runtime/net.ts +0 -36
  204. package/runtime/sdk/runtime/rpc.ts +0 -218
  205. package/runtime/sdk/runtime/service.ts +0 -83
  206. package/runtime/sdk/runtime/share.ts +0 -9
  207. package/runtime/sdk/runtime/storage.ts +0 -13
  208. package/runtime/sdk/runtime/touch.ts +0 -76
  209. package/runtime/sdk/scene/defineScene.ts +0 -1353
  210. package/runtime/sdk/scene/editorPlugins.ts +0 -110
  211. package/runtime/sdk/scene/gizmos.ts +0 -148
  212. package/runtime/sdk/scene/grammar.ts +0 -120
  213. package/runtime/sdk/scene/material.ts +0 -188
  214. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  215. package/runtime/sdk/server/auth/global.ts +0 -80
  216. package/runtime/sdk/server/auth/host.ts +0 -318
  217. package/runtime/sdk/server/auth/models.ts +0 -83
  218. package/runtime/sdk/server/auth/types.ts +0 -50
  219. package/runtime/sdk/server/channel.ts +0 -56
  220. package/runtime/sdk/server/context.ts +0 -36
  221. package/runtime/sdk/server/db/defineDb.ts +0 -237
  222. package/runtime/sdk/server/db/fields.ts +0 -132
  223. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  224. package/runtime/sdk/server/db/index.ts +0 -7
  225. package/runtime/sdk/server/db/marci/query.ts +0 -412
  226. package/runtime/sdk/server/db/types.ts +0 -202
  227. package/runtime/sdk/server/errors.ts +0 -12
  228. package/runtime/sdk/server/host.ts +0 -74
  229. package/runtime/sdk/server/inject.ts +0 -13
  230. package/runtime/sdk/server/runtime.ts +0 -133
  231. package/runtime/sdk/server/validate.ts +0 -87
  232. package/runtime/sdk/ui/NativeView.ts +0 -142
  233. package/runtime/sdk/ui/UI.ts +0 -39
  234. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  235. package/runtime/sdk/ui/UIButton.ts +0 -101
  236. package/runtime/sdk/ui/UIContainer.ts +0 -60
  237. package/runtime/sdk/ui/UIImage.ts +0 -83
  238. package/runtime/sdk/ui/UIInput.ts +0 -185
  239. package/runtime/sdk/ui/UIModal.ts +0 -139
  240. package/runtime/sdk/ui/UINode.ts +0 -830
  241. package/runtime/sdk/ui/UIPager.ts +0 -362
  242. package/runtime/sdk/ui/UIPopover.ts +0 -100
  243. package/runtime/sdk/ui/UIScreen.ts +0 -123
  244. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  245. package/runtime/sdk/ui/UISpacer.ts +0 -14
  246. package/runtime/sdk/ui/UITabs.ts +0 -236
  247. package/runtime/sdk/ui/UIText.ts +0 -51
  248. package/runtime/sdk/ui/UIVideo.ts +0 -88
  249. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  250. package/runtime/sdk/ui/UIWidget.ts +0 -127
  251. package/runtime/sdk/ui/fonts.ts +0 -13
  252. package/runtime/sdk/ui/presentable.ts +0 -117
  253. package/runtime/sdk/ui/router.ts +0 -132
  254. 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"