@primate/core 0.5.0 → 0.6.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 (297) hide show
  1. package/lib/private/App.d.ts +263 -26
  2. package/lib/private/App.js +42 -14
  3. package/lib/private/Flags.d.ts +8 -4
  4. package/lib/private/Module.d.ts +4 -16
  5. package/lib/private/Module.js +1 -17
  6. package/lib/private/app/EnvSchema.d.ts +5 -0
  7. package/lib/private/app/EnvSchema.js +2 -0
  8. package/lib/private/app/Facade.browser.d.ts +11 -0
  9. package/lib/private/app/Facade.browser.js +19 -0
  10. package/lib/private/app/Facade.d.ts +272 -27
  11. package/lib/private/app/Facade.js +23 -0
  12. package/lib/private/build/client/index.js +8 -3
  13. package/lib/private/build/client/plugin/app-request.d.ts +4 -0
  14. package/lib/private/build/client/plugin/app-request.js +19 -0
  15. package/lib/private/build/client/plugin/view.d.ts +4 -0
  16. package/lib/private/build/client/plugin/view.js +13 -0
  17. package/lib/private/build/hook.d.ts +1 -2
  18. package/lib/private/build/hook.js +21 -16
  19. package/lib/private/build/index.d.ts +2 -1
  20. package/lib/private/build/index.js +20 -21
  21. package/lib/private/build/server/index.js +7 -9
  22. package/lib/private/build/server/plugin/app-request.d.ts +4 -0
  23. package/lib/private/build/server/plugin/app-request.js +19 -0
  24. package/lib/private/build/server/plugin/assets.js +1 -1
  25. package/lib/private/build/server/plugin/live-reload.js +9 -9
  26. package/lib/private/build/server/plugin/native-addons.js +4 -7
  27. package/lib/private/build/server/plugin/route.js +2 -2
  28. package/lib/private/build/server/plugin/store.js +9 -31
  29. package/lib/private/build/server/plugin/stores.js +11 -7
  30. package/lib/private/build/server/plugin/view.js +1 -1
  31. package/lib/private/build/shared/plugin/app-request.d.ts +4 -0
  32. package/lib/private/build/shared/plugin/app-request.js +19 -0
  33. package/lib/private/client/Data.d.ts +3 -2
  34. package/lib/private/{frontend → client}/Render.d.ts +1 -1
  35. package/lib/private/{frontend → client}/ViewResponse.d.ts +1 -1
  36. package/lib/private/client/app.js +1 -2
  37. package/lib/private/client/boot.d.ts +5 -0
  38. package/lib/private/client/boot.js +64 -0
  39. package/lib/private/client/create-form.d.ts +1 -0
  40. package/lib/private/client/create-form.js +19 -20
  41. package/lib/private/client/extract-issues.js +2 -1
  42. package/lib/private/client/http.d.ts +13 -0
  43. package/lib/private/client/http.js +57 -0
  44. package/lib/private/client/index.d.ts +23 -0
  45. package/lib/private/client/index.js +16 -0
  46. package/lib/private/client/navigate.d.ts +13 -0
  47. package/lib/private/client/navigate.js +67 -0
  48. package/lib/private/client/root.d.ts +9 -0
  49. package/lib/private/client/root.js +11 -0
  50. package/lib/private/client/submit.d.ts +2 -0
  51. package/lib/private/client/submit.js +41 -0
  52. package/lib/private/config/index.d.ts +7 -2
  53. package/lib/private/config/index.js +3 -2
  54. package/lib/private/config/schema.d.ts +82 -26
  55. package/lib/private/config/schema.js +17 -4
  56. package/lib/private/cookie.d.ts +12 -6
  57. package/lib/private/db/DB.d.ts +21 -5
  58. package/lib/private/db/DB.js +1 -0
  59. package/lib/private/db/MemoryDB.d.ts +2 -4
  60. package/lib/private/db/MemoryDB.js +40 -22
  61. package/lib/private/db/common.d.ts +7 -0
  62. package/lib/private/db/common.js +31 -0
  63. package/lib/private/db/errors.d.ts +104 -0
  64. package/lib/private/db/errors.js +237 -0
  65. package/lib/private/db/migrate/apply.d.ts +2 -0
  66. package/lib/private/db/migrate/apply.js +32 -0
  67. package/lib/private/db/migrate/bundle.d.ts +3 -0
  68. package/lib/private/db/migrate/bundle.js +22 -0
  69. package/lib/private/db/migrate/create.d.ts +2 -0
  70. package/lib/private/db/migrate/create.js +154 -0
  71. package/lib/private/db/migrate/index.d.ts +10 -0
  72. package/lib/private/db/migrate/index.js +6 -0
  73. package/lib/private/db/migrate/status.d.ts +2 -0
  74. package/lib/private/db/migrate/status.js +38 -0
  75. package/lib/private/db/migrate/store.d.ts +5 -0
  76. package/lib/private/db/migrate/store.js +33 -0
  77. package/lib/private/db/sql.js +3 -3
  78. package/lib/private/db/test.js +461 -95
  79. package/lib/private/errors.d.ts +88 -0
  80. package/lib/private/errors.js +211 -0
  81. package/lib/private/frontend.d.ts +72 -0
  82. package/lib/private/frontend.js +245 -0
  83. package/lib/private/i18n/Formatter.js +2 -2
  84. package/lib/private/i18n/errors.d.ts +16 -0
  85. package/lib/private/i18n/errors.js +27 -0
  86. package/lib/private/i18n/module.d.ts +3 -0
  87. package/lib/private/i18n/module.js +115 -0
  88. package/lib/private/i18n/schema.d.ts +10 -5
  89. package/lib/private/i18n/validate.js +5 -7
  90. package/lib/private/index.d.ts +14 -0
  91. package/lib/private/index.js +2 -0
  92. package/lib/private/log.js +6 -5
  93. package/lib/private/module/Setup.d.ts +21 -0
  94. package/lib/private/module/Setup.js +2 -0
  95. package/lib/private/module/create.d.ts +16 -0
  96. package/lib/private/module/create.js +28 -0
  97. package/lib/private/orm/ExtractSchema.d.ts +9 -0
  98. package/lib/private/orm/ExtractSchema.js +2 -0
  99. package/lib/private/orm/ForeignKey.d.ts +5 -2
  100. package/lib/private/orm/ForeignKey.js +3 -0
  101. package/lib/private/orm/PrimaryKey.d.ts +5 -3
  102. package/lib/private/orm/PrimaryKey.js +9 -6
  103. package/lib/private/orm/StoreInput.d.ts +10 -0
  104. package/lib/private/orm/StoreInput.js +2 -0
  105. package/lib/private/orm/key.d.ts +6 -6
  106. package/lib/private/orm/key.js +7 -3
  107. package/lib/private/orm/parse.d.ts +5 -4
  108. package/lib/private/orm/parse.js +2 -2
  109. package/lib/private/orm/relation.d.ts +2 -2
  110. package/lib/private/orm/{Store.d.ts → store.d.ts} +33 -17
  111. package/lib/private/orm/{Store.js → store.js} +91 -67
  112. package/lib/private/paths.js +3 -3
  113. package/lib/private/request/RequestBag.d.ts +4 -0
  114. package/lib/private/request/RequestBag.js +5 -2
  115. package/lib/private/request/RequestBody.d.ts +6 -9
  116. package/lib/private/request/RequestBody.js +49 -54
  117. package/lib/private/request/RequestFacade.d.ts +3 -8
  118. package/lib/private/request/RequestPublic.d.ts +9 -0
  119. package/lib/private/request/RequestPublic.js +2 -0
  120. package/lib/private/request/RequestView.d.ts +11 -0
  121. package/lib/private/request/RequestView.js +3 -0
  122. package/lib/private/request/handle.d.ts +4 -0
  123. package/lib/private/request/handle.js +18 -0
  124. package/lib/private/request/parse.js +1 -0
  125. package/lib/private/request/route.js +8 -9
  126. package/lib/private/request/router.js +21 -49
  127. package/lib/private/request/storage.d.ts +4 -0
  128. package/lib/private/request/storage.js +5 -0
  129. package/lib/private/response/ResponseFunction.d.ts +1 -1
  130. package/lib/private/response/binary.js +1 -1
  131. package/lib/private/response/error.d.ts +1 -1
  132. package/lib/private/response/error.js +1 -1
  133. package/lib/private/response/json.d.ts +1 -1
  134. package/lib/private/response/json.js +1 -1
  135. package/lib/private/response/redirect.d.ts +5 -5
  136. package/lib/private/response/redirect.js +8 -9
  137. package/lib/private/response/respond.js +9 -7
  138. package/lib/private/response/sse.d.ts +1 -1
  139. package/lib/private/response/sse.js +1 -1
  140. package/lib/private/response/text.d.ts +1 -1
  141. package/lib/private/response/text.js +1 -1
  142. package/lib/private/response/view.d.ts +1 -1
  143. package/lib/private/response/view.js +6 -13
  144. package/lib/private/response/ws.d.ts +1 -1
  145. package/lib/private/route/router.d.ts +3 -3
  146. package/lib/private/route/router.js +7 -10
  147. package/lib/private/serve/App.d.ts +6 -7
  148. package/lib/private/serve/App.js +52 -43
  149. package/lib/private/serve/Init.d.ts +2 -2
  150. package/lib/private/serve/dev-module.d.ts +2 -0
  151. package/lib/private/serve/dev-module.js +34 -0
  152. package/lib/private/serve/hook.d.ts +1 -2
  153. package/lib/private/serve/hook.js +2 -3
  154. package/lib/private/serve/index.d.ts +1 -1
  155. package/lib/private/serve/index.js +32 -2
  156. package/lib/private/server/TAG.d.ts +3 -0
  157. package/lib/private/server/TAG.js +2 -0
  158. package/lib/private/server/index.d.ts +5 -0
  159. package/lib/private/server/index.js +6 -0
  160. package/lib/private/session/SessionHandle.js +2 -1
  161. package/lib/private/session/index.d.ts +1 -1
  162. package/lib/private/session/module.d.ts +3 -0
  163. package/lib/private/session/module.js +114 -0
  164. package/lib/private/session/schema.d.ts +17 -9
  165. package/lib/private/session/schema.js +9 -5
  166. package/lib/private/target/Manager.js +6 -12
  167. package/lib/public/client.d.ts +2 -13
  168. package/lib/public/client.js +1 -9
  169. package/lib/public/db/errors.d.ts +2 -0
  170. package/lib/public/db/errors.js +2 -0
  171. package/lib/public/db/migrate.d.ts +2 -0
  172. package/lib/public/db/migrate.js +2 -0
  173. package/lib/public/db.d.ts +3 -3
  174. package/lib/public/frontend.d.ts +3 -0
  175. package/lib/public/frontend.js +2 -0
  176. package/lib/public/index.d.ts +2 -0
  177. package/lib/public/index.js +2 -0
  178. package/lib/public/orm/store.d.ts +2 -0
  179. package/lib/public/orm/store.js +2 -0
  180. package/lib/public/request/server.d.ts +5 -0
  181. package/lib/public/request/server.js +7 -0
  182. package/lib/public/response.d.ts +4 -4
  183. package/lib/public/server.d.ts +3 -0
  184. package/lib/public/server.js +2 -0
  185. package/package.json +30 -25
  186. package/lib/private/AppError.d.ts +0 -4
  187. package/lib/private/AppError.js +0 -8
  188. package/lib/private/backend/Module.d.ts +0 -18
  189. package/lib/private/backend/Module.js +0 -19
  190. package/lib/private/backend/TAG.d.ts +0 -3
  191. package/lib/private/backend/TAG.js +0 -2
  192. package/lib/private/build/server/plugin/db-default.d.ts +0 -4
  193. package/lib/private/build/server/plugin/db-default.js +0 -45
  194. package/lib/private/build/server/plugin/store-wrap.d.ts +0 -4
  195. package/lib/private/build/server/plugin/store-wrap.js +0 -33
  196. package/lib/private/client/spa/index.d.ts +0 -6
  197. package/lib/private/client/spa/index.js +0 -200
  198. package/lib/private/db/error.d.ts +0 -81
  199. package/lib/private/db/error.js +0 -199
  200. package/lib/private/db/symbol/wrap.d.ts +0 -3
  201. package/lib/private/db/symbol/wrap.js +0 -2
  202. package/lib/private/fail.d.ts +0 -3
  203. package/lib/private/fail.js +0 -5
  204. package/lib/private/frontend/Module.d.ts +0 -62
  205. package/lib/private/frontend/Module.js +0 -255
  206. package/lib/private/i18n/Module.d.ts +0 -16
  207. package/lib/private/i18n/Module.js +0 -133
  208. package/lib/private/module/BuildHook.d.ts +0 -5
  209. package/lib/private/module/BuildHook.js +0 -2
  210. package/lib/private/module/NextBuild.d.ts +0 -5
  211. package/lib/private/module/NextBuild.js +0 -2
  212. package/lib/private/module/NextServe.d.ts +0 -5
  213. package/lib/private/module/NextServe.js +0 -2
  214. package/lib/private/orm/Set.d.ts +0 -11
  215. package/lib/private/orm/Set.js +0 -2
  216. package/lib/private/orm/foreign.d.ts +0 -4
  217. package/lib/private/orm/foreign.js +0 -5
  218. package/lib/private/orm/primary.d.ts +0 -5
  219. package/lib/private/orm/primary.js +0 -5
  220. package/lib/private/orm/types.d.ts +0 -18
  221. package/lib/private/orm/types.js +0 -2
  222. package/lib/private/orm/wrap.d.ts +0 -5
  223. package/lib/private/orm/wrap.js +0 -5
  224. package/lib/private/reducer.d.ts +0 -24
  225. package/lib/private/reducer.js +0 -10
  226. package/lib/private/serve/module/Dev.d.ts +0 -11
  227. package/lib/private/serve/module/Dev.js +0 -32
  228. package/lib/private/serve/module/Handle.d.ts +0 -10
  229. package/lib/private/serve/module/Handle.js +0 -15
  230. package/lib/private/session/SessionModule.d.ts +0 -14
  231. package/lib/private/session/SessionModule.js +0 -122
  232. package/lib/public/App.d.ts +0 -2
  233. package/lib/public/App.js +0 -2
  234. package/lib/public/AppError.d.ts +0 -2
  235. package/lib/public/AppError.js +0 -2
  236. package/lib/public/BuildApp.d.ts +0 -2
  237. package/lib/public/BuildApp.js +0 -2
  238. package/lib/public/BuildHook.d.ts +0 -2
  239. package/lib/public/BuildHook.js +0 -2
  240. package/lib/public/Mode.d.ts +0 -2
  241. package/lib/public/Mode.js +0 -2
  242. package/lib/public/Module.d.ts +0 -2
  243. package/lib/public/Module.js +0 -2
  244. package/lib/public/Next.d.ts +0 -2
  245. package/lib/public/Next.js +0 -2
  246. package/lib/public/NextBuild.d.ts +0 -2
  247. package/lib/public/NextBuild.js +0 -2
  248. package/lib/public/NextHandle.d.ts +0 -2
  249. package/lib/public/NextHandle.js +0 -2
  250. package/lib/public/NextRoute.d.ts +0 -3
  251. package/lib/public/NextRoute.js +0 -2
  252. package/lib/public/NextServe.d.ts +0 -2
  253. package/lib/public/NextServe.js +0 -2
  254. package/lib/public/ServeApp.d.ts +0 -2
  255. package/lib/public/ServeApp.js +0 -2
  256. package/lib/public/Target.d.ts +0 -2
  257. package/lib/public/Target.js +0 -2
  258. package/lib/public/backend/Module.d.ts +0 -2
  259. package/lib/public/backend/Module.js +0 -2
  260. package/lib/public/backend/TAG.d.ts +0 -2
  261. package/lib/public/backend/TAG.js +0 -2
  262. package/lib/public/client/Data.d.ts +0 -2
  263. package/lib/public/client/Data.js +0 -2
  264. package/lib/public/client/spa.d.ts +0 -2
  265. package/lib/public/client/spa.js +0 -2
  266. package/lib/public/db/error.d.ts +0 -2
  267. package/lib/public/db/error.js +0 -2
  268. package/lib/public/fail.d.ts +0 -2
  269. package/lib/public/fail.js +0 -2
  270. package/lib/public/frontend/Module.d.ts +0 -2
  271. package/lib/public/frontend/Module.js +0 -2
  272. package/lib/public/frontend/Publish.d.ts +0 -2
  273. package/lib/public/frontend/Publish.js +0 -2
  274. package/lib/public/frontend/Render.d.ts +0 -2
  275. package/lib/public/frontend/Render.js +0 -2
  276. package/lib/public/frontend/ViewResponse.d.ts +0 -2
  277. package/lib/public/frontend/ViewResponse.js +0 -2
  278. package/lib/public/orm/Store.d.ts +0 -2
  279. package/lib/public/orm/Store.js +0 -2
  280. package/lib/public/orm/wrap.d.ts +0 -2
  281. package/lib/public/orm/wrap.js +0 -2
  282. package/lib/public/request.d.ts +0 -4
  283. package/lib/public/request.js +0 -2
  284. /package/lib/private/{frontend → client}/Publish.d.ts +0 -0
  285. /package/lib/private/{frontend → client}/Publish.js +0 -0
  286. /package/lib/private/{frontend → client}/Render.js +0 -0
  287. /package/lib/private/{frontend → client}/ServerData.d.ts +0 -0
  288. /package/lib/private/{frontend → client}/ServerData.js +0 -0
  289. /package/lib/private/{frontend → client}/ServerView.d.ts +0 -0
  290. /package/lib/private/{frontend → client}/ServerView.js +0 -0
  291. /package/lib/private/{frontend → client}/View.d.ts +0 -0
  292. /package/lib/private/{frontend → client}/View.js +0 -0
  293. /package/lib/private/{frontend → client}/ViewOptions.d.ts +0 -0
  294. /package/lib/private/{frontend → client}/ViewOptions.js +0 -0
  295. /package/lib/private/{frontend → client}/ViewResponse.js +0 -0
  296. /package/lib/private/client/{spa/storage.d.ts → storage.d.ts} +0 -0
  297. /package/lib/private/client/{spa/storage.js → storage.js} +0 -0
