@primate/core 0.6.3 → 0.7.1

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 (187) hide show
  1. package/lib/private/App.d.ts +76 -149
  2. package/lib/private/App.js +22 -6
  3. package/lib/private/Flags.d.ts +5 -3
  4. package/lib/private/Flags.js +4 -2
  5. package/lib/private/app/Facade.d.ts +77 -156
  6. package/lib/private/app/Facade.js +4 -1
  7. package/lib/private/build/App.d.ts +4 -2
  8. package/lib/private/build/App.js +13 -4
  9. package/lib/private/build/client/index.js +24 -10
  10. package/lib/private/build/client/plugin/routes.d.ts +4 -0
  11. package/lib/private/build/client/plugin/routes.js +77 -0
  12. package/lib/private/build/hook.js +14 -6
  13. package/lib/private/build/index.d.ts +3 -2
  14. package/lib/private/build/index.js +9 -15
  15. package/lib/private/build/preclient/index.d.ts +3 -0
  16. package/lib/private/build/preclient/index.js +69 -0
  17. package/lib/private/build/preclient/plugin/routes.d.ts +4 -0
  18. package/lib/private/build/preclient/plugin/routes.js +44 -0
  19. package/lib/private/build/server/index.js +8 -5
  20. package/lib/private/build/server/plugin/assets.js +3 -3
  21. package/lib/private/build/server/plugin/native-addons.js +6 -8
  22. package/lib/private/build/server/plugin/node-imports.js +5 -7
  23. package/lib/private/build/server/plugin/route-client.d.ts +4 -0
  24. package/lib/private/build/server/plugin/route-client.js +111 -0
  25. package/lib/private/build/server/plugin/route.js +3 -10
  26. package/lib/private/build/server/plugin/virtual-pages.js +3 -3
  27. package/lib/private/build/server/plugin/virtual-routes.d.ts +2 -1
  28. package/lib/private/build/server/plugin/virtual-routes.js +27 -8
  29. package/lib/private/build/server/plugin/wasm.js +3 -2
  30. package/lib/private/build/shared/intercept.d.ts +4 -0
  31. package/lib/private/build/shared/intercept.js +29 -0
  32. package/lib/private/client/Data.d.ts +1 -1
  33. package/lib/private/client/boot.js +2 -2
  34. package/lib/private/client/create-form.d.ts +11 -1
  35. package/lib/private/client/create-form.js +21 -3
  36. package/lib/private/client/index.d.ts +2 -2
  37. package/lib/private/client/navigate.d.ts +1 -0
  38. package/lib/private/client/navigate.js +7 -6
  39. package/lib/private/client/submit.d.ts +2 -1
  40. package/lib/private/client/submit.js +8 -7
  41. package/lib/private/client/{http.d.ts → transport.d.ts} +3 -3
  42. package/lib/private/client/{http.js → transport.js} +7 -9
  43. package/lib/private/config/schema.d.ts +5 -13
  44. package/lib/private/config/schema.js +1 -5
  45. package/lib/private/db/DB.d.ts +3 -1
  46. package/lib/private/db/MemoryDB.d.ts +5 -2
  47. package/lib/private/db/MemoryDB.js +33 -19
  48. package/lib/private/db/SQLDB.d.ts +23 -0
  49. package/lib/private/db/SQLDB.js +2 -0
  50. package/lib/private/db/errors.d.ts +74 -7
  51. package/lib/private/db/errors.js +31 -7
  52. package/lib/private/db/migrate/apply.js +8 -9
  53. package/lib/private/db/migrate/bundle.js +2 -2
  54. package/lib/private/db/migrate/create.js +18 -20
  55. package/lib/private/db/migrate/status.js +9 -10
  56. package/lib/private/db/migrate/store.d.ts +3 -3
  57. package/lib/private/db/migrate/store.js +5 -5
  58. package/lib/private/db/test.js +256 -115
  59. package/lib/private/db/testSQL.d.ts +50 -0
  60. package/lib/private/db/testSQL.js +196 -0
  61. package/lib/private/errors.d.ts +66 -9
  62. package/lib/private/errors.js +37 -16
  63. package/lib/private/frontend.d.ts +4 -4
  64. package/lib/private/frontend.js +11 -8
  65. package/lib/private/i18n/errors.d.ts +7 -1
  66. package/lib/private/i18n/errors.js +1 -1
  67. package/lib/private/i18n/index/types.d.ts +1 -1
  68. package/lib/private/i18n/locale.d.ts +1 -1
  69. package/lib/private/i18n/module.js +6 -5
  70. package/lib/private/index.d.ts +10 -2
  71. package/lib/private/index.js +13 -1
  72. package/lib/private/logger.d.ts +24 -0
  73. package/lib/private/logger.js +66 -0
  74. package/lib/private/module/Setup.d.ts +3 -0
  75. package/lib/private/module/create.d.ts +2 -1
  76. package/lib/private/module/create.js +4 -0
  77. package/lib/private/paths.d.ts +2 -3
  78. package/lib/private/paths.js +6 -12
  79. package/lib/private/request/ContentType.d.ts +3 -0
  80. package/lib/private/request/ContentType.js +2 -0
  81. package/lib/private/request/RequestBag.d.ts +2 -18
  82. package/lib/private/request/RequestBag.js +4 -16
  83. package/lib/private/request/RequestBody.d.ts +20 -28
  84. package/lib/private/request/RequestBody.js +63 -86
  85. package/lib/private/request/RequestFacade.d.ts +2 -2
  86. package/lib/private/request/handle.js +2 -2
  87. package/lib/private/request/parse.js +2 -4
  88. package/lib/private/request/route.js +15 -8
  89. package/lib/private/response/binary.d.ts +2 -2
  90. package/lib/private/response/binary.js +6 -6
  91. package/lib/private/response/error.js +6 -2
  92. package/lib/private/response/json.js +2 -2
  93. package/lib/private/response/null.d.ts +3 -0
  94. package/lib/private/response/null.js +6 -0
  95. package/lib/private/response/redirect.js +4 -3
  96. package/lib/private/response/respond.js +4 -4
  97. package/lib/private/response/sse.js +2 -2
  98. package/lib/private/response/text.js +2 -2
  99. package/lib/private/route/ContentTypeMap.d.ts +10 -0
  100. package/lib/private/route/ContentTypeMap.js +2 -0
  101. package/lib/private/route/Handler.d.ts +3 -2
  102. package/lib/private/route/NarrowedRequest.d.ts +23 -0
  103. package/lib/private/route/NarrowedRequest.js +2 -0
  104. package/lib/private/route/Options.d.ts +6 -1
  105. package/lib/private/route/Path.d.ts +2 -2
  106. package/lib/private/route/hook.d.ts +3 -1
  107. package/lib/private/route/hook.js +1 -2
  108. package/lib/private/route/router.d.ts +7 -11
  109. package/lib/private/route/router.js +13 -20
  110. package/lib/private/route.client.d.ts +36 -0
  111. package/lib/private/route.client.js +8 -0
  112. package/lib/private/route.d.ts +21 -5
  113. package/lib/private/route.js +21 -5
  114. package/lib/private/serve/App.d.ts +1 -2
  115. package/lib/private/serve/App.js +64 -58
  116. package/lib/private/serve/Init.d.ts +2 -0
  117. package/lib/private/serve/dev-module.js +2 -3
  118. package/lib/private/serve/index.js +5 -6
  119. package/lib/private/server/TAG.d.ts +1 -1
  120. package/lib/private/server/TAG.js +1 -1
  121. package/lib/private/session/index.d.ts +1 -1
  122. package/lib/private/session/index.js +3 -2
  123. package/lib/private/session/module.js +3 -3
  124. package/lib/private/session/schema.d.ts +3 -3
  125. package/lib/private/session/schema.js +4 -3
  126. package/lib/private/store/ExtractRelation.d.ts +7 -0
  127. package/lib/private/store/ExtractRelation.js +2 -0
  128. package/lib/private/store/ExtractSchema.d.ts +10 -0
  129. package/lib/private/{orm → store}/ForeignKey.d.ts +1 -1
  130. package/lib/private/store/Init.d.ts +13 -0
  131. package/lib/private/store/Init.js +2 -0
  132. package/lib/private/{orm/store.d.ts → store/Store.d.ts} +50 -50
  133. package/lib/private/{orm/store.js → store/Store.js} +163 -107
  134. package/lib/private/store/StoreInput.d.ts +11 -0
  135. package/lib/private/store/key.d.ts +8 -0
  136. package/lib/private/store/key.js +8 -0
  137. package/lib/private/{orm → store}/parse.d.ts +3 -3
  138. package/lib/private/{orm → store}/parse.js +7 -2
  139. package/lib/private/store/relation.d.ts +29 -0
  140. package/lib/private/store/relation.js +26 -0
  141. package/lib/private/store.d.ts +24 -0
  142. package/lib/private/store.js +10 -0
  143. package/lib/public/db/errors.d.ts +1 -1
  144. package/lib/public/db/errors.js +1 -1
  145. package/lib/public/db/testSQL.d.ts +2 -0
  146. package/lib/public/db/testSQL.js +2 -0
  147. package/lib/public/db.d.ts +1 -0
  148. package/lib/public/index.d.ts +1 -0
  149. package/lib/public/index.js +1 -1
  150. package/lib/public/response.d.ts +6 -6
  151. package/lib/public/response.js +4 -1
  152. package/lib/public/route.client.d.ts +2 -0
  153. package/lib/public/route.client.js +2 -0
  154. package/lib/public/store.d.ts +2 -0
  155. package/lib/public/store.js +2 -0
  156. package/package.json +24 -17
  157. package/lib/private/bye.d.ts +0 -3
  158. package/lib/private/bye.js +0 -4
  159. package/lib/private/log.d.ts +0 -20
  160. package/lib/private/log.js +0 -47
  161. package/lib/private/orm/ExtractSchema.d.ts +0 -9
  162. package/lib/private/orm/StoreInput.d.ts +0 -10
  163. package/lib/private/orm/key.d.ts +0 -8
  164. package/lib/private/orm/key.js +0 -8
  165. package/lib/private/orm/relation.d.ts +0 -43
  166. package/lib/private/orm/relation.js +0 -26
  167. package/lib/private/request/Verb.d.ts +0 -4
  168. package/lib/private/request/Verb.js +0 -2
  169. package/lib/private/request/verbs.d.ts +0 -3
  170. package/lib/private/request/verbs.js +0 -12
  171. package/lib/private/route/wrap.d.ts +0 -2
  172. package/lib/private/route/wrap.js +0 -12
  173. package/lib/public/log.d.ts +0 -2
  174. package/lib/public/log.js +0 -2
  175. package/lib/public/orm/key.d.ts +0 -2
  176. package/lib/public/orm/key.js +0 -2
  177. package/lib/public/orm/relation.d.ts +0 -2
  178. package/lib/public/orm/relation.js +0 -2
  179. package/lib/public/orm/store.d.ts +0 -2
  180. package/lib/public/orm/store.js +0 -2
  181. package/lib/public/request/verbs.d.ts +0 -2
  182. package/lib/public/request/verbs.js +0 -2
  183. /package/lib/private/{orm → store}/ExtractSchema.js +0 -0
  184. /package/lib/private/{orm → store}/ForeignKey.js +0 -0
  185. /package/lib/private/{orm → store}/PrimaryKey.d.ts +0 -0
  186. /package/lib/private/{orm → store}/PrimaryKey.js +0 -0
  187. /package/lib/private/{orm → store}/StoreInput.js +0 -0
