lecodes-cli 0.18.2 → 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 (224) hide show
  1. package/dist/index.js +1866 -301
  2. package/package.json +14 -12
  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/core/Aspect.ts +0 -512
  86. package/runtime/sdk/core/InspectorUI.ts +0 -212
  87. package/runtime/sdk/core/color.ts +0 -66
  88. package/runtime/sdk/core/compWrite.ts +0 -42
  89. package/runtime/sdk/core/events.ts +0 -38
  90. package/runtime/sdk/core/fields.ts +0 -120
  91. package/runtime/sdk/core/registry.ts +0 -23
  92. package/runtime/sdk/core/signals.ts +0 -277
  93. package/runtime/sdk/core/time.ts +0 -81
  94. package/runtime/sdk/g2/Camera2D.ts +0 -40
  95. package/runtime/sdk/g2/CharacterController2D.ts +0 -276
  96. package/runtime/sdk/g2/Node2D.ts +0 -267
  97. package/runtime/sdk/g2/OneWay2D.ts +0 -66
  98. package/runtime/sdk/g2/Physics2D.ts +0 -346
  99. package/runtime/sdk/g2/Scene2D.ts +0 -209
  100. package/runtime/sdk/g2/Shape2D.ts +0 -259
  101. package/runtime/sdk/g2/Sprite.ts +0 -89
  102. package/runtime/sdk/g2/SpriteAnimation.ts +0 -171
  103. package/runtime/sdk/g2/SpriteSheet.ts +0 -166
  104. package/runtime/sdk/g2/Texture2D.ts +0 -47
  105. package/runtime/sdk/g2/Tilemap.ts +0 -41
  106. package/runtime/sdk/g2/Tileset.ts +0 -71
  107. package/runtime/sdk/g2/Trigger2D.ts +0 -77
  108. package/runtime/sdk/g2/autotile.ts +0 -433
  109. package/runtime/sdk/g2/cells.ts +0 -91
  110. package/runtime/sdk/g2/defineScene2d.ts +0 -381
  111. package/runtime/sdk/g2/groups2d.ts +0 -106
  112. package/runtime/sdk/g2/loop.ts +0 -50
  113. package/runtime/sdk/g2/scenarios2d.ts +0 -69
  114. package/runtime/sdk/g2/touch.ts +0 -83
  115. package/runtime/sdk/gl/Camera.ts +0 -160
  116. package/runtime/sdk/gl/CameraPlace.ts +0 -52
  117. package/runtime/sdk/gl/CharacterController.ts +0 -238
  118. package/runtime/sdk/gl/Gearbox.ts +0 -212
  119. package/runtime/sdk/gl/Geometry.ts +0 -279
  120. package/runtime/sdk/gl/IK.ts +0 -193
  121. package/runtime/sdk/gl/InstancedMesh.ts +0 -132
  122. package/runtime/sdk/gl/Light.ts +0 -99
  123. package/runtime/sdk/gl/Lightmap.ts +0 -179
  124. package/runtime/sdk/gl/Material.ts +0 -245
  125. package/runtime/sdk/gl/Mesh.ts +0 -83
  126. package/runtime/sdk/gl/Model.ts +0 -64
  127. package/runtime/sdk/gl/Node.ts +0 -350
  128. package/runtime/sdk/gl/Noise.ts +0 -30
  129. package/runtime/sdk/gl/Particles.ts +0 -676
  130. package/runtime/sdk/gl/Physics.ts +0 -222
  131. package/runtime/sdk/gl/Plane.ts +0 -53
  132. package/runtime/sdk/gl/Ray.ts +0 -16
  133. package/runtime/sdk/gl/Scene.ts +0 -479
  134. package/runtime/sdk/gl/Shape.ts +0 -377
  135. package/runtime/sdk/gl/Texture.ts +0 -46
  136. package/runtime/sdk/gl/Trigger.ts +0 -45
  137. package/runtime/sdk/gl/Vehicle.ts +0 -473
  138. package/runtime/sdk/gl/Wheel.ts +0 -240
  139. package/runtime/sdk/gl/animation/AnimationClip.ts +0 -204
  140. package/runtime/sdk/gl/animation/Animator.ts +0 -87
  141. package/runtime/sdk/gl/animation/Layer.ts +0 -29
  142. package/runtime/sdk/gl/animation/Loop.ts +0 -25
  143. package/runtime/sdk/gl/animation/Playback.ts +0 -43
  144. package/runtime/sdk/gl/animation/core.ts +0 -294
  145. package/runtime/sdk/gl/controls.ts +0 -95
  146. package/runtime/sdk/gl/physicsEvents.ts +0 -20
  147. package/runtime/sdk/gl/scenarios.ts +0 -291
  148. package/runtime/sdk/gl/state.ts +0 -6
  149. package/runtime/sdk/gl/touch.ts +0 -68
  150. package/runtime/sdk/inject.ts +0 -186
  151. package/runtime/sdk/math/Mathf.ts +0 -118
  152. package/runtime/sdk/math/mat4.ts +0 -278
  153. package/runtime/sdk/math/quat.ts +0 -232
  154. package/runtime/sdk/math/vec.ts +0 -255
  155. package/runtime/sdk/plugins/camera.ts +0 -81
  156. package/runtime/sdk/plugins/geolocation.ts +0 -123
  157. package/runtime/sdk/plugins/oauth.ts +0 -61
  158. package/runtime/sdk/plugins/permission.ts +0 -7
  159. package/runtime/sdk/plugins/push.ts +0 -132
  160. package/runtime/sdk/plugins/qr.ts +0 -73
  161. package/runtime/sdk/plugins/service.ts +0 -47
  162. package/runtime/sdk/runtime/app.ts +0 -101
  163. package/runtime/sdk/runtime/appEvents.ts +0 -54
  164. package/runtime/sdk/runtime/channel.ts +0 -61
  165. package/runtime/sdk/runtime/clipboard.ts +0 -20
  166. package/runtime/sdk/runtime/datetime.ts +0 -329
  167. package/runtime/sdk/runtime/device.ts +0 -293
  168. package/runtime/sdk/runtime/fetch.ts +0 -77
  169. package/runtime/sdk/runtime/files.ts +0 -23
  170. package/runtime/sdk/runtime/input.ts +0 -175
  171. package/runtime/sdk/runtime/media.ts +0 -111
  172. package/runtime/sdk/runtime/misc.ts +0 -16
  173. package/runtime/sdk/runtime/net.ts +0 -36
  174. package/runtime/sdk/runtime/rpc.ts +0 -218
  175. package/runtime/sdk/runtime/service.ts +0 -83
  176. package/runtime/sdk/runtime/share.ts +0 -9
  177. package/runtime/sdk/runtime/storage.ts +0 -13
  178. package/runtime/sdk/runtime/touch.ts +0 -76
  179. package/runtime/sdk/scene/defineScene.ts +0 -1227
  180. package/runtime/sdk/scene/editorPlugins.ts +0 -92
  181. package/runtime/sdk/scene/gizmos.ts +0 -148
  182. package/runtime/sdk/scene/grammar.ts +0 -120
  183. package/runtime/sdk/scene/material.ts +0 -188
  184. package/runtime/sdk/server/auth/appConfig.ts +0 -12
  185. package/runtime/sdk/server/auth/global.ts +0 -80
  186. package/runtime/sdk/server/auth/host.ts +0 -318
  187. package/runtime/sdk/server/auth/models.ts +0 -83
  188. package/runtime/sdk/server/auth/types.ts +0 -50
  189. package/runtime/sdk/server/channel.ts +0 -56
  190. package/runtime/sdk/server/context.ts +0 -36
  191. package/runtime/sdk/server/db/defineDb.ts +0 -237
  192. package/runtime/sdk/server/db/fields.ts +0 -132
  193. package/runtime/sdk/server/db/httpTransport.ts +0 -93
  194. package/runtime/sdk/server/db/index.ts +0 -7
  195. package/runtime/sdk/server/db/marci/query.ts +0 -412
  196. package/runtime/sdk/server/db/types.ts +0 -202
  197. package/runtime/sdk/server/errors.ts +0 -12
  198. package/runtime/sdk/server/host.ts +0 -74
  199. package/runtime/sdk/server/inject.ts +0 -13
  200. package/runtime/sdk/server/runtime.ts +0 -133
  201. package/runtime/sdk/server/validate.ts +0 -87
  202. package/runtime/sdk/ui/NativeView.ts +0 -142
  203. package/runtime/sdk/ui/UI.ts +0 -39
  204. package/runtime/sdk/ui/UIBottomSheet.ts +0 -139
  205. package/runtime/sdk/ui/UIButton.ts +0 -101
  206. package/runtime/sdk/ui/UIContainer.ts +0 -60
  207. package/runtime/sdk/ui/UIImage.ts +0 -83
  208. package/runtime/sdk/ui/UIInput.ts +0 -185
  209. package/runtime/sdk/ui/UIModal.ts +0 -139
  210. package/runtime/sdk/ui/UINode.ts +0 -826
  211. package/runtime/sdk/ui/UIPager.ts +0 -362
  212. package/runtime/sdk/ui/UIPopover.ts +0 -100
  213. package/runtime/sdk/ui/UIScreen.ts +0 -123
  214. package/runtime/sdk/ui/UIScrollable.ts +0 -87
  215. package/runtime/sdk/ui/UISpacer.ts +0 -14
  216. package/runtime/sdk/ui/UITabs.ts +0 -236
  217. package/runtime/sdk/ui/UIText.ts +0 -51
  218. package/runtime/sdk/ui/UIVideo.ts +0 -88
  219. package/runtime/sdk/ui/UIVirtualizedList.ts +0 -241
  220. package/runtime/sdk/ui/UIWidget.ts +0 -127
  221. package/runtime/sdk/ui/fonts.ts +0 -13
  222. package/runtime/sdk/ui/presentable.ts +0 -117
  223. package/runtime/sdk/ui/router.ts +0 -132
  224. 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"