@@ -1,5 +1,4 @@
1
- import E from "#db/error";
2
- import wrap from "#db/symbol/wrap";
1
+ import E from "#db/errors";
3
2
  import parse from "#orm/parse";
4
3
  import assert from "@rcompat/assert";
5
4
  import is from "@rcompat/is";
@@ -64,35 +63,55 @@ const DATE_OPS = ["$before", "$after", "$ne"];
64
63
  * document database table/collection. It pairs a Pema schema with a uniform
65
64
  * CRUD/query API.
66
65
  */
67
- export default class Store {
66
+ export class Store {
68
67
  #schema;
69
68
  #type;
70
69
  #types;
71
70
  #nullables;
72
- #db;
73
71
  #name;
72
+ #db;
74
73
  #pk;
75
74
  #generate_pk;
76
75
  #fks;
77
76
  #relations;
78
- constructor(input, config = {}) {
79
- const { pk, generate_pk, fks, schema } = parse(input);
77
+ #migrate;
78
+ constructor(init) {
79
+ const { pk, generate_pk, fks, schema } = parse(init.schema);
80
+ const { name, db, migrate } = init;
81
+ if (name === undefined)
82
+ throw E.store_name_required();
83
+ assert.string(name);
84
+ if (!VALID_IDENTIFIER.test(name))
85
+ throw E.identifier_invalid(name);
86
+ assert.defined(db, E.db_missing);
87
+ assert.dict(schema);
88
+ assert.maybe.boolean(migrate);
89
+ assert.maybe.dict(init.extend);
80
90
  this.#schema = schema;
81
91
  this.#type = new StoreType(schema, pk);
82
92
  this.#types = Object.fromEntries(Object.entries(schema).map(([key, value]) => [key, value.datatype]));
83
- this.#name = config.name;
84
- this.#db = config.db;
93
+ this.#name = name;
94
+ this.#db = db;
85
95
  this.#pk = pk;
86
96
  this.#generate_pk = generate_pk;
87
97
  this.#fks = fks;
88
- this.#relations = config.relations ?? {};
98
+ this.#relations = init.relations ?? {};
99
+ this.#migrate = migrate ?? true;
100
+ for (const relation of Object.keys(this.#relations)) {
101
+ if (relation in schema)
102
+ throw E.relation_conflicts_with_field(relation);
103
+ }
89
104
  this.#nullables = new Set(Object.entries(this.#type.properties)
90
105
  .filter(([, v]) => v.nullable)
91
106
  .map(([k]) => k));
92
- registry.set(input, this);
93
- }
94
- static new(input, config = {}) {
95
- return new Store(input, config);
107
+ if (init.extend !== undefined) {
108
+ for (const [k, v] of Object.entries(init.extend)) {
109
+ if (k in this)
110
+ throw E.key_duplicate(k);
111
+ this[k] = v;
112
+ }
113
+ }
114
+ registry.set(init.schema, this);
96
115
  }
97
116
  get #as() {
98
117
  return {
@@ -102,14 +121,15 @@ export default class Store {
102
121
  types: this.#types,
103
122
  };
104
123
  }
105
- get collection() {
124
+ get table() {
106
125
  const db = this.db;
107
- const name = this.name;
108
- const schema = this.#schema;
109
- const as = this.#as;
126
+ const pk = {
127
+ name: this.#pk,
128
+ generate: this.#generate_pk,
129
+ };
110
130
  return {
111
- create: () => db.schema.create(as, schema),
112
- delete: () => db.schema.delete(name),
131
+ create: () => db.schema.create(this.#name, pk, this.#types),
132
+ delete: () => db.schema.delete(this.#name),
113
133
  };
114
134
  }
115
135
  get infer() {
@@ -121,26 +141,32 @@ export default class Store {
121
141
  get type() {
122
142
  return this.#type;
123
143
  }
124
- [wrap](name, db) {
125
- this.#db ??= db;
126
- this.#name ??= name;
127
- return this;
144
+ get migrate() {
145
+ return this.#migrate;
146
+ }
147
+ get name() {
148
+ return this.#name;
149
+ }
150
+ get pk() {
151
+ return this.#pk;
128
152
  }
129
153
  get db() {
130
- if (this.#db === undefined)
131
- throw E.db_missing();
132
154
  return this.#db;
133
155
  }
134
156
  get types() {
135
157
  return this.#types;
136
158
  }
137
- get name() {
138
- const name = this.#name;
139
- if (name === undefined)
140
- throw E.store_name_required();
141
- if (!VALID_IDENTIFIER.test(name))
142
- throw E.identifier_invalid(name);
143
- return name;
159
+ #parse_pk(pkv) {
160
+ const pk = this.#pk;
161
+ if (pk === null)
162
+ throw E.pk_undefined(this.name);
163
+ try {
164
+ this.#schema[pk].parse(pkv);
165
+ }
166
+ catch {
167
+ throw E.pk_invalid(pkv);
168
+ }
169
+ return pk;
144
170
  }
145
171
  #parse_query(query, types) {
146
172
  const { where, select, sort, limit } = query;
@@ -294,6 +320,12 @@ export default class Store {
294
320
  if (!is.blob(value))
295
321
  throw E.wrong_type("blob", k, value);
296
322
  continue;
323
+ case "uuid":
324
+ case "uuid_v4":
325
+ case "uuid_v7":
326
+ if (!is.string(value))
327
+ throw E.wrong_type("string", k, value);
328
+ continue;
297
329
  default:
298
330
  throw E.where_invalid_value(k, value);
299
331
  }
@@ -360,14 +392,11 @@ export default class Store {
360
392
  * Check whether a record with the given primary key exists.
361
393
  */
362
394
  async has(pkv) {
363
- if (this.#pk === null)
364
- throw E.pk_undefined(this.name);
365
- return (await this.count({ where: { [this.#pk]: pkv } })) === 1;
395
+ const pk = this.#parse_pk(pkv);
396
+ return (await this.count({ where: { [pk]: pkv } })) === 1;
366
397
  }
367
398
  async get(pkv, options) {
368
- const pk = this.#pk;
369
- if (pk === null)
370
- throw E.pk_undefined(this.name);
399
+ const pk = this.#parse_pk(pkv);
371
400
  guard_options(options, ["select", "with"]);
372
401
  this.#parse_query(options ?? {}, this.#types);
373
402
  const $with = this.#with(options?.with);
@@ -376,8 +405,9 @@ export default class Store {
376
405
  fields: options?.select ? [...options.select] : undefined,
377
406
  with: $with,
378
407
  });
379
- assert.true(records.length <= 1);
380
- if (records.length === 0) {
408
+ const n = records.length;
409
+ assert.true(n <= 1, E.record_number_invalid(n, "get"));
410
+ if (n === 0) {
381
411
  const err = E.record_not_found(pk, pkv);
382
412
  err.not_found = true;
383
413
  throw err;
@@ -442,29 +472,20 @@ export default class Store {
442
472
  ...this.#type.partial().parse(to_parse),
443
473
  ...nulls,
444
474
  };
445
- if (by_pk) {
475
+ if (by_pk)
446
476
  return this.#update_1(arg0, parsed);
447
- }
448
477
  const where = arg0.where;
449
478
  if (where !== undefined)
450
479
  this.#parse_where(where, this.#types);
451
480
  return this.#update_n((where ?? {}), parsed);
452
481
  }
453
482
  async #update_1(pkv, set) {
454
- const pk = this.#pk;
455
- if (pk === null)
456
- throw E.pk_undefined(this.name);
457
- const n = await this.db.update(this.#as, {
458
- set: set,
459
- where: { [pk]: pkv },
460
- });
461
- assert.true(n === 1, `${n} records updated instead of 1`);
483
+ const pk = this.#parse_pk(pkv);
484
+ const n = await this.db.update(this.#as, { set, where: { [pk]: pkv } });
485
+ assert.true(n === 1, E.record_number_invalid(n, "update"));
462
486
  }
463
487
  async #update_n(where, set) {
464
- return await this.db.update(this.#as, {
465
- set: set,
466
- where: where,
467
- });
488
+ return await this.db.update(this.#as, { set, where });
468
489
  }
469
490
  async delete(pkv_or_options) {
470
491
  if (is.dict(pkv_or_options)) {
@@ -477,11 +498,9 @@ export default class Store {
477
498
  return this.#delete_1(pkv_or_options);
478
499
  }
479
500
  async #delete_1(pkv) {
480
- const pk = this.#pk;
481
- if (pk === null)
482
- throw E.pk_undefined(this.name);
501
+ const pk = this.#parse_pk(pkv);
483
502
  const n = await this.db.delete(this.#as, { where: { [pk]: pkv } });
484
- assert.true(n === 1, `${n} records deleted instead of 1`);
503
+ assert.true(n === 1, E.record_number_invalid(n, "delete"));
485
504
  }
486
505
  async #delete_n(where) {
487
506
  return await this.db.delete(this.#as, { where: where });
@@ -495,7 +514,7 @@ export default class Store {
495
514
  this.#parse_query(options ?? {}, this.#types);
496
515
  const result = await this.db.read(this.#as, {
497
516
  where: options?.where ?? {},
498
- fields: options?.select ? [...options.select] : undefined,
517
+ fields: options?.select !== undefined ? [...options.select] : undefined,
499
518
  limit: options?.limit,
500
519
  sort: options?.sort,
501
520
  with: this.#with(options?.with),
@@ -506,13 +525,18 @@ export default class Store {
506
525
  return this.#type.toJSON();
507
526
  }
508
527
  extend(extensor) {
509
- const extensions = extensor(this);
510
- const keys = Object.keys(extensions);
511
- for (const k of keys)
512
- if (k in this)
513
- throw E.key_duplicate(k);
514
- Object.assign(this, extensions);
515
- return this;
528
+ return new Store({
529
+ name: this.name,
530
+ db: this.db,
531
+ schema: this.#schema,
532
+ relations: this.#relations,
533
+ migrate: false,
534
+ extend: extensor(this),
535
+ });
516
536
  }
517
537
  }
518
- //# sourceMappingURL=Store.js.map
538
+ function store(init) {
539
+ return new Store(init);
540
+ }
541
+ export default store;
542
+ //# sourceMappingURL=store.js.map
@@ -1,4 +1,4 @@
1
- import fail from "#fail";
1
+ import E from "#errors";
2
2
  import log from "#log";
3
3
  async function resolve(root, config_paths) {
4
4
  const tsconfig_path = root.join("tsconfig.json");
@@ -13,13 +13,13 @@ async function resolve(root, config_paths) {
13
13
  const config = JSON.parse(text);
14
14
  const ts_paths = config.compilerOptions?.paths ?? {};
15
15
  if (config_paths !== undefined && Object.keys(ts_paths).length > 0) {
16
- return fail("tsconfig.json exists with paths, remove config paths");
16
+ return E.config_tsconfig_has_paths();
17
17
  }
18
18
  // merge with defaults (user paths override)
19
19
  return { ...ts_paths };
20
20
  }
21
21
  catch {
22
- log.warn("Failed to parse tsconfig.json, falling back to config");
22
+ log.warn("failed to parse tsconfig.json, falling back to config");
23
23
  }
24
24
  }
25
25
  if (config_paths !== undefined)
@@ -4,6 +4,9 @@ type Normalize = (key: string) => string;
4
4
  interface Schema<T> {
5
5
  parse(input: unknown): T;
6
6
  }
7
+ interface CoercibleSchema<T> extends Schema<T> {
8
+ coerce(input: unknown): T;
9
+ }
7
10
  type Options = {
8
11
  normalize?: Normalize;
9
12
  raw?: string;
@@ -67,6 +70,7 @@ export default class RequestBag {
67
70
  * @throws Whatever the schema throws.
68
71
  */
69
72
  parse<T>(schema: Schema<T>): T;
73
+ coerce<T>(schema: CoercibleSchema<T>): T;
70
74
  /** Returns {@link raw}. Useful in template strings. */
71
75
  toString(): string;
72
76
  /**
@@ -1,4 +1,4 @@
1
- import fail from "#fail";
1
+ import E from "#errors";
2
2
  import assert from "@rcompat/assert";
3
3
  import fn from "@rcompat/fn";
4
4
  export default class RequestBag {
@@ -72,7 +72,7 @@ export default class RequestBag {
72
72
  if (v !== undefined)
73
73
  return v;
74
74
  }
75
- throw fail("{0} has no key {1}", this.#name, key);
75
+ throw E.request_bag_missing_key(this.#name, key);
76
76
  }
77
77
  /**
78
78
  * Try to get a value by key.
@@ -107,6 +107,9 @@ export default class RequestBag {
107
107
  parse(schema) {
108
108
  return schema.parse(this.#contents);
109
109
  }
110
+ coerce(schema) {
111
+ return schema.coerce(this.#contents);
112
+ }
110
113
  /** Returns {@link raw}. Useful in template strings. */
111
114
  toString() {
112
115
  return this.raw;
@@ -1,4 +1,4 @@
1
- import type { Dict, JSONValue, Schema } from "@rcompat/type";
1
+ import type { Dict, JSONValue } from "@rcompat/type";
2
2
  type Form = Dict<string>;
3
3
  type Parsed = {
4
4
  type: "binary";
@@ -16,19 +16,16 @@ type Parsed = {
16
16
  type: "text";
17
17
  value: string;
18
18
  };
19
- type ParseReturn<S> = S extends {
20
- parse: (v: unknown) => infer R;
21
- } ? R : never;
19
+ declare function parse(request: Request, url: URL): Promise<RequestBody>;
20
+ declare function none(): RequestBody;
22
21
  export default class RequestBody {
23
22
  #private;
24
- static parse(request: Request, url: URL): Promise<RequestBody>;
25
- static none(): RequestBody;
23
+ static parse: typeof parse;
24
+ static none: typeof none;
26
25
  constructor(parsed: Parsed, files?: Dict<File>);
27
- get type(): "binary" | "json" | "text" | "none" | "form";
26
+ get type(): "json" | "binary" | "text" | "none" | "form";
28
27
  json(): JSONValue;
29
- json<S extends Schema<unknown>>(schema: S): ParseReturn<S>;
30
28
  form(): Form;
31
- form<S extends Schema<unknown>>(schema: S): ParseReturn<S>;
32
29
  files(): Dict<File>;
33
30
  text(): string;
34
31
  binary(): Blob;
@@ -1,5 +1,5 @@
1
- import fail from "#fail";
2
- import MIME from "@rcompat/http/mime";
1
+ import E from "#errors";
2
+ import { MIME } from "@rcompat/http";
3
3
  import is from "@rcompat/is";
4
4
  async function anyform(request) {
5
5
  const form = Object.create(null);
@@ -15,40 +15,41 @@ async function anyform(request) {
15
15
  return { form, files };
16
16
  }
17
17
  ;
18
- export default class RequestBody {
19
- #parsed;
20
- #files;
21
- static async parse(request, url) {
22
- const raw = request.headers.get("content-type") ?? "none";
23
- const type = raw.split(";")[0].trim().toLowerCase();
24
- const path = url.pathname;
25
- try {
26
- switch (type) {
27
- case MIME.APPLICATION_OCTET_STREAM:
28
- return new RequestBody({ type: "binary", value: await request.blob() });
29
- case MIME.APPLICATION_X_WWW_FORM_URLENCODED:
30
- case MIME.MULTIPART_FORM_DATA: {
31
- const { form, files } = await anyform(request);
32
- return new RequestBody({ type: "form", value: form }, files);
33
- }
34
- case MIME.APPLICATION_JSON:
35
- return new RequestBody({ type: "json", value: await request.json() });
36
- case MIME.TEXT_PLAIN:
37
- return new RequestBody({ type: "text", value: await request.text() });
38
- case "none":
39
- return RequestBody.none();
40
- default:
41
- throw fail("{0}: unsupported content type {1}", path, type);
18
+ async function parse(request, url) {
19
+ const raw = request.headers.get("content-type") ?? "none";
20
+ const type = raw.split(";")[0].trim().toLowerCase();
21
+ const path = url.pathname;
22
+ try {
23
+ switch (type) {
24
+ case MIME.APPLICATION_OCTET_STREAM:
25
+ return new RequestBody({ type: "binary", value: await request.blob() });
26
+ case MIME.APPLICATION_X_WWW_FORM_URLENCODED:
27
+ case MIME.MULTIPART_FORM_DATA: {
28
+ const { form, files } = await anyform(request);
29
+ return new RequestBody({ type: "form", value: form }, files);
42
30
  }
43
- }
44
- catch (cause) {
45
- const message = "{0}: unparseable content type {1} - cause:\n[2]";
46
- throw fail(message, path, type, cause);
31
+ case MIME.APPLICATION_JSON:
32
+ return new RequestBody({ type: "json", value: await request.json() });
33
+ case MIME.TEXT_PLAIN:
34
+ return new RequestBody({ type: "text", value: await request.text() });
35
+ case "none":
36
+ return RequestBody.none();
37
+ default:
38
+ throw E.request_unsupported_mime(path, type);
47
39
  }
48
40
  }
49
- static none() {
50
- return new RequestBody({ type: "none", value: null });
41
+ catch (cause) {
42
+ throw E.request_unparsable_mime(path, type, cause);
51
43
  }
44
+ }
45
+ function none() {
46
+ return new RequestBody({ type: "none", value: null });
47
+ }
48
+ export default class RequestBody {
49
+ #parsed;
50
+ #files;
51
+ static parse = parse;
52
+ static none = none;
52
53
  constructor(parsed, files = {}) {
53
54
  this.#parsed = parsed;
54
55
  this.#files = files;
@@ -59,44 +60,38 @@ export default class RequestBody {
59
60
  #value() {
60
61
  return this.#parsed.value;
61
62
  }
62
- #throw(expected) {
63
- throw fail("request body: expected {0}, got {1}", expected, this.type);
63
+ #unexpected_body(expected) {
64
+ throw E.request_unexpected_body(expected, this.type);
64
65
  }
65
- json(schema) {
66
- if (this.type !== "json") {
67
- this.#throw("JSON");
68
- }
69
- const value = this.#value();
70
- return schema ? schema.parse(value) : value;
66
+ json() {
67
+ if (this.type !== "json")
68
+ this.#unexpected_body("JSON");
69
+ return this.#value();
71
70
  }
72
- form(schema) {
73
- if (this.type !== "form") {
74
- this.#throw("form");
75
- }
76
- const value = this.#value();
77
- return schema ? schema.parse(value) : value;
71
+ form() {
72
+ if (this.type !== "form")
73
+ this.#unexpected_body("form");
74
+ return this.#value();
78
75
  }
79
76
  files() {
80
- if (this.type !== "form") {
81
- this.#throw("form");
82
- }
77
+ if (this.type !== "form")
78
+ this.#unexpected_body("form");
83
79
  return this.#files;
84
80
  }
85
81
  text() {
86
- if (this.type !== "text") {
87
- this.#throw("plaintext");
88
- }
82
+ if (this.type !== "text")
83
+ this.#unexpected_body("plaintext");
89
84
  return this.#value();
90
85
  }
91
86
  binary() {
92
87
  if (this.type !== "binary") {
93
- this.#throw("binary");
88
+ this.#unexpected_body("binary");
94
89
  }
95
90
  return this.#value();
96
91
  }
97
92
  none() {
98
93
  if (this.type !== "none") {
99
- this.#throw("none");
94
+ this.#unexpected_body("none");
100
95
  }
101
96
  return null;
102
97
  }
@@ -1,15 +1,10 @@
1
1
  import type RequestBag from "#request/RequestBag";
2
2
  import type RequestBody from "#request/RequestBody";
3
+ import type RequestView from "#request/RequestView";
4
+ import type Verb from "#request/Verb";
3
5
  import type { Dict } from "@rcompat/type";
4
- type RequestView = {
5
- context: Dict;
6
- cookies: Dict<string>;
7
- headers: Dict<string>;
8
- path: Dict<string>;
9
- query: Dict<string>;
10
- url: URL;
11
- };
12
6
  type RequestFacade = {
7
+ method: Verb;
13
8
  body: RequestBody;
14
9
  cookies: RequestBag;
15
10
  forward(to: string, headers?: Dict<string>): Promise<Response>;
@@ -0,0 +1,9 @@
1
+ import type { Dict } from "@rcompat/type";
2
+ type RequestPublic = {
3
+ cookies: Dict<string>;
4
+ headers: Dict<string>;
5
+ query: Dict<string>;
6
+ url: URL;
7
+ };
8
+ export type { RequestPublic as default };
9
+ //# sourceMappingURL=RequestPublic.d.ts.map
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=RequestPublic.js.map
@@ -0,0 +1,11 @@
1
+ import type { Dict } from "@rcompat/type";
2
+ type RequestView = {
3
+ context: Dict;
4
+ cookies: Dict<string>;
5
+ headers: Dict<string>;
6
+ path: Dict<string>;
7
+ query: Dict<string>;
8
+ url: URL;
9
+ };
10
+ export type { RequestView as default };
11
+ //# sourceMappingURL=RequestView.d.ts.map
@@ -0,0 +1,3 @@
1
+ ;
2
+ export {};
3
+ //# sourceMappingURL=RequestView.js.map
@@ -0,0 +1,4 @@
1
+ import type RequestFacade from "#request/RequestFacade";
2
+ import type ServeApp from "#serve/App";
3
+ export default function handle(app: ServeApp, request: RequestFacade): Promise<Response>;
4
+ //# sourceMappingURL=handle.d.ts.map
@@ -0,0 +1,18 @@
1
+ import { Status } from "@rcompat/http";
2
+ async function run(hooks, request) {
3
+ const [first, ...rest] = hooks;
4
+ if (first === undefined) {
5
+ return new Response(null, {
6
+ headers: {
7
+ "Content-Length": String(0),
8
+ "Cache-Control": "no-cache",
9
+ },
10
+ status: Status.INTERNAL_SERVER_ERROR,
11
+ });
12
+ }
13
+ return await first(request, next => run(rest, next));
14
+ }
15
+ export default function handle(app, request) {
16
+ return run(app.handle_hooks, request);
17
+ }
18
+ //# sourceMappingURL=handle.js.map
@@ -48,6 +48,7 @@ function parse(request) {
48
48
  const url = new URL(request.url);
49
49
  const facade = {
50
50
  body: RequestBody.none(),
51
+ method: request.method.toLowerCase(),
51
52
  cookies: cookie_bag(request),
52
53
  forward(to, headers) {
53
54
  return fetch(to, {
@@ -1,9 +1,9 @@
1
- import fail from "#fail";
1
+ import E from "#errors";
2
2
  import log from "#log";
3
3
  import response_error from "#response/error";
4
4
  import response_json from "#response/json";
5
5
  import respond from "#response/respond";
6
- import Status from "@rcompat/http/Status";
6
+ import { Status } from "@rcompat/http";
7
7
  import ParseError from "pema/ParseError";
8
8
  const result = (request, response) => ({ request, response });
9
9
  function wrap_hook(h) {
@@ -12,17 +12,16 @@ function wrap_hook(h) {
12
12
  let called_next = false;
13
13
  const response = await h(request, async (r) => {
14
14
  if (called_next)
15
- throw fail("hook called next() more than once");
15
+ throw E.hook_reused_next();
16
16
  called_next = true;
17
17
  downstream = await next(r);
18
18
  return downstream.response;
19
19
  });
20
20
  // hook returned nothing
21
21
  if (response === undefined) {
22
- if (called_next) {
23
- throw fail("hook called next() but did not return a result; did you forget `return next(request)`?");
24
- }
25
- throw fail("hook must return a response-like value or return next(request)");
22
+ if (called_next)
23
+ throw E.hook_no_return();
24
+ throw E.hook_bad_return();
26
25
  }
27
26
  // if the hook called next(), we preserve the downstream request (mutations) but
28
27
  // allow the hook to override the response by returning something else.
@@ -48,10 +47,10 @@ export default async function (app, partial_request) {
48
47
  }
49
48
  const { errors, hooks, layouts, handler } = route;
50
49
  errorRoute = errors[0];
51
- const module_hooks = app.modules.map(m => wrap_hook(m.route.bind(m)));
50
+ const internal_hooks = app.route_hooks.map(wrap_hook);
52
51
  const route_hooks = hooks.map(wrap_hook);
53
52
  const last = async (req, _next) => result(req, await handler(req));
54
- const { request, response } = await run([...module_hooks, ...route_hooks, last], route.request);
53
+ const { request, response } = await run([...internal_hooks, ...route_hooks, last], route.request);
55
54
  return await respond(response)(app, {
56
55
  layouts: await Promise.all(layouts.map(layout => layout(request))),
57
56
  }, request);