@@ -0,0 +1,50 @@
1
+ import type SQLDB from "#db/SQLDB";
2
+ import test from "@rcompat/test";
3
+ declare const users: {
4
+ table: string;
5
+ pk: string;
6
+ types: {
7
+ id: "u32";
8
+ name: "string";
9
+ age: "u8";
10
+ };
11
+ };
12
+ declare const posts: {
13
+ table: string;
14
+ pk: string;
15
+ types: {
16
+ id: "u32";
17
+ user_id: "u32";
18
+ title: "string";
19
+ };
20
+ };
21
+ declare const transactions: {
22
+ table: string;
23
+ pk: string;
24
+ types: {
25
+ id: "u64";
26
+ amount: "u128";
27
+ memo: "string";
28
+ };
29
+ };
30
+ declare const json: {
31
+ table: string;
32
+ pk: string;
33
+ types: {
34
+ readonly id: "string";
35
+ readonly data: "json";
36
+ };
37
+ };
38
+ type Setup = {
39
+ $: (body: () => Promise<void>) => Promise<void>;
40
+ users: typeof users;
41
+ posts: typeof posts;
42
+ transactions: typeof transactions;
43
+ json: typeof json;
44
+ test: typeof test;
45
+ query_equals: (name: string, work: Work, expected: string) => void;
46
+ };
47
+ type Work = () => Promise<unknown>;
48
+ export default function testSQL(db: SQLDB, cb: (setup: Setup) => void): void;
49
+ export {};
50
+ //# sourceMappingURL=testSQL.d.ts.map
@@ -0,0 +1,196 @@
1
+ import { Code } from "#db/errors";
2
+ import test from "@rcompat/test";
3
+ import p from "pema";
4
+ const dt = {
5
+ u8: p.u8.datatype,
6
+ u32: p.u32.datatype,
7
+ u64: p.u64.datatype,
8
+ u128: p.u128.datatype,
9
+ string: p.string.datatype,
10
+ };
11
+ const users = {
12
+ table: "users",
13
+ pk: "id",
14
+ types: { id: dt.u32, name: dt.string, age: dt.u8 },
15
+ };
16
+ const posts = {
17
+ table: "posts",
18
+ pk: "id",
19
+ types: {
20
+ id: dt.u32, user_id: dt.u32, title: dt.string,
21
+ },
22
+ };
23
+ const transactions = {
24
+ table: "transactions",
25
+ pk: "id",
26
+ types: {
27
+ id: dt.u64, amount: dt.u128, memo: dt.string,
28
+ },
29
+ };
30
+ const json = {
31
+ table: "json",
32
+ pk: "id",
33
+ types: { id: dt.string, data: "json" },
34
+ };
35
+ const pk_config = { name: "id", generate: true };
36
+ function normalize(sql) {
37
+ return sql.replace(/\s+/g, " ").trim();
38
+ }
39
+ export default function testSQL(db, cb) {
40
+ async function $(body) {
41
+ await db.schema.create("users", pk_config, users.types);
42
+ await db.schema.create("posts", pk_config, posts.types);
43
+ await db.schema.create("transactions", pk_config, transactions.types);
44
+ await db.schema.create("json", pk_config, json.types);
45
+ await body();
46
+ await db.schema.delete("users");
47
+ await db.schema.delete("posts");
48
+ await db.schema.delete("transactions");
49
+ await db.schema.delete("json");
50
+ }
51
+ function query_equals(name, work, expected) {
52
+ test.case(name, async (assert) => {
53
+ await $(async () => {
54
+ await work();
55
+ assert(normalize(db.explain.users.query)).equals(normalize(expected));
56
+ });
57
+ });
58
+ }
59
+ cb({
60
+ $,
61
+ users,
62
+ posts,
63
+ transactions,
64
+ json,
65
+ test,
66
+ query_equals,
67
+ });
68
+ test.ended(() => db.close());
69
+ test.group("sql", () => {
70
+ test.case("select with input and output", async (assert) => {
71
+ await $(async () => {
72
+ await db.create(users, { id: 1, name: "alice", age: 30 });
73
+ await db.create(users, { id: 2, name: "bob", age: 25 });
74
+ const findByAge = db.sql({
75
+ input: p({ age: p.u8 }),
76
+ query: "SELECT name FROM users WHERE age > :age",
77
+ output: p.array(p({ name: p.string })),
78
+ });
79
+ const results = await findByAge({ age: 24 });
80
+ assert(results.length).equals(2);
81
+ assert(results[0].name).equals("alice");
82
+ assert(results[1].name).equals("bob");
83
+ });
84
+ });
85
+ test.case("select with no input", async (assert) => {
86
+ await $(async () => {
87
+ await db.create(users, { id: 1, name: "alice", age: 30 });
88
+ await db.create(users, { id: 2, name: "bob", age: 25 });
89
+ const findAll = db.sql({
90
+ query: "SELECT name FROM users",
91
+ output: p.array(p({ name: p.string })),
92
+ });
93
+ const results = await findAll();
94
+ assert(results.length).equals(2);
95
+ });
96
+ });
97
+ test.case("insert with no output", async (assert) => {
98
+ await $(async () => {
99
+ const insert = db.sql({
100
+ input: p({ name: p.string, age: p.u8 }),
101
+ query: "INSERT INTO users (name, age) VALUES (:name, :age)",
102
+ });
103
+ await insert({ name: "charlie", age: 20 });
104
+ const rows = await db.read(users, { where: { name: "charlie" } });
105
+ assert(rows.length).equals(1);
106
+ assert(rows[0].name).equals("charlie");
107
+ });
108
+ });
109
+ test.case("update with no output", async (assert) => {
110
+ await $(async () => {
111
+ await db.create(users, { id: 1, name: "alice", age: 30 });
112
+ const updateAge = db.sql({
113
+ input: p({ age: p.u8, name: p.string }),
114
+ query: "UPDATE users SET age = :age WHERE name = :name",
115
+ });
116
+ await updateAge({ age: 31, name: "alice" });
117
+ const rows = await db.read(users, { where: { name: "alice" } });
118
+ assert(rows[0].age).equals(31);
119
+ });
120
+ });
121
+ test.case("delete with no output", async (assert) => {
122
+ await $(async () => {
123
+ await db.create(users, { id: 1, name: "alice", age: 30 });
124
+ const deleteByName = db.sql({
125
+ input: p({ name: p.string }),
126
+ query: "DELETE FROM users WHERE name = :name",
127
+ });
128
+ await deleteByName({ name: "alice" });
129
+ const rows = await db.read(users, { where: {} });
130
+ assert(rows.length).equals(0);
131
+ });
132
+ });
133
+ test.case("input validation fails", async (assert) => {
134
+ await $(async () => {
135
+ const findByAge = db.sql({
136
+ input: p({ age: p.u8 }),
137
+ query: "SELECT name FROM users WHERE age > :age",
138
+ output: p.array(p({ name: p.string })),
139
+ });
140
+ try {
141
+ await findByAge({ age: -1 });
142
+ assert(false).true();
143
+ }
144
+ catch {
145
+ assert(true).true();
146
+ }
147
+ });
148
+ });
149
+ test.case("empty result", async (assert) => {
150
+ await $(async () => {
151
+ const findByAge = db.sql({
152
+ input: p({ age: p.u8 }),
153
+ query: "SELECT name FROM users WHERE age > :age",
154
+ output: p.array(p({ name: p.string })),
155
+ });
156
+ const results = await findByAge({ age: 100 });
157
+ assert(results.length).equals(0);
158
+ });
159
+ });
160
+ test.case("placeholder in input but not in query", async (assert) => {
161
+ await $(async () => {
162
+ assert(() => db.sql({
163
+ // @ts-expect-error 'name' in put but missing in query
164
+ input: p({ age: p.u8, name: p.string }),
165
+ query: "SELECT name FROM users WHERE age > :age",
166
+ })).throws(Code.sql_placeholder_missing);
167
+ });
168
+ });
169
+ test.case("placeholder in query but not in input", async (assert) => {
170
+ await $(async () => {
171
+ assert(() => db.sql({
172
+ // @ts-expect-error 'name' required but missing in input
173
+ input: p({ age: p.u8 }),
174
+ query: "SELECT name FROM users WHERE age > :age AND name = :name",
175
+ })).throws(Code.sql_input_missing);
176
+ });
177
+ });
178
+ test.case("output validation fails", async (assert) => {
179
+ await $(async () => {
180
+ await db.create(users, { id: 1, name: "alice", age: 30 });
181
+ const findAll = db.sql({
182
+ query: "SELECT name FROM users",
183
+ output: p.array(p({ name: p.u8 })),
184
+ });
185
+ try {
186
+ await findAll();
187
+ assert(false).true();
188
+ }
189
+ catch {
190
+ assert(true).true();
191
+ }
192
+ });
193
+ });
194
+ });
195
+ }
196
+ //# sourceMappingURL=testSQL.js.map
@@ -1,23 +1,29 @@
1
1
  import type { FileRef } from "@rcompat/fs";
2
+ import type http from "@rcompat/http";
3
+ import type { Method } from "@rcompat/http";
2
4
  import type ParseError from "pema/ParseError";
5
+ type MIME = typeof http.MIME;
6
+ type MIMEValue = MIME[keyof MIME];
3
7
  declare function app_reserved_directory(directory: string): import("@rcompat/error").TemplateError;
4
8
  declare function app_duplicate_module(name: string): import("@rcompat/error").TemplateError;
5
9
  declare function build_missing_binary_addon(): import("@rcompat/error").TemplateError;
6
10
  declare function build_missing_route(route: string, file: FileRef): import("@rcompat/error").TemplateError;
7
- declare function build_multiple_db_drivers(drivers: string[]): import("@rcompat/error").TemplateError;
8
11
  declare function build_live_reload_failed(filename: string, cause: Error): import("@rcompat/error").TemplateError;
9
12
  declare function build_previous_build_exists(file: FileRef): import("@rcompat/error").TemplateError;
10
- declare function config_tsconfig_has_paths(): import("@rcompat/error").TemplateError;
13
+ declare function build_no_path_schema(route: string): import("@rcompat/error").TemplateError;
14
+ declare function build_body_requires_content_type(): import("@rcompat/error").TemplateError;
11
15
  declare function config_file_missing(): import("@rcompat/error").TemplateError;
12
16
  declare function config_file_empty(file: FileRef): import("@rcompat/error").TemplateError;
13
17
  declare function config_file_error(file: FileRef, cause: Error): import("@rcompat/error").TemplateError;
14
18
  declare function config_missing(property: string): import("@rcompat/error").TemplateError;
19
+ declare function config_failed_to_parse_tsconfig(path: FileRef, cause: Error): import("@rcompat/error").TemplateError;
15
20
  declare function frontend_missing(view: string, module?: string): import("@rcompat/error").TemplateError;
16
21
  declare function frontend_missing_app_js(): import("@rcompat/error").TemplateError;
17
22
  declare function request_unsupported_mime(path: string, mime: string): import("@rcompat/error").TemplateError;
18
23
  declare function request_unparsable_mime(path: string, mime: string, cause: Error): import("@rcompat/error").TemplateError;
19
- declare function request_unexpected_body(expected: string, actual: string): import("@rcompat/error").TemplateError;
24
+ declare function request_body_already_parsed(): import("@rcompat/error").TemplateError;
20
25
  declare function request_bag_missing_key(bag: string, key: string): import("@rcompat/error").TemplateError;
26
+ declare function request_content_type_mismatch(expected: MIMEValue, actual: string): import("@rcompat/error").TemplateError;
21
27
  declare function response_invalid_body(body: string): import("@rcompat/error").TemplateError;
22
28
  declare function hook_route_functions_not_allowed(file: string): import("@rcompat/error").TemplateError;
23
29
  declare function hook_not_allowed(file: string): import("@rcompat/error").TemplateError;
@@ -28,9 +34,10 @@ declare function hook_bad_return(): import("@rcompat/error").TemplateError;
28
34
  declare function session_missing_id(): import("@rcompat/error").TemplateError;
29
35
  declare function session_id_string(): import("@rcompat/error").TemplateError;
30
36
  declare function session_id_data(): import("@rcompat/error").TemplateError;
37
+ declare function session_handle_unavailable(): import("@rcompat/error").TemplateError;
31
38
  declare function target_missing(target: string, targets: string[]): import("@rcompat/error").TemplateError;
32
39
  declare function target_duplicate(target: string): import("@rcompat/error").TemplateError;
33
- declare function route_missing_verb(route: string, verb: string): import("@rcompat/error").TemplateError;
40
+ declare function route_missing_method(route: string, method: Method): import("@rcompat/error").TemplateError;
34
41
  declare function route_invalid_special_file(route: string): import("@rcompat/error").TemplateError;
35
42
  declare function route_invalid_parameter(route: string, parameter: string): import("@rcompat/error").TemplateError;
36
43
  declare function route_invalid_characters(route: string, regexp: RegExp): import("@rcompat/error").TemplateError;
@@ -40,7 +47,9 @@ declare function view_duplicate_extension(extension: string): import("@rcompat/e
40
47
  declare function view_error(view: string, cause: Error): import("@rcompat/error").TemplateError;
41
48
  declare function env_invalid_schema(cause: ParseError): import("@rcompat/error").TemplateError;
42
49
  declare function env_missing_key(key: string): import("@rcompat/error").TemplateError;
50
+ declare function openapi_no_operation_for(method: Method, path: string): import("@rcompat/error").TemplateError;
43
51
  declare const errors: {
52
+ openapi_no_operation_for: typeof openapi_no_operation_for;
44
53
  env_invalid_schema: typeof env_invalid_schema;
45
54
  env_missing_key: typeof env_missing_key;
46
55
  view_missing: typeof view_missing;
@@ -52,6 +61,7 @@ declare const errors: {
52
61
  session_missing_id: typeof session_missing_id;
53
62
  session_id_string: typeof session_id_string;
54
63
  session_id_data: typeof session_id_data;
64
+ session_handle_unavailable: typeof session_handle_unavailable;
55
65
  hook_route_functions_not_allowed: typeof hook_route_functions_not_allowed;
56
66
  hook_not_allowed: typeof hook_not_allowed;
57
67
  hook_unused: typeof hook_unused;
@@ -61,9 +71,10 @@ declare const errors: {
61
71
  response_invalid_body: typeof response_invalid_body;
62
72
  request_unsupported_mime: typeof request_unsupported_mime;
63
73
  request_unparsable_mime: typeof request_unparsable_mime;
64
- request_unexpected_body: typeof request_unexpected_body;
74
+ request_body_already_parsed: typeof request_body_already_parsed;
65
75
  request_bag_missing_key: typeof request_bag_missing_key;
66
- route_missing_verb: typeof route_missing_verb;
76
+ request_content_type_mismatch: typeof request_content_type_mismatch;
77
+ route_missing_method: typeof route_missing_method;
67
78
  route_invalid_special_file: typeof route_invalid_special_file;
68
79
  route_invalid_parameter: typeof route_invalid_parameter;
69
80
  route_invalid_characters: typeof route_invalid_characters;
@@ -71,18 +82,64 @@ declare const errors: {
71
82
  frontend_missing_app_js: typeof frontend_missing_app_js;
72
83
  config_file_missing: typeof config_file_missing;
73
84
  config_missing: typeof config_missing;
74
- config_tsconfig_has_paths: typeof config_tsconfig_has_paths;
75
85
  config_file_empty: typeof config_file_empty;
76
86
  config_file_error: typeof config_file_error;
87
+ config_failed_to_parse_tsconfig: typeof config_failed_to_parse_tsconfig;
77
88
  build_missing_binary_addon: typeof build_missing_binary_addon;
78
89
  build_missing_route: typeof build_missing_route;
79
- build_multiple_db_drivers: typeof build_multiple_db_drivers;
80
90
  build_live_reload_failed: typeof build_live_reload_failed;
81
91
  build_previous_build_exists: typeof build_previous_build_exists;
92
+ build_no_path_schema: typeof build_no_path_schema;
93
+ build_body_requires_content_type: typeof build_body_requires_content_type;
82
94
  app_reserved_directory: typeof app_reserved_directory;
83
95
  app_duplicate_module: typeof app_duplicate_module;
84
96
  };
97
+ export declare const Code: {
98
+ openapi_no_operation_for: "openapi_no_operation_for";
99
+ env_invalid_schema: "env_invalid_schema";
100
+ env_missing_key: "env_missing_key";
101
+ view_missing: "view_missing";
102
+ view_missing_default_export: "view_missing_default_export";
103
+ view_duplicate_extension: "view_duplicate_extension";
104
+ view_error: "view_error";
105
+ target_missing: "target_missing";
106
+ target_duplicate: "target_duplicate";
107
+ session_missing_id: "session_missing_id";
108
+ session_id_string: "session_id_string";
109
+ session_id_data: "session_id_data";
110
+ session_handle_unavailable: "session_handle_unavailable";
111
+ hook_route_functions_not_allowed: "hook_route_functions_not_allowed";
112
+ hook_not_allowed: "hook_not_allowed";
113
+ hook_unused: "hook_unused";
114
+ hook_reused_next: "hook_reused_next";
115
+ hook_no_return: "hook_no_return";
116
+ hook_bad_return: "hook_bad_return";
117
+ response_invalid_body: "response_invalid_body";
118
+ request_unsupported_mime: "request_unsupported_mime";
119
+ request_unparsable_mime: "request_unparsable_mime";
120
+ request_body_already_parsed: "request_body_already_parsed";
121
+ request_bag_missing_key: "request_bag_missing_key";
122
+ request_content_type_mismatch: "request_content_type_mismatch";
123
+ route_missing_method: "route_missing_method";
124
+ route_invalid_special_file: "route_invalid_special_file";
125
+ route_invalid_parameter: "route_invalid_parameter";
126
+ route_invalid_characters: "route_invalid_characters";
127
+ frontend_missing: "frontend_missing";
128
+ frontend_missing_app_js: "frontend_missing_app_js";
129
+ config_file_missing: "config_file_missing";
130
+ config_missing: "config_missing";
131
+ config_file_empty: "config_file_empty";
132
+ config_file_error: "config_file_error";
133
+ config_failed_to_parse_tsconfig: "config_failed_to_parse_tsconfig";
134
+ build_missing_binary_addon: "build_missing_binary_addon";
135
+ build_missing_route: "build_missing_route";
136
+ build_live_reload_failed: "build_live_reload_failed";
137
+ build_previous_build_exists: "build_previous_build_exists";
138
+ build_no_path_schema: "build_no_path_schema";
139
+ build_body_requires_content_type: "build_body_requires_content_type";
140
+ app_reserved_directory: "app_reserved_directory";
141
+ app_duplicate_module: "app_duplicate_module";
142
+ };
85
143
  export type Code = keyof typeof errors;
86
- export declare const Code: { [K in Code]: K; };
87
144
  export default errors;
88
145
  //# sourceMappingURL=errors.d.ts.map
@@ -16,26 +16,29 @@ function build_missing_binary_addon() {
16
16
  function build_missing_route(route, file) {
17
17
  return t `cannot find route source for ${route} under ${file.path}`;
18
18
  }
19
- function build_multiple_db_drivers(drivers) {
20
- const defaults = ["index.ts", "index.js", "default.ts", "default.js"];
21
- return t `multiple database drivers ${drivers}, add one of ${defaults}`;
22
- }
23
19
  function build_live_reload_failed(filename, cause) {
24
20
  return t `failed to live-reload ${filename}: ${cause}`;
25
21
  }
26
22
  function build_previous_build_exists(file) {
27
23
  return t `${file.path} exists but does not contain a previous build`;
28
24
  }
25
+ function build_no_path_schema(route) {
26
+ const example = "route.with({ path: ... })";
27
+ return t `route ${route} has dynamic segments but no path schema — declare a path schema in ${example} to use this route from a client`;
28
+ }
29
+ function build_body_requires_content_type() {
30
+ const b = "contentType";
31
+ const c = "route.with({ contentType: ..., body: ... })";
32
+ return t `body schema requires ${b} to be set — declare a contentType in ${c}`;
33
+ }
29
34
  const BUILD = error.coded({
30
35
  build_missing_binary_addon,
31
36
  build_missing_route,
32
- build_multiple_db_drivers,
33
37
  build_live_reload_failed,
34
38
  build_previous_build_exists,
39
+ build_no_path_schema,
40
+ build_body_requires_content_type,
35
41
  });
36
- function config_tsconfig_has_paths() {
37
- return t `tsconfig.json exists with paths, remove config paths`;
38
- }
39
42
  function config_file_missing() {
40
43
  return t `missing ${"config/app.ts"}`;
41
44
  }
@@ -48,12 +51,15 @@ function config_file_error(file, cause) {
48
51
  function config_missing(property) {
49
52
  return t `${property} not configured`;
50
53
  }
54
+ function config_failed_to_parse_tsconfig(path, cause) {
55
+ return t `failed to parse tsconfig at ${path}: ${cause}`;
56
+ }
51
57
  const CONFIG = error.coded({
52
58
  config_file_missing,
53
59
  config_missing,
54
- config_tsconfig_has_paths,
55
60
  config_file_empty,
56
61
  config_file_error,
62
+ config_failed_to_parse_tsconfig,
57
63
  });
58
64
  function frontend_missing(view, module) {
59
65
  if (module === undefined)
@@ -74,17 +80,21 @@ function request_unsupported_mime(path, mime) {
74
80
  function request_unparsable_mime(path, mime, cause) {
75
81
  return t `${path}: unparsable MIME type ${mime} (${cause})`;
76
82
  }
77
- function request_unexpected_body(expected, actual) {
78
- return t `request body: expected ${expected}, got ${actual}`;
83
+ function request_body_already_parsed() {
84
+ return t `body is already parsed`;
79
85
  }
80
86
  function request_bag_missing_key(bag, key) {
81
87
  return t `${bag} has no key ${key}`;
82
88
  }
89
+ function request_content_type_mismatch(expected, actual) {
90
+ return t `content-type mismatch: expected ${expected}, got ${actual}`;
91
+ }
83
92
  const REQUEST = error.coded({
84
93
  request_unsupported_mime,
85
94
  request_unparsable_mime,
86
- request_unexpected_body,
95
+ request_body_already_parsed,
87
96
  request_bag_missing_key,
97
+ request_content_type_mismatch,
88
98
  });
89
99
  function response_invalid_body(body) {
90
100
  return t `invalid body ${body} returned from route`;
@@ -128,10 +138,14 @@ function session_id_string() {
128
138
  function session_id_data() {
129
139
  return t `"both ${"id"} and ${"data"} must be defined or undefined`;
130
140
  }
141
+ function session_handle_unavailable() {
142
+ return t `session handle not available in this context`;
143
+ }
131
144
  const SESSION = error.coded({
132
145
  session_missing_id,
133
146
  session_id_string,
134
147
  session_id_data,
148
+ session_handle_unavailable,
135
149
  });
136
150
  function target_missing(target, targets) {
137
151
  return t `no target ${target}, available targets ${targets}`;
@@ -143,8 +157,8 @@ const TARGET = error.coded({
143
157
  target_missing,
144
158
  target_duplicate,
145
159
  });
146
- function route_missing_verb(route, verb) {
147
- return t `${route} has no verb ${verb}`;
160
+ function route_missing_method(route, method) {
161
+ return t `${route} has no method ${method}`;
148
162
  }
149
163
  function route_invalid_special_file(route) {
150
164
  return t `${route} is not a valid special file`;
@@ -156,7 +170,7 @@ function route_invalid_characters(route, regexp) {
156
170
  return t `${route} may only contain any of ${regexp.source.slice(1, -1)}`;
157
171
  }
158
172
  const ROUTE = error.coded({
159
- route_missing_verb,
173
+ route_missing_method,
160
174
  route_invalid_special_file,
161
175
  route_invalid_parameter,
162
176
  route_invalid_characters,
@@ -192,6 +206,12 @@ const ENV = error.coded({
192
206
  env_invalid_schema,
193
207
  env_missing_key,
194
208
  });
209
+ function openapi_no_operation_for(method, path) {
210
+ return t `no operation for ${path} (${method}`;
211
+ }
212
+ const OPENAPI = error.coded({
213
+ openapi_no_operation_for,
214
+ });
195
215
  const errors = {
196
216
  ...APP,
197
217
  ...BUILD,
@@ -205,7 +225,8 @@ const errors = {
205
225
  ...TARGET,
206
226
  ...VIEW,
207
227
  ...ENV,
228
+ ...OPENAPI,
208
229
  };
209
- export const Code = Object.fromEntries(Object.keys(errors).map(k => [k, k]));
230
+ export const Code = error.names(errors);
210
231
  export default errors;
211
232
  //# sourceMappingURL=errors.js.map
@@ -12,11 +12,11 @@ import type { ObjectType } from "pema";
12
12
  export type { Module };
13
13
  declare const base_schema: ObjectType<import("pema").NormalizeSchemaObject<{
14
14
  readonly extensions: import("pema").OptionalType<import("pema").ArrayType<import("pema").StringType>>;
15
- readonly spa: import("pema").DefaultType<import("pema").BooleanType, true>;
15
+ readonly csr: import("pema").DefaultType<import("pema").BooleanType, true>;
16
16
  readonly ssr: import("pema").DefaultType<import("pema").BooleanType, true>;
17
17
  }>, {
18
18
  extensions: string[] | undefined;
19
- spa: boolean;
19
+ csr: boolean;
20
20
  ssr: boolean;
21
21
  }>;
22
22
  type C = typeof base_schema.Complement;
@@ -62,11 +62,11 @@ export interface Init<S = ServerView, E extends C = ObjectType<never>> {
62
62
  }
63
63
  export default function frontend_module<S = ServerView, E extends C = ObjectType<never>>(init: Init<S, E>): (input?: E extends ObjectType<never, never> ? {
64
64
  extensions?: string[] | undefined;
65
- spa?: boolean | undefined;
65
+ csr?: boolean | undefined;
66
66
  ssr?: boolean | undefined;
67
67
  } | undefined : Unpack<({
68
68
  extensions?: string[] | undefined;
69
- spa?: boolean | undefined;
69
+ csr?: boolean | undefined;
70
70
  ssr?: boolean | undefined;
71
71
  } | undefined) & E["input"]>) => Module;
72
72
  //# sourceMappingURL=frontend.d.ts.map
@@ -4,11 +4,11 @@ import location from "#location";
4
4
  import hash from "@rcompat/crypto/hash";
5
5
  import fn from "@rcompat/fn";
6
6
  import fs from "@rcompat/fs";
7
- import { MIME, Status } from "@rcompat/http";
7
+ import http from "@rcompat/http";
8
8
  import p from "pema";
9
9
  const base_schema = p({
10
10
  extensions: p.array(p.string).optional(),
11
- spa: p.boolean.default(true),
11
+ csr: p.boolean.default(true),
12
12
  ssr: p.boolean.default(true),
13
13
  });
14
14
  export default function frontend_module(init) {
@@ -24,7 +24,7 @@ export default function frontend_module(init) {
24
24
  }
25
25
  return (input) => {
26
26
  const options = schema.parse(input);
27
- const spa = options.spa;
27
+ const csr = options.csr;
28
28
  const extensions = options.extensions ?? init.extensions;
29
29
  let mode = "development";
30
30
  function ssr() {
@@ -50,12 +50,15 @@ export default function frontend_module(init) {
50
50
  }
51
51
  return { body, head, headers };
52
52
  }
53
+ const interactive = !ssr() || csr;
54
+ if (!interactive)
55
+ return { body, head, headers };
53
56
  const app_asset = app.assets.find(asset => asset.src?.includes("app") && asset.src.endsWith(".js"));
54
57
  if (app_asset === undefined)
55
58
  throw E.frontend_missing_app_js();
56
59
  const app_script = `<script type="module" src="${app_asset.src}"></script>`;
57
60
  const props = JSON.stringify({ frontend: init.name, ...client });
58
- const hydrated = await inline(props, MIME.APPLICATION_JSON, "hydration");
61
+ const hydrated = await inline(props, http.MIME.APPLICATION_JSON, "hydration");
59
62
  const script_src = [hydrated.integrity];
60
63
  return {
61
64
  body,
@@ -81,21 +84,21 @@ export default function frontend_module(init) {
81
84
  : { props, request: $request };
82
85
  const client = {
83
86
  view: init.layouts ? "root" : await normalize(view),
84
- spa: spa,
87
+ csr,
85
88
  ssr: ssr(),
86
89
  mode: app.mode,
87
90
  ...$props,
88
91
  };
89
- if (spa && request.headers.try("Accept") === MIME.APPLICATION_JSON) {
92
+ if (csr && request.headers.try("Accept") === http.MIME.APPLICATION_JSON) {
90
93
  const json_body = JSON.stringify(client);
91
94
  return new Response(json_body, {
92
95
  headers: {
93
96
  ...app.headers(),
94
- "Content-Type": MIME.APPLICATION_JSON,
97
+ "Content-Type": http.MIME.APPLICATION_JSON,
95
98
  "Content-Length": String(app.body_length(json_body)),
96
99
  "Cache-Control": "no-store",
97
100
  },
98
- status: options.status ?? Status.OK,
101
+ status: options.status ?? http.Status.OK,
99
102
  });
100
103
  }
101
104
  if (!ssr()) {
@@ -10,7 +10,13 @@ declare const errors: {
10
10
  unit_not_supported: typeof unit_not_supported;
11
11
  no_dots_catalog_keys: typeof no_dots_catalog_keys;
12
12
  };
13
+ export declare const Code: {
14
+ at_least_one_locale: "at_least_one_locale";
15
+ missing_default_locale: "missing_default_locale";
16
+ unused_default_locale: "unused_default_locale";
17
+ unit_not_supported: "unit_not_supported";
18
+ no_dots_catalog_keys: "no_dots_catalog_keys";
19
+ };
13
20
  export type Code = keyof typeof errors;
14
- export declare const Code: { [K in Code]: K; };
15
21
  export default errors;
16
22
  //# sourceMappingURL=errors.d.ts.map
@@ -22,6 +22,6 @@ const errors = error.coded({
22
22
  unit_not_supported,
23
23
  no_dots_catalog_keys,
24
24
  });
25
- export const Code = Object.fromEntries(Object.keys(errors).map(k => [k, k]));
25
+ export const Code = error.names(errors);
26
26
  export default errors;
27
27
  //# sourceMappingURL=errors.js.map
@@ -7,7 +7,7 @@ type EntryOf<Body extends string> = Body extends `${infer Name}:${infer Spec}` ?
7
7
  __name: Body & string;
8
8
  __type: string;
9
9
  };
10
- export type EntriesOf<S extends string> = string extends S ? never : S extends `${infer _}{${infer Body}}${infer Rest}` ? EntryOf<Body> | EntriesOf<Rest> : never;
10
+ export type EntriesOf<S extends string> = string extends S ? never : S extends `${infer _}{{${infer _}}}${infer Rest}` ? EntriesOf<Rest> : S extends `${infer _}{${infer Body}}${infer Rest}` ? EntryOf<Body> | EntriesOf<Rest> : never;
11
11
  export type ParamsFromEntries<E> = [
12
12
  E
13
13
  ] extends [never] ? Dict : {
@@ -6,6 +6,6 @@ type EnforceNoDots<T, D extends number = 6> = [
6
6
  ] extends [never] ? T : T extends string ? T : T extends readonly (infer E)[] ? readonly EnforceNoDots<E, Prev[D]>[] : T extends object ? {
7
7
  [K in keyof T]: K extends string ? K extends `${string}.${string}` ? DotKeyMessage<K> : EnforceNoDots<T[K], Prev[D]> : EnforceNoDots<T[K], Prev[D]>;
8
8
  } : T;
9
- export default function locale<const M extends Catalog>(messages: EnforceNoDots<M>): M;
9
+ export default function locale<const M>(messages: EnforceNoDots<M> & Catalog): M;
10
10
  export {};
11
11
  //# sourceMappingURL=locale.d.ts.map